@cosmicdrift/kumiko-framework 0.387.0 → 0.388.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/AGENTS.md +6 -1
  2. package/agents/examples/anonymous-access.md +1 -1
  3. package/agents/examples/apex-landing.md +204 -0
  4. package/agents/examples/api-worker-split.md +64 -0
  5. package/agents/examples/app-config.md +14 -0
  6. package/agents/examples/auth-mfa.md +115 -0
  7. package/agents/examples/env-composition.md +28 -1
  8. package/agents/examples/legal-pages.md +1 -1
  9. package/agents/examples/prod-boot-options.md +16 -0
  10. package/agents/examples/rate-limiting.md +48 -0
  11. package/agents/examples/record-detail-layout.md +692 -0
  12. package/agents/examples/seed-migration.md +212 -0
  13. package/agents/examples/wizard-form.md +941 -0
  14. package/agents/examples/writeform-section.md +548 -0
  15. package/agents/features.md +19 -2
  16. package/agents/widgets.md +1 -0
  17. package/dist/api/client-error-route.js +8 -2
  18. package/dist/api/index.d.ts +3 -1
  19. package/dist/api/index.js +3 -1
  20. package/dist/api/request-context.d.ts +1 -1
  21. package/dist/api/request-context.js +1 -1
  22. package/dist/api/server-boot-checks.d.ts +10 -0
  23. package/dist/api/server-boot-checks.js +157 -0
  24. package/dist/api/server-event-dispatcher.d.ts +26 -0
  25. package/dist/api/server-event-dispatcher.js +208 -0
  26. package/dist/api/server-extra-routes.d.ts +11 -0
  27. package/dist/api/server-extra-routes.js +203 -0
  28. package/dist/api/server-middleware.d.ts +20 -0
  29. package/dist/api/server-middleware.js +211 -0
  30. package/dist/api/server-routes.d.ts +19 -0
  31. package/dist/api/server-routes.js +293 -0
  32. package/dist/api/server-websocket-routes.d.ts +4 -0
  33. package/dist/api/server-websocket-routes.js +108 -0
  34. package/dist/api/server-wiring.d.ts +32 -0
  35. package/dist/api/server-wiring.js +236 -0
  36. package/dist/api/server.d.ts +10 -15
  37. package/dist/api/server.js +48 -1251
  38. package/dist/api/session-cookies.js +4 -4
  39. package/dist/api/sse-route.js +16 -12
  40. package/dist/crypto/owner-transition-rekey.d.ts +5 -1
  41. package/dist/crypto/owner-transition-rekey.js +1 -1
  42. package/dist/crypto/pii-field-encryption.d.ts +5 -1
  43. package/dist/crypto/pii-field-encryption.js +3 -1
  44. package/dist/db/aggregate-sql.d.ts +21 -0
  45. package/dist/db/aggregate-sql.js +155 -0
  46. package/dist/db/closed-connection.d.ts +9 -0
  47. package/dist/db/closed-connection.js +95 -0
  48. package/dist/db/dialect.js +1 -1
  49. package/dist/db/distinct-on.d.ts +1 -1
  50. package/dist/db/driver-row-coercion.d.ts +2 -0
  51. package/dist/db/driver-row-coercion.js +186 -0
  52. package/dist/db/driver-value-preparation.d.ts +16 -0
  53. package/dist/db/driver-value-preparation.js +99 -0
  54. package/dist/db/entity-field-encryption.d.ts +1 -0
  55. package/dist/db/entity-field-encryption.js +1 -1
  56. package/dist/db/entity-table-meta.js +103 -110
  57. package/dist/db/event-store-executor-context.d.ts +2 -1
  58. package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
  59. package/dist/db/event-store-executor-read-aggregate.js +112 -0
  60. package/dist/db/event-store-executor-read-filter.d.ts +31 -0
  61. package/dist/db/event-store-executor-read-filter.js +203 -0
  62. package/dist/db/event-store-executor-read-paging.d.ts +14 -0
  63. package/dist/db/event-store-executor-read-paging.js +99 -0
  64. package/dist/db/event-store-executor-read-rows.d.ts +6 -0
  65. package/dist/db/event-store-executor-read-rows.js +32 -0
  66. package/dist/db/event-store-executor-read-search.d.ts +28 -0
  67. package/dist/db/event-store-executor-read-search.js +221 -0
  68. package/dist/db/event-store-executor-read-sort.d.ts +41 -0
  69. package/dist/db/event-store-executor-read-sort.js +130 -0
  70. package/dist/db/event-store-executor-read-where.d.ts +24 -0
  71. package/dist/db/event-store-executor-read-where.js +81 -0
  72. package/dist/db/event-store-executor-read.d.ts +2 -9
  73. package/dist/db/event-store-executor-read.js +48 -701
  74. package/dist/db/event-store-executor-write-steps.d.ts +20 -0
  75. package/dist/db/event-store-executor-write-steps.js +100 -0
  76. package/dist/db/event-store-executor-write.js +395 -499
  77. package/dist/db/index.d.ts +6 -2
  78. package/dist/db/index.js +5 -1
  79. package/dist/db/list-aggregate-labels.d.ts +14 -0
  80. package/dist/db/list-aggregate-labels.js +26 -0
  81. package/dist/db/migrate-generator.js +71 -86
  82. package/dist/db/migrate-runner.js +4 -1
  83. package/dist/db/query.d.ts +2 -15
  84. package/dist/db/query.js +8 -761
  85. package/dist/db/reference-data.js +6 -1
  86. package/dist/db/table-builder.js +113 -166
  87. package/dist/db/table-info.d.ts +10 -0
  88. package/dist/db/table-info.js +45 -0
  89. package/dist/db/temporal-value-guards.d.ts +6 -0
  90. package/dist/db/temporal-value-guards.js +15 -0
  91. package/dist/db/tenant-db-runner.d.ts +1 -0
  92. package/dist/db/tenant-db-runner.js +3 -0
  93. package/dist/db/tenant-db.js +39 -9
  94. package/dist/db/where-clause.d.ts +6 -0
  95. package/dist/db/where-clause.js +236 -0
  96. package/dist/derivatives/delete-stored-file.js +2 -1
  97. package/dist/engine/boot-validator/boot-check.js +1 -1
  98. package/dist/engine/boot-validator/detail-screens.js +2 -2
  99. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  100. package/dist/engine/boot-validator/entity-handler.js +17 -0
  101. package/dist/engine/boot-validator/form-derivations.js +35 -1
  102. package/dist/engine/boot-validator/i18n-keys.js +5 -3
  103. package/dist/engine/boot-validator/index.js +2 -0
  104. package/dist/engine/boot-validator/pii-retention.js +233 -155
  105. package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
  106. package/dist/engine/boot-validator/screens-actions.js +8 -1
  107. package/dist/engine/boot-validator/screens-dashboard.js +2 -5
  108. package/dist/engine/boot-validator/screens-forms.js +2 -6
  109. package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
  110. package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
  111. package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
  112. package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
  113. package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
  114. package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
  115. package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
  116. package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
  117. package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
  118. package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
  119. package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
  120. package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
  121. package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
  122. package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
  123. package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
  124. package/dist/engine/boot-validator/screens-lists.js +12 -9
  125. package/dist/engine/boot-validator/screens.d.ts +0 -2
  126. package/dist/engine/boot-validator/screens.js +99 -860
  127. package/dist/engine/build-app-schema.js +56 -96
  128. package/dist/engine/define-feature.js +9 -2
  129. package/dist/engine/define-handler.js +1 -0
  130. package/dist/engine/entity-handlers.d.ts +13 -0
  131. package/dist/engine/entity-handlers.js +10 -21
  132. package/dist/engine/entity-list-runtime-options.d.ts +8 -0
  133. package/dist/engine/entity-list-runtime-options.js +18 -0
  134. package/dist/engine/feature-ast/extractors/handlers.js +1 -0
  135. package/dist/engine/feature-ast/extractors/round2.js +25 -6
  136. package/dist/engine/feature-ast/patch.js +4 -182
  137. package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
  138. package/dist/engine/feature-ast/pattern-change-schema.js +1 -0
  139. package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
  140. package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
  141. package/dist/engine/feature-ast/patterns.d.ts +9 -0
  142. package/dist/engine/feature-ast/patterns.js +43 -51
  143. package/dist/engine/feature-ast/render.js +48 -87
  144. package/dist/engine/feature-entity-handlers.js +3 -0
  145. package/dist/engine/form-derivations.js +18 -7
  146. package/dist/engine/handler-field-references.js +1 -1
  147. package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
  148. package/dist/engine/project-app-schema-for-roles.js +6 -4
  149. package/dist/engine/registry-ingest.js +1 -1
  150. package/dist/engine/schema-builder.js +228 -212
  151. package/dist/engine/screen-helpers.js +1 -1
  152. package/dist/engine/screen-kind.d.ts +7 -0
  153. package/dist/engine/screen-kind.js +1 -0
  154. package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
  155. package/dist/engine/validate-projection-allowlist.js +2 -1
  156. package/dist/entrypoint/index.d.ts +4 -1
  157. package/dist/entrypoint/index.js +7 -1
  158. package/dist/errors/docs-url.d.ts +1 -1
  159. package/dist/errors/docs-url.js +22 -2
  160. package/dist/es-ops/context.js +1 -1
  161. package/dist/event-store/admin-api.js +1 -1
  162. package/dist/event-store/event-store.js +10 -4
  163. package/dist/files/bind-pending-file-refs.js +3 -3
  164. package/dist/files/create-file-ref-from-bytes.js +4 -1
  165. package/dist/files/file-handle.js +2 -3
  166. package/dist/files/file-routes.js +11 -8
  167. package/dist/files/resolve-content-type.js +43 -0
  168. package/dist/files/upload-ledger.js +7 -0
  169. package/dist/i18n/required-surface-keys.js +182 -206
  170. package/dist/internal/stack-support.d.ts +2 -1
  171. package/dist/internal/stack-support.js +1 -1
  172. package/dist/jobs/job-runner-context.d.ts +32 -0
  173. package/dist/jobs/job-runner-context.js +305 -0
  174. package/dist/jobs/job-runner-execution.d.ts +7 -0
  175. package/dist/jobs/job-runner-execution.js +160 -0
  176. package/dist/jobs/job-runner-gates.d.ts +19 -0
  177. package/dist/jobs/job-runner-gates.js +198 -0
  178. package/dist/jobs/job-runner-helpers.d.ts +17 -3
  179. package/dist/jobs/job-runner-helpers.js +61 -2
  180. package/dist/jobs/job-runner-scope.d.ts +44 -0
  181. package/dist/jobs/job-runner-scope.js +102 -0
  182. package/dist/jobs/job-runner-types.d.ts +12 -0
  183. package/dist/jobs/job-runner.d.ts +0 -1
  184. package/dist/jobs/job-runner.js +47 -657
  185. package/dist/observability/escape-hatch-report.d.ts +3 -0
  186. package/dist/observability/escape-hatch-report.js +12 -1
  187. package/dist/pipeline/cascade-handler.js +165 -138
  188. package/dist/pipeline/dispatch-query.js +10 -3
  189. package/dist/pipeline/dispatch-shared.d.ts +13 -3
  190. package/dist/pipeline/dispatch-shared.js +58 -710
  191. package/dist/pipeline/dispatch-write.js +296 -189
  192. package/dist/pipeline/event-dispatcher-admin.js +2 -1
  193. package/dist/pipeline/event-dispatcher-delivery.js +3 -5
  194. package/dist/pipeline/handler-context-auth.d.ts +9 -0
  195. package/dist/pipeline/handler-context-auth.js +54 -0
  196. package/dist/pipeline/handler-context-config.d.ts +14 -0
  197. package/dist/pipeline/handler-context-config.js +113 -0
  198. package/dist/pipeline/handler-context-db.d.ts +24 -0
  199. package/dist/pipeline/handler-context-db.js +149 -0
  200. package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
  201. package/dist/pipeline/handler-context-dispatch.js +89 -0
  202. package/dist/pipeline/handler-context-events.d.ts +43 -0
  203. package/dist/pipeline/handler-context-events.js +237 -0
  204. package/dist/pipeline/handler-context-notify.d.ts +12 -0
  205. package/dist/pipeline/handler-context-notify.js +55 -0
  206. package/dist/pipeline/handler-context-observability.d.ts +7 -0
  207. package/dist/pipeline/handler-context-observability.js +21 -0
  208. package/dist/pipeline/handler-context-scope.d.ts +48 -0
  209. package/dist/pipeline/handler-context-scope.js +88 -0
  210. package/dist/pipeline/notify-scope.js +12 -1
  211. package/dist/pipeline/pre-handler-gates.js +20 -4
  212. package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
  213. package/dist/pipeline/pre-transaction-reservations.js +9 -3
  214. package/dist/pipeline/projection-rebuild.js +2 -1
  215. package/dist/schema-cli/apply-command.d.ts +2 -0
  216. package/dist/schema-cli/apply-command.js +97 -0
  217. package/dist/schema-cli/baseline-command.d.ts +2 -0
  218. package/dist/schema-cli/baseline-command.js +33 -0
  219. package/dist/schema-cli/generate-command.d.ts +2 -0
  220. package/dist/schema-cli/generate-command.js +90 -0
  221. package/dist/schema-cli/shared.d.ts +39 -0
  222. package/dist/schema-cli/shared.js +95 -0
  223. package/dist/schema-cli/status-command.d.ts +2 -0
  224. package/dist/schema-cli/status-command.js +73 -0
  225. package/dist/schema-cli/validate-boot-input.d.ts +5 -0
  226. package/dist/schema-cli/validate-boot-input.js +130 -0
  227. package/dist/schema-cli/validate-command.d.ts +2 -0
  228. package/dist/schema-cli/validate-command.js +130 -0
  229. package/dist/schema-cli.d.ts +2 -18
  230. package/dist/schema-cli.js +47 -604
  231. package/dist/search/meilisearch-adapter.js +2 -1
  232. package/dist/upgrade-cli.js +149 -121
  233. package/package.json +4 -4
  234. package/src/changes.json +310 -0
  235. package/src/scripts/codemod/feature-layout.ts +10 -2
  236. package/src/scripts/codemod/object-form.ts +41 -4
  237. package/src/scripts/codemod/package-merge.ts +58 -8
  238. package/src/scripts/codemod/pii-personal-migration.ts +175 -145
  239. package/src/scripts/codemod/testing-injection.ts +122 -42
  240. package/src/scripts/codemod/testing-move.ts +29 -7
@@ -34,7 +34,8 @@ function isSubsetOf(stored, wanted) {
34
34
  }
35
35
  // A stored list that lies inside the current default and misses attributes is an
36
36
  // older default that gained fields since; anything with a foreign attribute is
37
- // an explicit configure() and stays untouched.
37
+ // an explicit configure() and stays untouched. Known limit: an explicit configure() that
38
+ // narrowed to a strict subset of the default is indistinguishable and gets widened again.
38
39
  function isOutdatedDefault(stored, wanted) {
39
40
  const pairs = [
40
41
  [stored.searchable, wanted.searchable],
@@ -659,27 +659,27 @@ function matchManualRefs(open, refs) {
659
659
  }
660
660
  return { resolvedIds, errors };
661
661
  }
662
+ function failUpgrade(out, ...lines) {
663
+ out.err("");
664
+ for (const line of lines)
665
+ out.err(` ${line}`);
666
+ out.err("");
667
+ return 1;
668
+ }
662
669
  function resolveManualMigrations(out, args, targetDir) {
663
- const fail = (...lines) => {
664
- out.err("");
665
- for (const line of lines)
666
- out.err(` ${line}`);
667
- out.err("");
668
- return 1;
669
- };
670
670
  const input = parseResolveInput(args);
671
671
  if ("error" in input)
672
- return fail(input.error);
672
+ return failUpgrade(out, input.error);
673
673
  const read = readMarker(targetDir);
674
674
  if (read.kind === "missing") {
675
- return fail(`No .kumiko/upgrade-state.json under ${targetDir}, nothing to resolve.`);
675
+ return failUpgrade(out, `No .kumiko/upgrade-state.json under ${targetDir}, nothing to resolve.`);
676
676
  }
677
677
  if (read.kind === "invalid")
678
- return fail("invalid .kumiko/upgrade-state.json, fix it first.");
678
+ return failUpgrade(out, "invalid .kumiko/upgrade-state.json, fix it first.");
679
679
  const open = read.marker.pendingManual;
680
680
  const { resolvedIds, errors } = matchManualRefs(open, input.refs);
681
681
  if (errors.length > 0)
682
- return fail(...errors, "Nothing was written.");
682
+ return failUpgrade(out, ...errors, "Nothing was written.");
683
683
  const { reason } = input;
684
684
  const resolution = getFlag(args, "not-applicable")
685
685
  ? "not-applicable"
@@ -721,32 +721,7 @@ kumiko-upgrade --resolve <id|version>[,<id|version>...] --reason "<text>" [--not
721
721
  --not-applicable with --resolve, record the entries as not applicable instead of migrated
722
722
  --dry-run with --apply, report what would run without writing anything
723
723
  --help, -h print this usage and exit`;
724
- // kumiko-lint-ignore complexity-budget CLI orchestration moved from bin/commands/upgrade.ts — same branching surface, shared by kumiko upgrade + published kumiko-upgrade bin
725
- export async function runUpgradeCli(argv, cwd, out, options) {
726
- if (argv.includes("--help") || argv.includes("-h")) {
727
- out.log(UPGRADE_USAGE);
728
- return 0;
729
- }
730
- // Standalone CLI entry, not booted via runProdApp/runDevApp — Temporal needs an explicit polyfill here.
731
- await ensureTemporalPolyfill();
732
- const repoRoot = options?.repoRoot ?? cwd;
733
- const args = parseArgs(argv);
734
- const jsonMode = getFlag(args, "json");
735
- const verbose = getFlag(args, "verbose");
736
- const fromFlag = getStringFlag(args, "from");
737
- // Resolved once here (not just inside --apply) so both the filter
738
- // baseline below and the --apply codemod run target the same directory.
739
- const dirFlag = getStringFlag(args, "dir");
740
- const targetDir = dirFlag ? resolve(dirFlag) : cwd;
741
- if (!existsSync(targetDir) || !statSync(targetDir).isDirectory()) {
742
- out.err("");
743
- out.err(` --dir path is not a directory: ${targetDir}`);
744
- out.err("");
745
- return 1;
746
- }
747
- if (args.flags.has("resolve")) {
748
- return resolveManualMigrations(out, args, targetDir);
749
- }
724
+ function resolveUpgradeBaseline(cwd, targetDir, fromFlag) {
750
725
  // Always resolve the actually installed version, even when --from is set,
751
726
  // so --json can report both the filter baseline and what's really there.
752
727
  const installedVersion = readCurrentVersion(cwd);
@@ -761,33 +736,40 @@ export async function runUpgradeCli(argv, cwd, out, options) {
761
736
  const markerRead = readMarker(targetDir);
762
737
  const marker = fromFlag === undefined ? markerRead : { kind: "missing" };
763
738
  if (marker.kind === "invalid") {
764
- out.err("");
765
- out.err(" invalid .kumiko/upgrade-state.json, fix it or pass --from <version>");
766
- out.err("");
767
- return 1;
739
+ return {
740
+ kind: "error",
741
+ lines: ["invalid .kumiko/upgrade-state.json, fix it or pass --from <version>"],
742
+ };
768
743
  }
769
744
  const currentVersion = fromFlag ?? (marker.kind === "ok" ? marker.version : undefined) ?? installedVersion;
770
745
  if (!currentVersion) {
771
- out.err("");
772
- out.err(" Could not detect Kumiko version.");
773
- out.err(" Run from an app directory with node_modules, or use --from <version>.");
774
- out.err("");
775
- return 1;
746
+ return {
747
+ kind: "error",
748
+ lines: [
749
+ "Could not detect Kumiko version.",
750
+ "Run from an app directory with node_modules, or use --from <version>.",
751
+ ],
752
+ };
776
753
  }
777
754
  if (!SEMVER_RE.test(currentVersion)) {
778
- out.err("");
779
- out.err(` Invalid version format: "${currentVersion}" — expected x.y.z`);
780
- out.err("");
781
- return 1;
755
+ return {
756
+ kind: "error",
757
+ lines: [`Invalid version format: "${currentVersion}" — expected x.y.z`],
758
+ };
782
759
  }
760
+ return { kind: "ok", value: { currentVersion, installedVersion, markerRead } };
761
+ }
762
+ function collectAllChangelogEntries(cwd) {
783
763
  const featuresDirs = findFeaturesDirs(cwd);
784
764
  const packageChangelogFiles = findPackageChangelogFiles(cwd);
785
765
  if (featuresDirs.length === 0 && packageChangelogFiles.length === 0) {
786
- out.err("");
787
- out.err(" Could not find bundled-features directory.");
788
- out.err(" Run from framework/enterprise repo or an app with node_modules.");
789
- out.err("");
790
- return 1;
766
+ return {
767
+ kind: "error",
768
+ lines: [
769
+ "Could not find bundled-features directory.",
770
+ "Run from framework/enterprise repo or an app with node_modules.",
771
+ ],
772
+ };
791
773
  }
792
774
  const allEntries = [];
793
775
  for (const dir of featuresDirs) {
@@ -796,6 +778,18 @@ export async function runUpgradeCli(argv, cwd, out, options) {
796
778
  for (const changelogFile of packageChangelogFiles) {
797
779
  allEntries.push(...readChangelogFile(changelogFile));
798
780
  }
781
+ return { kind: "ok", value: allEntries };
782
+ }
783
+ function planUpgrade(args, cwd, targetDir) {
784
+ const dirFlag = getStringFlag(args, "dir");
785
+ const baseline = resolveUpgradeBaseline(cwd, targetDir, getStringFlag(args, "from"));
786
+ if (baseline.kind === "error")
787
+ return baseline;
788
+ const entries = collectAllChangelogEntries(cwd);
789
+ if (entries.kind === "error")
790
+ return entries;
791
+ const { currentVersion, installedVersion } = baseline.value;
792
+ const allEntries = entries.value;
799
793
  // Installed version of the target the marker belongs to (cwd unless --dir).
800
794
  const targetInstalledVersion = dirFlag ? readCurrentVersion(targetDir) : installedVersion;
801
795
  // Changelogs can be found in a parent workspace's newer install. A change
@@ -804,87 +798,121 @@ export async function runUpgradeCli(argv, cwd, out, options) {
804
798
  ? allEntries
805
799
  : allEntries.filter((entry) => compareVersions(entry.version, targetInstalledVersion) <= 0);
806
800
  const pending = sortEntries(filterEntriesAfter(installedEntries, currentVersion));
807
- if (getFlag(args, "apply")) {
808
- // Marker must reflect what is actually installed under the target (or
809
- // cwd), not the filter baseline above — otherwise CI stays green forever.
810
- const markerVersion = targetInstalledVersion;
811
- const dryRun = getFlag(args, "dry-run");
812
- if (markerVersion === null && !dryRun) {
813
- out.err("");
814
- out.err(` Could not detect the installed Kumiko version under ${targetDir}.`);
815
- out.err(" Refusing to write an upgrade marker without it (--from is only a filter).");
816
- out.err("");
817
- return 1;
801
+ return {
802
+ kind: "ok",
803
+ value: { ...baseline.value, targetInstalledVersion, allEntries, pending },
804
+ };
805
+ }
806
+ async function applyPlannedUpgrade(out, args, plan, repoRoot, targetDir) {
807
+ // Marker must reflect what is actually installed under the target (or
808
+ // cwd), not the filter baseline above — otherwise CI stays green forever.
809
+ const markerVersion = plan.targetInstalledVersion;
810
+ const dryRun = getFlag(args, "dry-run");
811
+ if (markerVersion === null && !dryRun) {
812
+ return failUpgrade(out, `Could not detect the installed Kumiko version under ${targetDir}.`, "Refusing to write an upgrade marker without it (--from is only a filter).");
813
+ }
814
+ out.log("");
815
+ const code = await applyCodemods(out, plan.pending, repoRoot, targetDir, dryRun, markerVersion ?? plan.currentVersion, carryOf(plan.markerRead), plan.currentVersion, getFlag(args, "verbose") ? manualDetailsOf(plan.allEntries) : undefined);
816
+ out.log("");
817
+ return code;
818
+ }
819
+ function reportUpgradeJson(out, plan) {
820
+ const { currentVersion, installedVersion } = plan;
821
+ const { pendingManual } = carryOf(plan.markerRead);
822
+ const pendingWithManualId = plan.pending.map((entry) => {
823
+ const manualId = manualIdOf(entry);
824
+ return manualId === undefined ? entry : { ...entry, manualId };
825
+ });
826
+ out.log(JSON.stringify({ currentVersion, installedVersion, pending: pendingWithManualId, pendingManual }, null, 2));
827
+ return 0;
828
+ }
829
+ function logEntryDetail(out, entry, verbose) {
830
+ if (verbose && entry.detail) {
831
+ out.log(` ${entry.detail}`);
832
+ }
833
+ }
834
+ function logBreakingSection(out, breaking, verbose) {
835
+ // skip: no breaking entries, the section is omitted
836
+ if (breaking.length === 0)
837
+ return;
838
+ out.log(` ⚠ BREAKING (${breaking.length})`);
839
+ out.log("");
840
+ for (const e of breaking) {
841
+ const manualId = manualIdOf(e);
842
+ out.log(` ${e.version} · ${e.title}${manualId === undefined ? "" : ` [${manualId}]`}`);
843
+ logEntryDetail(out, e, verbose);
844
+ if (e.migration) {
845
+ out.log(` → Migration: ${e.migration}`);
818
846
  }
819
847
  out.log("");
820
- const code = await applyCodemods(out, pending, repoRoot, targetDir, dryRun, markerVersion ?? currentVersion, carryOf(markerRead), currentVersion, verbose ? manualDetailsOf(allEntries) : undefined);
821
- out.log("");
822
- return code;
823
- }
824
- if (jsonMode) {
825
- const { pendingManual } = carryOf(markerRead);
826
- const pendingWithManualId = pending.map((entry) => {
827
- const manualId = manualIdOf(entry);
828
- return manualId === undefined ? entry : { ...entry, manualId };
829
- });
830
- out.log(JSON.stringify({ currentVersion, installedVersion, pending: pendingWithManualId, pendingManual }, null, 2));
831
- return 0;
832
848
  }
849
+ }
850
+ function logNonBreakingSection(out, heading, entries, verbose, showCodemod) {
851
+ // skip: no entries of this type, the section is omitted
852
+ if (entries.length === 0)
853
+ return;
854
+ out.log(` ✓ ${heading} (${entries.length})`);
855
+ out.log("");
856
+ for (const e of entries) {
857
+ out.log(` ${e.version} · ${e.title}`);
858
+ logEntryDetail(out, e, verbose);
859
+ if (showCodemod && hasCodemod(e)) {
860
+ out.log(` → codemod: ${e.codemod} (runs on --apply)`);
861
+ }
862
+ }
863
+ out.log("");
864
+ }
865
+ function reportUpgradeText(out, plan, verbose) {
866
+ const { pending } = plan;
833
867
  const breaking = pending.filter((e) => e.type === "breaking");
834
- const improvements = pending.filter((e) => e.type === "improvement");
835
- const fixes = pending.filter((e) => e.type === "fix");
836
- const openManual = carryOf(markerRead).pendingManual;
868
+ const openManual = carryOf(plan.markerRead).pendingManual;
869
+ const manualDetails = verbose ? manualDetailsOf(plan.allEntries) : undefined;
837
870
  out.log("");
838
- out.log(` Upgrade: ${currentVersion} → latest`);
871
+ out.log(` Upgrade: ${plan.currentVersion} → latest`);
839
872
  out.log("");
840
873
  if (pending.length === 0) {
841
874
  out.log(" ✓ Nothing new since your version.");
842
875
  out.log("");
843
- logOpenManual(out, openManual, verbose ? manualDetailsOf(allEntries) : undefined);
876
+ logOpenManual(out, openManual, manualDetails);
844
877
  return 0;
845
878
  }
846
- if (breaking.length > 0) {
847
- out.log(` ⚠ BREAKING (${breaking.length})`);
848
- out.log("");
849
- for (const e of breaking) {
850
- const manualId = manualIdOf(e);
851
- out.log(` ${e.version} · ${e.title}${manualId === undefined ? "" : ` [${manualId}]`}`);
852
- if (verbose && e.detail) {
853
- out.log(` ${e.detail}`);
854
- }
855
- if (e.migration) {
856
- out.log(` → Migration: ${e.migration}`);
857
- }
858
- out.log("");
859
- }
860
- }
861
- if (improvements.length > 0) {
862
- out.log(` ✓ IMPROVEMENTS (${improvements.length})`);
863
- out.log("");
864
- for (const e of improvements) {
865
- out.log(` ${e.version} · ${e.title}`);
866
- if (verbose && e.detail) {
867
- out.log(` ${e.detail}`);
868
- }
869
- }
870
- out.log("");
871
- }
872
- if (fixes.length > 0) {
873
- out.log(` ✓ FIXES (${fixes.length})`);
874
- out.log("");
875
- for (const e of fixes) {
876
- out.log(` ${e.version} · ${e.title}`);
877
- if (verbose && e.detail) {
878
- out.log(` ${e.detail}`);
879
- }
880
- }
881
- out.log("");
882
- }
879
+ logBreakingSection(out, breaking, verbose);
880
+ logNonBreakingSection(out, "IMPROVEMENTS", pending.filter((e) => e.type === "improvement"), verbose, true);
881
+ logNonBreakingSection(out, "FIXES", pending.filter((e) => e.type === "fix"), verbose, false);
883
882
  if (breaking.length > 0) {
884
883
  out.log(" ⚠ Review breaking changes above before upgrading.");
885
884
  out.log(" Run with --verbose for full migration details.");
886
885
  out.log("");
887
886
  }
888
- logOpenManual(out, openManual, verbose ? manualDetailsOf(allEntries) : undefined);
887
+ logOpenManual(out, openManual, manualDetails);
889
888
  return 0;
890
889
  }
890
+ // Shared by `kumiko upgrade` and the published kumiko-upgrade bin.
891
+ export async function runUpgradeCli(argv, cwd, out, options) {
892
+ if (argv.includes("--help") || argv.includes("-h")) {
893
+ out.log(UPGRADE_USAGE);
894
+ return 0;
895
+ }
896
+ // Standalone CLI entry, not booted via runProdApp/runDevApp — Temporal needs an explicit polyfill here.
897
+ await ensureTemporalPolyfill();
898
+ const args = parseArgs(argv);
899
+ // Resolved once here (not just inside --apply) so both the filter
900
+ // baseline and the --apply codemod run target the same directory.
901
+ const dirFlag = getStringFlag(args, "dir");
902
+ const targetDir = dirFlag ? resolve(dirFlag) : cwd;
903
+ if (!existsSync(targetDir) || !statSync(targetDir).isDirectory()) {
904
+ return failUpgrade(out, `--dir path is not a directory: ${targetDir}`);
905
+ }
906
+ if (args.flags.has("resolve")) {
907
+ return resolveManualMigrations(out, args, targetDir);
908
+ }
909
+ const planned = planUpgrade(args, cwd, targetDir);
910
+ if (planned.kind === "error")
911
+ return failUpgrade(out, ...planned.lines);
912
+ if (getFlag(args, "apply")) {
913
+ return applyPlannedUpgrade(out, args, planned.value, options?.repoRoot ?? cwd, targetDir);
914
+ }
915
+ if (getFlag(args, "json"))
916
+ return reportUpgradeJson(out, planned.value);
917
+ return reportUpgradeText(out, planned.value, getFlag(args, "verbose"));
918
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.387.0",
3
+ "version": "0.388.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -199,7 +199,7 @@
199
199
  "./package.json": "./package.json"
200
200
  },
201
201
  "dependencies": {
202
- "@cosmicdrift/kumiko-types": "0.387.0",
202
+ "@cosmicdrift/kumiko-types": "0.388.0",
203
203
  "bullmq": "^5.76.7",
204
204
  "bun-types": "^1.3.13",
205
205
  "hono": "^4.13.1",
@@ -208,14 +208,14 @@
208
208
  "jose": "^6.2.3",
209
209
  "meilisearch": "^0.58.0",
210
210
  "pino": "^10.3.1",
211
- "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.1",
211
+ "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.2",
212
212
  "temporal-polyfill": "^0.3.2",
213
213
  "ts-morph": "^28.0.0",
214
214
  "uuid": "^14.0.0",
215
215
  "zod": "^4.4.3"
216
216
  },
217
217
  "devDependencies": {
218
- "@cosmicdrift/kumiko-testing": "0.387.0",
218
+ "@cosmicdrift/kumiko-testing": "0.388.0",
219
219
  "bun-types": "^1.3.13",
220
220
  "pino-pretty": "^13.1.3"
221
221
  },