Browse Source

add upgrade-fixed-package

Yuki Takei 2 weeks ago
parent
commit
2a35785d4d

+ 262 - 0
.kiro/specs/upgrade-fixed-packages/design.md

@@ -0,0 +1,262 @@
+# Design Document: upgrade-fixed-packages
+
+## Overview
+
+**Purpose**: This feature audits and upgrades version-pinned packages in `apps/app/package.json` that were frozen due to upstream bugs, ESM-only migrations, or licensing constraints. The build environment has shifted from webpack to Turbopack, and the runtime now targets Node.js 24 with stable `require(esm)` support, invalidating several original pinning reasons.
+
+**Users**: Maintainers and developers benefit from up-to-date dependencies with bug fixes, security patches, and reduced technical debt.
+
+**Impact**: Modifies `apps/app/package.json` dependency versions and comment blocks; touches source files where `escape-string-regexp` is replaced by native `RegExp.escape()`.
+
+### Goals
+- Verify each pinning reason against current upstream status
+- Upgrade packages where the original constraint no longer applies
+- Replace `escape-string-regexp` with native `RegExp.escape()` (Node.js 24)
+- Update or remove comment blocks to reflect current state
+- Produce audit documentation for future reference
+
+### Non-Goals
+- Replacing handsontable with an alternative library (license constraint remains; replacement is a separate initiative)
+- Upgrading `@keycloak/keycloak-admin-client` to v19+ (significant API breaking changes; deferred to separate task)
+- Major version upgrades of unrelated packages
+- Modifying the build pipeline or Turbopack configuration
+
+## Architecture
+
+This is a dependency maintenance task, not a feature implementation. No new components or architectural changes are introduced.
+
+### Existing Architecture Analysis
+
+The pinned packages fall into distinct categories by their usage context:
+
+| Category | Packages | Build Context |
+|----------|----------|---------------|
+| Server-only (tsc → CJS) | `escape-string-regexp`, `@aws-sdk/*`, `@keycloak/*` | Express server compiled by tsc |
+| Client-only (Turbopack) | `string-width` (via @growi/editor), `bootstrap` | Bundled by Turbopack/Vite |
+| Client + SSR | `next-themes` | Turbopack + SSR rendering |
+| License-pinned | `handsontable`, `@handsontable/react` | Client-only |
+
+Key enabler: Node.js ^24 provides stable `require(esm)` support, removing the fundamental CJS/ESM incompatibility that caused several pins.
+
+### Technology Stack
+
+| Layer | Choice / Version | Role in Feature | Notes |
+|-------|------------------|-----------------|-------|
+| Runtime | Node.js ^24 | Enables `require(esm)` and `RegExp.escape()` | ES2026 Stage 4 features available |
+| Build (client) | Turbopack (Next.js 16) | Bundles ESM-only packages without issues | No changes needed |
+| Build (server) | tsc (CommonJS output) | `require(esm)` handles ESM-only imports | Node.js 24 native support |
+| Package manager | pnpm v10 | Manages dependency resolution | No changes needed |
+
+## System Flows
+
+### Upgrade Verification Flow
+
+```mermaid
+flowchart TD
+    Start[Select package to upgrade] --> Update[Update version in package.json]
+    Update --> Install[pnpm install]
+    Install --> Build{turbo run build}
+    Build -->|Pass| Lint{turbo run lint}
+    Build -->|Fail| Revert[Revert package change]
+    Lint -->|Pass| Test{turbo run test}
+    Lint -->|Fail| Revert
+    Test -->|Pass| Verify[Verify .next/node_modules symlinks]
+    Test -->|Fail| Revert
+    Verify --> Next[Proceed to next package]
+    Revert --> Document[Document failure reason]
+    Document --> Next
+```
+
+Each package is upgraded and verified independently. Failures are isolated and reverted without affecting other upgrades.
+
+## Requirements Traceability
+
+| Requirement | Summary | Components | Action |
+|-------------|---------|------------|--------|
+| 1.1 | Bootstrap bug investigation | PackageAudit | Verify #39798 fixed in v5.3.4 |
+| 1.2 | next-themes issue investigation | PackageAudit | Verify #122 resolved; check v0.4.x compatibility |
+| 1.3 | @aws-sdk constraint verification | PackageAudit | Confirm mongodb constraint is on different package |
+| 1.4 | Document investigation results | AuditReport | Summary table in research.md |
+| 2.1 | ESM compatibility per package | PackageAudit | Assess escape-string-regexp, string-width, @keycloak |
+| 2.2 | Server build ESM support | PackageAudit | Verify Node.js 24 require(esm) for server context |
+| 2.3 | Client build ESM support | PackageAudit | Confirm Turbopack handles ESM-only packages |
+| 2.4 | Compatibility matrix | AuditReport | Table in research.md |
+| 3.1 | Handsontable license check | PackageAudit | Confirm v7+ still non-MIT |
+| 3.2 | Document pinning requirement | AuditReport | Note in audit summary |
+| 4.1 | Update package.json versions and comments | UpgradeExecution | Modify versions and comment blocks |
+| 4.2 | Build verification | UpgradeExecution | `turbo run build --filter @growi/app` |
+| 4.3 | Lint verification | UpgradeExecution | `turbo run lint --filter @growi/app` |
+| 4.4 | Test verification | UpgradeExecution | `turbo run test --filter @growi/app` |
+| 4.5 | Revert on failure | UpgradeExecution | Git revert per package |
+| 4.6 | Update comment blocks | UpgradeExecution | Remove or update comments |
+| 5.1 | Audit summary table | AuditReport | Final summary with decisions |
+| 5.2 | Document continued pinning | AuditReport | Reasons for remaining pins |
+| 5.3 | Document upgrade rationale | AuditReport | What changed upstream |
+
+## Components and Interfaces
+
+| Component | Domain | Intent | Req Coverage | Key Dependencies |
+|-----------|--------|--------|--------------|------------------|
+| PackageAudit | Investigation | Research upstream status for each pinned package | 1.1–1.4, 2.1–2.4, 3.1–3.2 | GitHub issues, npm registry |
+| UpgradeExecution | Implementation | Apply version changes and verify build | 4.1–4.6 | pnpm, turbo, tsc |
+| SourceMigration | Implementation | Replace escape-string-regexp with RegExp.escape() | 4.1 | 9 source files |
+| AuditReport | Documentation | Produce summary of all decisions | 5.1–5.3 | research.md |
+
+### Investigation Layer
+
+#### PackageAudit
+
+| Field | Detail |
+|-------|--------|
+| Intent | Investigate upstream status of each pinned package and determine upgrade feasibility |
+| Requirements | 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2 |
+
+**Responsibilities & Constraints**
+- Check upstream issue trackers for bug fix status
+- Verify ESM compatibility against Node.js 24 `require(esm)` and Turbopack
+- Confirm license status for handsontable
+- Produce actionable recommendation per package
+
+**Audit Decision Matrix**
+
+| Package | Current | Action | Target | Risk | Rationale |
+|---------|---------|--------|--------|------|-----------|
+| `bootstrap` | `=5.3.2` | Upgrade | `^5.3.4` | Low | Bug #39798 fixed in v5.3.4 |
+| `next-themes` | `^0.2.1` | Upgrade | `^0.4.4` | Medium | Original issue was misattributed; v0.4.x works with Pages Router |
+| `escape-string-regexp` | `^4.0.0` | Replace | Remove dep | Low | Native `RegExp.escape()` in Node.js 24 |
+| `string-width` | `=4.2.2` | Upgrade | `^7.0.0` | Low | Used only in ESM context (@growi/editor) |
+| `@aws-sdk/client-s3` | `3.454.0` | Relax | `^3.454.0` | Low | Pinning comment was misleading |
+| `@aws-sdk/s3-request-presigner` | `3.454.0` | Relax | `^3.454.0` | Low | Same as above |
+| `@keycloak/keycloak-admin-client` | `^18.0.0` | Defer | No change | N/A | API breaking changes; separate task |
+| `handsontable` | `=6.2.2` | Keep | No change | N/A | License constraint (non-MIT since v7) |
+| `@handsontable/react` | `=2.1.0` | Keep | No change | N/A | Requires handsontable >= 7 |
+
+### Implementation Layer
+
+#### UpgradeExecution
+
+| Field | Detail |
+|-------|--------|
+| Intent | Apply version changes incrementally with build verification |
+| Requirements | 4.1, 4.2, 4.3, 4.4, 4.5, 4.6 |
+
+**Responsibilities & Constraints**
+- Upgrade one package at a time to isolate failures
+- Run full verification suite (build, lint, test) after each change
+- Revert and document any package that causes failures
+- Update `// comments for dependencies` block to reflect new state
+
+**Upgrade Order** (lowest risk first):
+1. `@aws-sdk/*` — relax version range (no code changes)
+2. `string-width` — upgrade in @growi/editor (isolated ESM package)
+3. `bootstrap` — upgrade to ^5.3.4 (verify SCSS compilation)
+4. `escape-string-regexp` → `RegExp.escape()` — source code changes across 9 files
+5. `next-themes` — upgrade to ^0.4.x (review API changes across 12 files)
+
+**Implementation Notes**
+- After each upgrade, verify `.next/node_modules/` symlinks for Turbopack externalisation compliance (per `package-dependencies` rule)
+- For bootstrap: run `pnpm run pre:styles-commons` and `pnpm run pre:styles-components` to verify SCSS compilation
+- For next-themes: review v0.3.0 and v0.4.0 changelogs for breaking API changes before modifying code
+
+#### SourceMigration
+
+| Field | Detail |
+|-------|--------|
+| Intent | Replace all `escape-string-regexp` usage with native `RegExp.escape()` |
+| Requirements | 4.1 |
+
+**Files to Modify**:
+
+`apps/app/src/` (6 files):
+- `server/models/page.ts`
+- `server/service/page/index.ts`
+- `server/service/page-grant.ts`
+- `server/routes/apiv3/users.js`
+- `server/models/obsolete-page.js`
+- `features/openai/server/services/openai.ts`
+
+`packages/` (3 files):
+- `packages/core/src/utils/page-path-utils/` (2 files)
+- `packages/remark-lsx/src/server/routes/list-pages/index.ts`
+
+**Migration Pattern**:
+```typescript
+// Before
+import escapeStringRegexp from 'escape-string-regexp';
+const pattern = new RegExp(escapeStringRegexp(input));
+
+// After
+const pattern = new RegExp(RegExp.escape(input));
+```
+
+**Implementation Notes**
+- Remove `escape-string-regexp` from `apps/app/package.json` dependencies after migration
+- Remove from `packages/core/package.json` and `packages/remark-lsx/package.json` if listed
+- Verify `RegExp.escape()` TypeScript types are available (may need `@types/node` update or lib config)
+
+### Documentation Layer
+
+#### AuditReport
+
+| Field | Detail |
+|-------|--------|
+| Intent | Document all audit decisions for future maintainers |
+| Requirements | 5.1, 5.2, 5.3 |
+
+**Deliverables**:
+- Updated `// comments for dependencies` in package.json (only retained pins with current reasons)
+- Updated `// comments for defDependencies` (handsontable entries unchanged)
+- Summary in research.md with final decision per package
+
+**Updated Comment Blocks** (target state):
+
+```json
+{
+  "// comments for dependencies": {
+    "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM. API breaking changes require separate migration effort.",
+    "next-themes": "(if upgrade fails) Document specific failure reason here"
+  },
+  "// comments for defDependencies": {
+    "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
+    "handsontable": "v7.0.0 or above is no longer MIT license."
+  }
+}
+```
+
+Note: The exact final state depends on which upgrades succeed. If all planned upgrades pass, only `@keycloak` and `handsontable` entries remain.
+
+## Testing Strategy
+
+### Build Verification (per package)
+- `turbo run build --filter @growi/app` — Turbopack client build + tsc server build
+- `ls apps/app/.next/node_modules/ | grep <package>` — Externalisation check
+- `pnpm run pre:styles-commons` — SCSS compilation (bootstrap only)
+
+### Lint Verification (per package)
+- `turbo run lint --filter @growi/app` — TypeScript type check + Biome
+
+### Unit/Integration Tests (per package)
+- `turbo run test --filter @growi/app` — Full test suite
+- For `RegExp.escape()` migration: run tests for page model, page service, page-grant service specifically
+
+### Regression Verification (final)
+- Full build + lint + test after all upgrades applied together
+- Verify `.next/node_modules/` symlink integrity via `check-next-symlinks.sh` (if available locally)
+
+## Migration Strategy
+
+```mermaid
+flowchart LR
+    Phase1[Phase 1: Low Risk] --> Phase2[Phase 2: Medium Risk]
+    Phase1 --> P1a[aws-sdk relax range]
+    Phase1 --> P1b[string-width upgrade]
+    Phase2 --> P2a[bootstrap upgrade]
+    Phase2 --> P2b[escape-string-regexp replace]
+    Phase2 --> P2c[next-themes upgrade]
+```
+
+- **Phase 1** (low risk): @aws-sdk range relaxation, string-width upgrade — minimal code changes
+- **Phase 2** (medium risk): bootstrap, escape-string-regexp replacement, next-themes — requires code review and/or source changes
+- Each upgrade is independently revertible
+- Deferred: @keycloak (high risk, separate task)
+- No change: handsontable (license constraint)

+ 75 - 0
.kiro/specs/upgrade-fixed-packages/requirements.md

@@ -0,0 +1,75 @@
+# Requirements Document
+
+## Introduction
+
+The `apps/app/package.json` file contains several packages whose versions are intentionally pinned due to ESM-only upgrades, upstream bugs, or licensing concerns. These pinning reasons were documented in `// comments for dependencies` and `// comments for defDependencies` comment blocks. Since the build environment has significantly changed (webpack → Turbopack), and upstream issues may have been resolved, a systematic audit is needed to determine which packages can now be safely upgraded.
+
+### Pinned Packages Inventory
+
+| # | Package | Current Version | Pinning Reason |
+|---|---------|----------------|----------------|
+| 1 | `@aws-sdk/client-s3`, `@aws-sdk/s3-request-presigner` | `3.454.0` | Fix version above 3.186.0 required by mongodb@4.16.0 |
+| 2 | `@keycloak/keycloak-admin-client` | `^18.0.0` | 19.0.0+ exports only ESM |
+| 3 | `bootstrap` | `=5.3.2` | v5.3.3 has a bug (twbs/bootstrap#39798) |
+| 4 | `escape-string-regexp` | `^4.0.0` | 5.0.0+ exports only ESM |
+| 5 | `next-themes` | `^0.2.1` | 0.3.0 causes type error (pacocoursey/next-themes#122) |
+| 6 | `string-width` | `=4.2.2` | 5.0.0+ exports only ESM |
+| 7 | `@handsontable/react` | `=2.1.0` | v3 requires handsontable >= 7.0.0 |
+| 8 | `handsontable` | `=6.2.2` | v7.0.0+ is no longer MIT license |
+
+## Requirements
+
+### Requirement 1: Upstream Bug and Issue Investigation
+
+**Objective:** As a maintainer, I want to verify whether upstream bugs and issues that originally caused version pinning have been resolved, so that I can make informed upgrade decisions.
+
+#### Acceptance Criteria
+
+1. When investigating the bootstrap pinning, the audit process shall check the current status of https://github.com/twbs/bootstrap/issues/39798 and determine whether v5.3.3+ has fixed the reported bug.
+2. When investigating the next-themes pinning, the audit process shall check the current status of https://github.com/pacocoursey/next-themes/issues/122 and determine whether v0.3.0+ has resolved the type error.
+3. When investigating the @aws-sdk pinning, the audit process shall verify whether the mongodb version used in GROWI still requires the `>=3.186.0` constraint and whether the latest @aws-sdk versions are compatible.
+4. The audit process shall document the investigation result for each package, including: current upstream status, whether the original issue is resolved, and the recommended action (upgrade/keep/replace).
+
+### Requirement 2: ESM-Only Package Compatibility Assessment
+
+**Objective:** As a maintainer, I want to assess whether ESM-only versions of pinned packages are now compatible with the current Turbopack-based build environment, so that outdated CJS-only constraints can be removed.
+
+#### Acceptance Criteria
+
+1. When assessing ESM compatibility, the audit process shall evaluate each ESM-pinned package (`escape-string-regexp`, `string-width`, `@keycloak/keycloak-admin-client`) against the current build pipeline (Turbopack for client, tsc for server).
+2. When a package is used in server-side code (transpiled via tsc with `tsconfig.build.server.json`), the audit process shall verify whether the server build output format (CJS or ESM) supports importing ESM-only packages.
+3. When a package is used only in client-side code (bundled via Turbopack), the audit process shall confirm that Turbopack can resolve ESM-only packages without issues.
+4. The audit process shall produce a compatibility matrix showing each ESM-pinned package, its usage context (server/client/both), and whether upgrading to the ESM-only version is feasible.
+
+### Requirement 3: License Compliance Verification
+
+**Objective:** As a maintainer, I want to confirm that the handsontable/`@handsontable/react` licensing situation has not changed, so that I can determine whether these packages must remain pinned or can be replaced.
+
+#### Acceptance Criteria
+
+1. When evaluating handsontable, the audit process shall verify the current license of handsontable v7.0.0+ and confirm whether it remains non-MIT.
+2. If handsontable v7.0.0+ is still non-MIT, the audit process shall document that `handsontable` (`=6.2.2`) and `@handsontable/react` (`=2.1.0`) must remain pinned or an alternative library must be identified.
+3. If a MIT-licensed alternative to handsontable exists, the audit process shall note it as a potential replacement candidate (out of scope for this spec but documented for future work).
+
+### Requirement 4: Safe Upgrade Execution
+
+**Objective:** As a maintainer, I want to upgrade packages that are confirmed safe to update, so that the project benefits from bug fixes, security patches, and new features.
+
+#### Acceptance Criteria
+
+1. When upgrading a pinned package, the upgrade process shall update the version specifier in `apps/app/package.json` and remove or update the corresponding entry in the `// comments for dependencies` or `// comments for defDependencies` block.
+2. When a package is upgraded, the upgrade process shall verify that `turbo run build --filter @growi/app` completes successfully.
+3. When a package is upgraded, the upgrade process shall verify that `turbo run lint --filter @growi/app` completes without new errors.
+4. When a package is upgraded, the upgrade process shall verify that `turbo run test --filter @growi/app` passes without new failures.
+5. If a package upgrade causes build, lint, or test failures, the upgrade process shall revert that specific package change and document the failure reason.
+6. When all upgrades are complete, the `// comments for dependencies` and `// comments for defDependencies` blocks shall accurately reflect only the packages that remain pinned, with updated reasons if applicable.
+
+### Requirement 5: Audit Documentation
+
+**Objective:** As a maintainer, I want a clear record of the audit results, so that future maintainers understand which packages were evaluated and why decisions were made.
+
+#### Acceptance Criteria
+
+1. The audit process shall produce a summary table documenting each pinned package with: package name, previous version, new version (or "unchanged"), and rationale for the decision.
+2. When a package remains pinned, the documentation shall include the verified reason for continued pinning.
+3. When a package is upgraded, the documentation shall note what changed upstream that made the upgrade possible.

+ 160 - 0
.kiro/specs/upgrade-fixed-packages/research.md

@@ -0,0 +1,160 @@
+# Research & Design Decisions
+
+---
+**Purpose**: Capture discovery findings for the pinned package audit and upgrade initiative.
+**Usage**: Inform design.md decisions; provide evidence for future maintainers.
+---
+
+## Summary
+- **Feature**: `upgrade-fixed-packages`
+- **Discovery Scope**: Extension (auditing existing dependency constraints)
+- **Key Findings**:
+  - Bootstrap bug (#39798) fixed in v5.3.4 — safe to upgrade to latest 5.3.x
+  - next-themes original issue (#122) was resolved long ago; upgrade to v0.4.x feasible but has Next.js 16 `cacheComponents` caveat
+  - Node.js ^24 enables stable `require(esm)`, unlocking ESM-only package upgrades for server code
+  - `escape-string-regexp` can be replaced entirely by native `RegExp.escape()` (ES2026, Node.js 24)
+  - handsontable license situation unchanged — must remain pinned at 6.2.2
+  - @aws-sdk pinning comment is misleading; packages can be freely upgraded
+
+## Research Log
+
+### Bootstrap v5.3.3 Bug (#39798)
+- **Context**: bootstrap pinned at `=5.3.2` due to modal header regression in v5.3.3
+- **Sources Consulted**: https://github.com/twbs/bootstrap/issues/39798, https://github.com/twbs/bootstrap/pull/41336
+- **Findings**:
+  - Issue CLOSED on 2025-04-03
+  - Fixed in v5.3.4 via PR #41336 (Fix modal and offcanvas header collapse)
+  - Bug: `.modal-header` lost `justify-content: space-between`, causing content collapse
+  - Latest stable: v5.3.8 (August 2025)
+- **Implications**: Safe to upgrade from `=5.3.2` to `^5.3.4`. Skip v5.3.3 entirely. Recommend `^5.3.4` or pin to latest `=5.3.8`.
+
+### next-themes Type Error (#122)
+- **Context**: next-themes pinned at `^0.2.1` due to reported type error in v0.3.0
+- **Sources Consulted**: https://github.com/pacocoursey/next-themes/issues/122, https://github.com/pacocoursey/next-themes/issues/375
+- **Findings**:
+  - Issue #122 CLOSED on 2022-06-02 — was specific to an old beta version (v0.0.13-beta.3), not v0.3.0
+  - The pinning reason was based on incomplete information; v0.2.0+ already had the fix
+  - Latest: v0.4.6 (March 2025). Peers: `react ^16.8 || ^17 || ^18 || ^19`
+  - **Caveat**: Issue #375 reports a bug with Next.js 16's `cacheComponents` feature — stale theme values when cached components reactivate
+  - PR #377 in progress to fix via `useSyncExternalStore`
+  - Without `cacheComponents`, v0.4.6 works fine with Next.js 16
+- **Implications**: Upgrade to v0.4.x is feasible. GROWI uses Pages Router (not App Router), so `cacheComponents` is likely not relevant. Breaking API changes between v0.2 → v0.4 need review. Used in 12 files across apps/app.
+
+### ESM-only Package Compatibility (escape-string-regexp, string-width, @keycloak)
+- **Context**: Three packages pinned to CJS-compatible versions because newer versions are ESM-only
+- **Sources Consulted**: Node.js v22.12.0 release notes (require(esm) enabled by default), TC39 RegExp.escape Stage 4, sindresorhus ESM guidance, npm package pages
+- **Findings**:
+
+  **escape-string-regexp** (^4.0.0):
+  - Used in 6 server-side files + 3 shared package files (all server context)
+  - Node.js 24 has stable `require(esm)` — ESM-only v5 would work
+  - **Better**: `RegExp.escape()` is ES2026 Stage 4, natively available in Node.js 24 (V8 support)
+  - Can eliminate the dependency entirely
+
+  **string-width** (=4.2.2):
+  - Used only in `packages/editor/src/models/markdown-table.js`
+  - `@growi/editor` has `"type": "module"` and builds with Vite (ESM context)
+  - No server-side value imports (only type imports in `sync-ydoc.ts`, erased at compile)
+  - Safe to upgrade to v7.x
+
+  **@keycloak/keycloak-admin-client** (^18.0.0):
+  - Used in 1 server-side file: `features/external-user-group/server/service/keycloak-user-group-sync.ts`
+  - Latest: v26.5.5 (February 2026)
+  - `require(esm)` in Node.js 24 should handle it, but API has significant breaking changes (v18 → v26)
+  - Sub-path exports need verification
+  - Higher risk upgrade — API surface changes expected
+
+- **Implications**: string-width is the easiest upgrade. escape-string-regexp should be replaced by native `RegExp.escape()`. @keycloak requires careful API migration and is higher risk.
+
+### @aws-sdk Pinning Analysis
+- **Context**: @aws-sdk/client-s3 and @aws-sdk/s3-request-presigner pinned at 3.454.0
+- **Sources Consulted**: mongodb package.json, npm registry, GROWI source code
+- **Findings**:
+  - Pinning comment says "required by mongodb@4.16.0" but is misleading
+  - mongodb@4.17.2 has `@aws-sdk/credential-providers: ^3.186.0` as **optional** dependency — a different package
+  - The S3 client packages are used directly by GROWI for file upload (server/service/file-uploader/aws/)
+  - Latest: @aws-sdk/client-s3@3.1014.0 (March 2026) — over 500 versions behind
+  - AWS SDK v3 follows semver; any 3.x should be compatible
+- **Implications**: Remove the misleading comment. Change from exact `3.454.0` to `^3.454.0` or update to latest. Low risk.
+
+### Handsontable License Status
+- **Context**: handsontable pinned at =6.2.2 (last MIT version), @handsontable/react at =2.1.0
+- **Sources Consulted**: handsontable.com/docs/software-license, npm, Hacker News discussion
+- **Findings**:
+  - v7.0.0+ (March 2019) switched from MIT to proprietary license — unchanged as of 2026
+  - Free "Hobby" license exists but restricted to non-commercial personal use
+  - Commercial use requires paid subscription
+  - MIT alternatives: AG Grid Community (most mature), Jspreadsheet CE, Univer (Apache 2.0)
+- **Implications**: Must remain pinned. No action possible without license purchase or library replacement. Library replacement is out of scope for this spec.
+
+## Design Decisions
+
+### Decision: Replace escape-string-regexp with native RegExp.escape()
+- **Context**: escape-string-regexp v5 is ESM-only; used in 9 files across server code
+- **Alternatives Considered**:
+  1. Upgrade to v5 with require(esm) support — works but adds unnecessary dependency
+  2. Replace with native `RegExp.escape()` — zero dependencies, future-proof
+- **Selected Approach**: Replace with `RegExp.escape()`
+- **Rationale**: Node.js 24 supports `RegExp.escape()` natively (ES2026 Stage 4). Eliminates a dependency entirely.
+- **Trade-offs**: Requires touching 9 files, but changes are mechanical (find-and-replace)
+- **Follow-up**: Verify `RegExp.escape()` is available in the project's Node.js 24 target
+
+### Decision: Upgrade string-width directly to v7.x
+- **Context**: Used only in @growi/editor (ESM package, Vite-bundled, client-only)
+- **Selected Approach**: Direct upgrade to latest v7.x
+- **Rationale**: Consumer is already ESM; zero CJS concern
+- **Trade-offs**: None significant; API is stable
+
+### Decision: Upgrade bootstrap to ^5.3.4
+- **Context**: Bug fixed in v5.3.4; latest is 5.3.8
+- **Selected Approach**: Change from `=5.3.2` to `^5.3.4`
+- **Rationale**: Original bug resolved; skip v5.3.3
+- **Trade-offs**: Need to verify GROWI's custom SCSS and modal usage against 5.3.4+ changes
+
+### Decision: Upgrade next-themes to latest 0.4.x
+- **Context**: Original issue was a misunderstanding; latest is v0.4.6
+- **Selected Approach**: Upgrade to `^0.4.4` (or latest)
+- **Rationale**: Issue #122 was specific to old beta, not v0.3.0. GROWI uses Pages Router, so cacheComponents bug is not relevant.
+- **Trade-offs**: Breaking API changes between v0.2 → v0.4 need review. 12 files import from next-themes.
+- **Follow-up**: Review v0.3.0 and v0.4.0 changelogs for breaking changes
+
+### Decision: Relax @aws-sdk version to caret range
+- **Context**: Pinning was based on misleading comment; packages are independent of mongodb constraint
+- **Selected Approach**: Change from `3.454.0` to `^3.454.0`
+- **Rationale**: AWS SDK v3 follows semver; the comment conflated credential-providers with S3 client
+- **Trade-offs**: Low risk. Conservative approach keeps minimum at 3.454.0.
+
+### Decision: Defer @keycloak upgrade (high risk)
+- **Context**: v18 → v26 has significant API breaking changes; only 1 file affected
+- **Selected Approach**: Document as upgradeable but defer to a separate task
+- **Rationale**: API migration requires Keycloak server compatibility testing; out of proportion for a batch upgrade task
+- **Trade-offs**: Remains on old version longer, but isolated to one feature
+
+### Decision: Keep handsontable pinned (license constraint)
+- **Context**: v7+ is proprietary; no free alternative that's drop-in
+- **Selected Approach**: No change. Document for future reference.
+- **Rationale**: License constraint is permanent unless library is replaced entirely
+- **Trade-offs**: None — this is a business/legal decision, not technical
+
+## Risks & Mitigations
+- **Bootstrap SCSS breakage**: v5.3.4+ may have SCSS variable changes → Run `pre:styles-commons` and `pre:styles-components` builds to verify
+- **next-themes API changes**: v0.2 → v0.4 has breaking changes → Review changelog; test all 12 consuming files
+- **RegExp.escape() availability**: Ensure Node.js 24 V8 includes it → Verify with simple runtime test
+- **@aws-sdk transitive dependency changes**: Newer AWS SDK may pull different transitive deps → Monitor bundle size
+- **Build regression**: Any upgrade could break Turbopack build → Follow incremental upgrade strategy with build verification per package
+
+## Future Considerations (Out of Scope)
+
+### transpilePackages cleanup in next.config.ts
+- **Context**: `next.config.ts` defines `getTranspilePackages()` listing 60+ ESM-only packages to force Turbopack to bundle them instead of externalising. The original comment says: "listing ESM packages until experimental.esmExternals works correctly to avoid ERR_REQUIRE_ESM".
+- **Relationship to require(esm)**: `transpilePackages` and `require(esm)` solve different problems. `transpilePackages` prevents Turbopack from externalising packages during SSR; `require(esm)` allows Node.js to load ESM packages via `require()` at runtime. With Node.js 24's stable `require(esm)`, externalised ESM packages *should* load correctly in SSR, meaning some `transpilePackages` entries may become unnecessary.
+- **Why not now**: (1) Turbopack's `esmExternals` handling is still `experimental`; (2) removing entries shifts packages from bundled to externalised, which means they appear in `.next/node_modules/` and must be classified as `dependencies` per the `package-dependencies` rule; (3) 60+ packages need individual verification. This is a separate investigation with a large blast radius.
+- **Recommendation**: Track as a separate task. Test by removing a few low-risk entries (e.g., `bail`, `ccount`, `zwitch`) and checking whether SSR still works with Turbopack externalisation + Node.js 24 `require(esm)`.
+
+## References
+- [Bootstrap issue #39798](https://github.com/twbs/bootstrap/issues/39798) — modal header regression, fixed in v5.3.4
+- [next-themes issue #122](https://github.com/pacocoursey/next-themes/issues/122) — type error, resolved in v0.2.0
+- [next-themes issue #375](https://github.com/pacocoursey/next-themes/issues/375) — Next.js 16 cacheComponents bug
+- [TC39 RegExp.escape() Stage 4](https://socket.dev/blog/tc39-advances-3-proposals-to-stage-4-regexp-escaping-float16array-and-redeclarable-global-eval) — ES2026
+- [Node.js require(esm) stability](https://joyeecheung.github.io/blog/2025/12/30/require-esm-in-node-js-from-experiment-to-stability/) — stable since Node.js 22.12.0
+- [Handsontable license change](https://handsontable.com/docs/javascript-data-grid/software-license/) — proprietary since v7.0.0

+ 22 - 0
.kiro/specs/upgrade-fixed-packages/spec.json

@@ -0,0 +1,22 @@
+{
+  "feature_name": "upgrade-fixed-packages",
+  "created_at": "2026-03-23T00:00:00Z",
+  "updated_at": "2026-03-23T00:00:00Z",
+  "language": "en",
+  "phase": "tasks-generated",
+  "approvals": {
+    "requirements": {
+      "generated": true,
+      "approved": true
+    },
+    "design": {
+      "generated": true,
+      "approved": true
+    },
+    "tasks": {
+      "generated": true,
+      "approved": false
+    }
+  },
+  "ready_for_implementation": false
+}

+ 89 - 0
.kiro/specs/upgrade-fixed-packages/tasks.md

@@ -0,0 +1,89 @@
+# Implementation Plan
+
+- [ ] 1. Pre-implementation verification
+- [ ] 1.1 Verify RegExp.escape() availability and TypeScript support
+  - Confirm `RegExp.escape()` is available at runtime in the project's Node.js 24 target
+  - Check whether TypeScript recognizes `RegExp.escape()` — may need `lib` config update or `@types/node` update
+  - If unavailable, fall back to upgrading `escape-string-regexp` to v5 with `require(esm)` instead
+  - _Requirements: 2.2_
+
+- [ ] 1.2 Review next-themes v0.3.0 and v0.4.0 breaking API changes
+  - Read changelogs for v0.3.0 and v0.4.0 releases to identify breaking changes
+  - Map breaking changes to the 12 consuming files in apps/app
+  - Determine migration effort and document required code changes
+  - Confirm GROWI's Pages Router usage is unaffected by the cacheComponents bug (issue #375)
+  - _Requirements: 1.2_
+
+- [ ] 2. Low-risk package upgrades
+- [ ] 2.1 (P) Relax @aws-sdk version range
+  - Change `@aws-sdk/client-s3` from `3.454.0` to `^3.454.0` in apps/app/package.json
+  - Change `@aws-sdk/s3-request-presigner` from `3.454.0` to `^3.454.0`
+  - Update the misleading `"@aws-skd/*"` comment to reflect the actual reason or remove it
+  - Run `pnpm install` and verify build with `turbo run build --filter @growi/app`
+  - Run `turbo run test --filter @growi/app` to confirm no regressions
+  - _Requirements: 1.3, 4.1, 4.2, 4.4_
+
+- [ ] 2.2 (P) Upgrade string-width in @growi/editor
+  - Update `string-width` from `=4.2.2` to `^7.0.0` in packages/editor/package.json
+  - Verify @growi/editor builds successfully (Vite, ESM context)
+  - Run `turbo run build --filter @growi/app` to confirm downstream build passes
+  - Run `turbo run test --filter @growi/app` to confirm no regressions
+  - Remove the `string-width` comment from apps/app/package.json `// comments for dependencies`
+  - _Requirements: 2.1, 2.3, 4.1, 4.2, 4.4_
+
+- [ ] 3. Upgrade bootstrap to ^5.3.4
+  - Change `bootstrap` from `=5.3.2` to `^5.3.4` in apps/app/package.json
+  - Run `pnpm install` to resolve the new version
+  - Run `pnpm run pre:styles-commons` and `pnpm run pre:styles-components` to verify SCSS compilation
+  - Run `turbo run build --filter @growi/app` to confirm Turbopack build passes
+  - Run `turbo run lint --filter @growi/app` to check for type or lint errors
+  - Run `turbo run test --filter @growi/app` to confirm no regressions
+  - Visually inspect modal headers if a dev server is available (original bug was modal header layout)
+  - Remove the `bootstrap` comment from `// comments for dependencies`
+  - If build or SCSS fails, revert and document the failure reason
+  - _Requirements: 1.1, 4.1, 4.2, 4.3, 4.4, 4.5_
+
+- [ ] 4. Replace escape-string-regexp with native RegExp.escape()
+- [ ] 4.1 Migrate all source files from escape-string-regexp to RegExp.escape()
+  - Replace `import escapeStringRegexp from 'escape-string-regexp'` and corresponding calls with `RegExp.escape()` in each file
+  - Files in apps/app/src: page.ts, page/index.ts, page-grant.ts, users.js, obsolete-page.js, openai.ts (6 files)
+  - Files in packages: core/src/utils/page-path-utils (2 files), remark-lsx/src/server/routes/list-pages/index.ts (1 file)
+  - Ensure each replacement preserves the exact same escaping behavior
+  - _Requirements: 4.1_
+
+- [ ] 4.2 Remove escape-string-regexp dependency and verify
+  - Remove `escape-string-regexp` from apps/app/package.json dependencies
+  - Remove from packages/core and packages/remark-lsx package.json if listed
+  - Remove the `escape-string-regexp` comment from `// comments for dependencies`
+  - Remove `escape-string-regexp` entry from `transpilePackages` in next.config.ts
+  - Run `pnpm install` to update lockfile
+  - Run `turbo run build --filter @growi/app` to verify build
+  - Run `turbo run lint --filter @growi/app` to verify no type errors
+  - Run `turbo run test --filter @growi/app` to verify no regressions
+  - If RegExp.escape() has TypeScript issues, add type declaration or adjust lib config
+  - _Requirements: 2.1, 2.2, 4.1, 4.2, 4.3, 4.4, 4.5_
+
+- [ ] 5. Upgrade next-themes to ^0.4.x
+- [ ] 5.1 Update next-themes and adapt consuming code
+  - Change `next-themes` from `^0.2.1` to `^0.4.4` in apps/app/package.json
+  - Apply required API migration changes across the 12 consuming files identified in design
+  - Pay attention to any renamed exports, changed hook signatures, or provider prop changes
+  - Ensure `useTheme()` and `ThemeProvider` usage is compatible with v0.4.x API
+  - _Requirements: 1.2, 4.1_
+
+- [ ] 5.2 Verify next-themes upgrade
+  - Run `turbo run build --filter @growi/app` to confirm build passes
+  - Run `turbo run lint --filter @growi/app` to check for type errors (original pinning was about types)
+  - Run `turbo run test --filter @growi/app` to confirm no regressions
+  - Remove the `next-themes` comment from `// comments for dependencies`
+  - If build or type errors occur, investigate whether the issue is the same as #122 or a new problem
+  - If upgrade fails, revert and document the reason; keep the pin with an updated comment
+  - _Requirements: 4.2, 4.3, 4.4, 4.5, 4.6_
+
+- [ ] 6. Finalize audit documentation and comment blocks
+  - Verify `// comments for dependencies` block contains only packages that remain pinned (@keycloak if unchanged)
+  - Verify `// comments for defDependencies` block is accurate (handsontable entries unchanged)
+  - Update comment text to reflect current reasons where applicable
+  - Produce a final summary table in research.md documenting: package name, previous version, new version or "unchanged", and rationale
+  - Confirm all requirements are satisfied by reviewing the checklist against actual changes made
+  - _Requirements: 3.1, 3.2, 4.6, 5.1, 5.2, 5.3_