@cosmicdrift/kumiko-framework 0.356.0 → 0.358.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 (51) hide show
  1. package/dist/crypto/pii-field-encryption.d.ts +1 -0
  2. package/dist/crypto/pii-field-encryption.js +1 -0
  3. package/dist/crypto/subject-resolver.d.ts +2 -0
  4. package/dist/crypto/subject-resolver.js +9 -6
  5. package/dist/db/connection.d.ts +2 -0
  6. package/dist/db/connection.js +10 -0
  7. package/dist/db/event-store-executor-context.d.ts +4 -1
  8. package/dist/db/event-store-executor-context.js +4 -2
  9. package/dist/db/event-store-executor-write.js +6 -1
  10. package/dist/db/index.d.ts +1 -1
  11. package/dist/db/index.js +1 -1
  12. package/dist/db/queries/backfill-pii.js +58 -34
  13. package/dist/engine/boot-validator/card-meta-overflow.d.ts +10 -0
  14. package/dist/engine/boot-validator/card-meta-overflow.js +95 -0
  15. package/dist/engine/boot-validator/entity-handler.js +3 -2
  16. package/dist/engine/boot-validator/index.d.ts +1 -0
  17. package/dist/engine/boot-validator/index.js +4 -1
  18. package/dist/engine/boot-validator/pii-retention.js +7 -0
  19. package/dist/engine/boot-validator/query-refs.js +3 -0
  20. package/dist/engine/boot-validator/screens.js +7 -1
  21. package/dist/engine/boot-validator.d.ts +1 -1
  22. package/dist/engine/boot-validator.js +1 -1
  23. package/dist/engine/factories.js +23 -19
  24. package/dist/engine/feature-ast/extractors/resolve-import.d.ts +3 -1
  25. package/dist/engine/feature-ast/extractors/resolve-import.js +9 -8
  26. package/dist/engine/feature-ast/extractors/round3.js +3 -2
  27. package/dist/engine/feature-ast/extractors/shared.d.ts +12 -2
  28. package/dist/engine/feature-ast/extractors/shared.js +31 -11
  29. package/dist/engine/index.d.ts +1 -1
  30. package/dist/engine/index.js +1 -1
  31. package/dist/files/file-ref-entity.d.ts +6 -0
  32. package/dist/files/file-ref-table.d.ts +6 -0
  33. package/dist/i18n/required-surface-keys.js +1 -0
  34. package/dist/jobs/job-runner.d.ts +1 -0
  35. package/dist/jobs/job-runner.js +51 -13
  36. package/dist/pipeline/dispatch-shared.d.ts +1 -0
  37. package/dist/pipeline/dispatch-shared.js +1 -1
  38. package/dist/pipeline/dispatcher.d.ts +2 -0
  39. package/dist/pipeline/dispatcher.js +1 -0
  40. package/dist/pipeline/system-hooks.js +7 -3
  41. package/dist/stack/db.d.ts +3 -0
  42. package/dist/stack/db.js +7 -1
  43. package/dist/stack/test-stack.d.ts +4 -0
  44. package/dist/stack/test-stack.js +5 -0
  45. package/dist/testing/shared-entities.d.ts +10 -0
  46. package/dist/ui-types/card-column-roles.d.ts +14 -0
  47. package/dist/ui-types/card-column-roles.js +9 -0
  48. package/dist/ui-types/index.d.ts +2 -0
  49. package/dist/ui-types/index.js +1 -0
  50. package/package.json +4 -4
  51. package/src/changes.json +38 -0
@@ -4,6 +4,7 @@ export declare const sharedWidgetEntity: import("@cosmicdrift/kumiko-types/field
4
4
  readonly description?: string;
5
5
  readonly maxLength?: number;
6
6
  readonly minLength?: number;
7
+ readonly autoComplete?: string;
7
8
  readonly required?: boolean;
8
9
  readonly searchable?: boolean;
9
10
  readonly sortable?: boolean;
@@ -28,6 +29,7 @@ export declare const sharedWidgetTable: import("../db/table-builder.js").EntityT
28
29
  readonly description?: string;
29
30
  readonly maxLength?: number;
30
31
  readonly minLength?: number;
32
+ readonly autoComplete?: string;
31
33
  readonly required?: boolean;
32
34
  readonly searchable?: boolean;
33
35
  readonly sortable?: boolean;
@@ -52,6 +54,7 @@ export declare const sharedUserEntity: import("@cosmicdrift/kumiko-types/fields"
52
54
  readonly description?: string;
53
55
  readonly maxLength?: number;
54
56
  readonly minLength?: number;
57
+ readonly autoComplete?: string;
55
58
  readonly required?: boolean;
56
59
  readonly searchable?: boolean;
57
60
  readonly sortable?: boolean;
@@ -74,6 +77,7 @@ export declare const sharedUserEntity: import("@cosmicdrift/kumiko-types/fields"
74
77
  readonly description?: string;
75
78
  readonly maxLength?: number;
76
79
  readonly minLength?: number;
80
+ readonly autoComplete?: string;
77
81
  readonly required?: boolean;
78
82
  readonly searchable?: boolean;
79
83
  readonly sortable?: boolean;
@@ -96,6 +100,7 @@ export declare const sharedUserEntity: import("@cosmicdrift/kumiko-types/fields"
96
100
  readonly description?: string;
97
101
  readonly maxLength?: number;
98
102
  readonly minLength?: number;
103
+ readonly autoComplete?: string;
99
104
  readonly required?: boolean;
100
105
  readonly searchable?: boolean;
101
106
  readonly sortable?: boolean;
@@ -123,6 +128,7 @@ export declare const sharedUserTable: import("../db/table-builder.js").EntityTab
123
128
  readonly description?: string;
124
129
  readonly maxLength?: number;
125
130
  readonly minLength?: number;
131
+ readonly autoComplete?: string;
126
132
  readonly required?: boolean;
127
133
  readonly searchable?: boolean;
128
134
  readonly sortable?: boolean;
@@ -145,6 +151,7 @@ export declare const sharedUserTable: import("../db/table-builder.js").EntityTab
145
151
  readonly description?: string;
146
152
  readonly maxLength?: number;
147
153
  readonly minLength?: number;
154
+ readonly autoComplete?: string;
148
155
  readonly required?: boolean;
149
156
  readonly searchable?: boolean;
150
157
  readonly sortable?: boolean;
@@ -167,6 +174,7 @@ export declare const sharedUserTable: import("../db/table-builder.js").EntityTab
167
174
  readonly description?: string;
168
175
  readonly maxLength?: number;
169
176
  readonly minLength?: number;
177
+ readonly autoComplete?: string;
170
178
  readonly required?: boolean;
171
179
  readonly searchable?: boolean;
172
180
  readonly sortable?: boolean;
@@ -194,6 +202,7 @@ export declare const sharedItemEntity: import("@cosmicdrift/kumiko-types/fields"
194
202
  readonly description?: string;
195
203
  readonly maxLength?: number;
196
204
  readonly minLength?: number;
205
+ readonly autoComplete?: string;
197
206
  readonly required?: boolean;
198
207
  readonly searchable?: boolean;
199
208
  readonly sortable?: boolean;
@@ -237,6 +246,7 @@ export declare const sharedItemTable: import("../db/table-builder.js").EntityTab
237
246
  readonly description?: string;
238
247
  readonly maxLength?: number;
239
248
  readonly minLength?: number;
249
+ readonly autoComplete?: string;
240
250
  readonly required?: boolean;
241
251
  readonly searchable?: boolean;
242
252
  readonly sortable?: boolean;
@@ -0,0 +1,14 @@
1
+ export declare const CARD_META_MAX = 3;
2
+ export type CardRoleColumn = {
3
+ readonly field: string;
4
+ readonly highlighted?: boolean;
5
+ readonly type?: string;
6
+ readonly renderer?: unknown;
7
+ readonly hideOnNarrow?: boolean;
8
+ };
9
+ export type CardColumnRoles<C extends CardRoleColumn> = {
10
+ readonly title: C | undefined;
11
+ readonly status: C | undefined;
12
+ readonly meta: readonly C[];
13
+ };
14
+ export declare function cardColumnRoles<C extends CardRoleColumn>(columns: readonly C[]): CardColumnRoles<C>;
@@ -0,0 +1,9 @@
1
+ export const CARD_META_MAX = 3;
2
+ // One rule for the narrow-layout list card, shared by the renderer and the
3
+ // boot validator so the validator counts exactly what the card would draw.
4
+ export function cardColumnRoles(columns) {
5
+ const title = columns.find((col) => col.highlighted === true) ?? columns[0];
6
+ const status = columns.find((col) => col !== title && col.type === "select" && col.renderer === undefined);
7
+ const meta = columns.filter((col) => col !== title && col !== status && col.hideOnNarrow !== true);
8
+ return { title, status, meta };
9
+ }
@@ -16,6 +16,8 @@ export type { TreeAction, TreeNode, TreeNodeState } from "../engine/types/tree-n
16
16
  export type { WorkspaceDefinition } from "../engine/types/workspace.js";
17
17
  export { ACTION_FORM_ENTITY, handlerFieldLabelKey, handlerTitleKey, PROJECTION_DETAIL_ENTITY, WRITE_FORM_SECTION_ENTITY, } from "../i18n/required-surface-keys.js";
18
18
  export type { AppSchema, FeatureSchema, QualifiedContentCollection, WorkspaceSchema, } from "./app-schema.js";
19
+ export type { CardColumnRoles, CardRoleColumn } from "./card-column-roles.js";
20
+ export { CARD_META_MAX, cardColumnRoles } from "./card-column-roles.js";
19
21
  export { type Formality, formalLocaleTag } from "./formality.js";
20
22
  export type { ListRowMetaColumnType, ListRowMetaReference, ReferenceLookupSource, SystemReferenceLabel, } from "./list-row-meta.js";
21
23
  export { LIST_ROW_META_COLUMNS, LIST_ROW_META_REFERENCES, REFERENCE_LOOKUP_SOURCES, SYSTEM_REFERENCE_LABELS, } from "./list-row-meta.js";
@@ -26,6 +26,7 @@ export { availableSelectOptions, evalFieldCondition, explicitListScreenId, findU
26
26
  export { isOpenToAllGranted, isUiAccessGranted } from "../engine/types/handlers.js";
27
27
  export { metricField, relatedListGroupHeaderLabel, relatedListGroupKey, } from "../engine/types/screen.js";
28
28
  export { ACTION_FORM_ENTITY, handlerFieldLabelKey, handlerTitleKey, PROJECTION_DETAIL_ENTITY, WRITE_FORM_SECTION_ENTITY, } from "../i18n/required-surface-keys.js";
29
+ export { CARD_META_MAX, cardColumnRoles } from "./card-column-roles.js";
29
30
  export { formalLocaleTag } from "./formality.js";
30
31
  export { LIST_ROW_META_COLUMNS, LIST_ROW_META_REFERENCES, REFERENCE_LOOKUP_SOURCES, SYSTEM_REFERENCE_LABELS, } from "./list-row-meta.js";
31
32
  export { resolveTranslationValue, translationValueOtherText, } from "./plural.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.356.0",
3
+ "version": "0.358.0",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -210,8 +210,8 @@
210
210
  "./package.json": "./package.json"
211
211
  },
212
212
  "dependencies": {
213
- "@cosmicdrift/kumiko-http": "0.356.0",
214
- "@cosmicdrift/kumiko-types": "0.356.0",
213
+ "@cosmicdrift/kumiko-http": "0.358.0",
214
+ "@cosmicdrift/kumiko-types": "0.358.0",
215
215
  "bullmq": "^5.76.7",
216
216
  "bun-types": "^1.3.13",
217
217
  "hono": "^4.13.1",
@@ -227,7 +227,7 @@
227
227
  "zod": "^4.4.3"
228
228
  },
229
229
  "devDependencies": {
230
- "@cosmicdrift/kumiko-dispatcher-live": "0.356.0",
230
+ "@cosmicdrift/kumiko-dispatcher-live": "0.358.0",
231
231
  "bun-types": "^1.3.13",
232
232
  "pino-pretty": "^13.1.3"
233
233
  },
package/src/changes.json CHANGED
@@ -1,4 +1,42 @@
1
1
  [
2
+ {
3
+ "version": "0.358.0",
4
+ "type": "fix",
5
+ "title": "useExtension options with function values and package-imported extension names no longer fail to parse",
6
+ "detail": "r.useExtension(...) options that contain arrow or function expressions (for example isPublic: ({ entityId }) => ...) were reported as \"options could not be read as a plain object\", which turned the whole feature into a legacy corpus entry. The functions are now kept verbatim as raw references, option keys stay visible and parse to render round-trips unchanged. An extension name imported from a package whose types resolve to dist/*.d.ts (an installed @cosmicdrift/* package in a consumer repo) is now read from its `export declare const NAME: \"value\"` type instead of failing. Function values elsewhere (entity, nav, metric, job) are still rejected on purpose.",
7
+ "migration": "Consumers that ship a checked-in few-shot corpus (Enterprise) should regenerate it on the next framework bump; its drift test enforces that. The new ratchet test in dev-server pins the framework's own canonical count and parse-error ceiling."
8
+ },
9
+ {
10
+ "version": "0.358.0",
11
+ "type": "fix",
12
+ "title": "Job runner removes job schedulers of cron jobs that no longer exist",
13
+ "detail": "When an app drops a cron job, its BullMQ job scheduler stayed in Redis and kept firing, so the worker logged `Unknown job` on every tick. On start, each lane now removes every scheduler in its own queue that does not belong to a registered cron job of that lane (logged per removal). Each runner records when it last upserted a scheduler; a scheduler outside the runner's own set is only removed if it was not upserted within the last hour, so an old pod restarting during a rolling deploy does not delete the scheduler of a cron job the new version just added. Schedulers with no record, such as the old colon-form ids, are removed right away. A failing cleanup is logged as a warning and does not stop the start. This replaces the dedicated removal of the old colon-form scheduler ids."
14
+ },
15
+ {
16
+ "version": "0.358.0",
17
+ "type": "fix",
18
+ "title": "Outside-transaction access no longer deadlocks against the write pool",
19
+ "detail": "ctx.dbOutsideTransaction, ctx.systemDb.outsideTransaction and ctx.outsideTransaction used the same pool as the handler's own transaction. With as many concurrent writes as pool slots, each write held a connection and waited for a second one, so every request hung forever. They now use a dedicated pool (DispatcherOptions.outsideTransactionDb) sized by the new DATABASE_OUTSIDE_TX_POOL_MAX (default 3). runProdApp and the worker boot also read DATABASE_POOL_MAX, DATABASE_POOL_IDLE_TIMEOUT and DATABASE_POOL_CONNECT_TIMEOUT now; before, production ignored them and used the postgres.js default of 10.",
20
+ "migration": "Each pod now opens up to DATABASE_POOL_MAX + DATABASE_OUTSIDE_TX_POOL_MAX connections (defaults 10 + 3) plus the LISTEN connection; check the total against Postgres max_connections, including rolling-update surge. A set but invalid DATABASE_POOL_MAX, DATABASE_POOL_IDLE_TIMEOUT, DATABASE_POOL_CONNECT_TIMEOUT or DATABASE_OUTSIDE_TX_POOL_MAX now aborts the boot. Hand-built dispatchers can pass outsideTransactionDb in DispatcherOptions; without it the app pool is shared as before."
21
+ },
22
+ {
23
+ "version": "0.358.0",
24
+ "type": "breaking",
25
+ "title": "Entity PII fields need whenAbsent: \"tenant\" when the owner field is nullable",
26
+ "detail": "personal: { of: \"<owner>\", whenAbsent: \"tenant\" } makes rows without an owner editable, deletable and forgettable with an active KMS, without plaintext PII. The subject is resolved in this order: the owner on the previous row, the owner the same event sets, then the tenant key (tenant:<tenantId>). Before, an update, delete or forget of an ownerless row failed with SubjectResolutionError (HTTP 500) once a KMS was configured. backfill-pii follows the same chain and checks the projection row for the owner before it falls back to the tenant key; an updated event whose previous.owner is null but whose changes.owner is set no longer loses the owner. Entity fields have no plaintext fallback, so \"tenant\" is the only value. bundled jobs: cron and system runs (triggeredById null) now store payload, error and log messages as ciphertext under the system tenant key instead of plaintext.",
27
+ "migration": "Boot now aborts for a personal: { of: \"<owner>\" } field whose owner field is not required and that declares no whenAbsent: [Feature X] Field \"f\" on entity \"e\" is personal: { of: \"ownerId\" }, but \"ownerId\" is not required — rows without an owner would fail to encrypt (SubjectResolutionError, HTTP 500) on update, delete and forget. Declare { personal: { of: \"ownerId\", whenAbsent: \"tenant\" } } to encrypt those rows under the tenant key, or make \"ownerId\" required: true. Entity fields have no plaintext fallback (fw#3654). Fix: add whenAbsent: \"tenant\", or make the owner field required: true if every row has one. whenAbsent on personal: { of: \"id\" } throws at declaration. Known consumer sites: kumiko-enterprise kumiko-credit entity.ts and bauspar-entity.ts (ownerUserId), ai-agent entity.ts (authorId, ownerId) and attachment-store.ts (authorId), ai-conversation entity.ts (authorId), pattern-storage entity.ts (authorId), offlot-app src/features/lead/schema/entity.ts (enquiryId). Existing plaintext job-run rows of cron runs stay readable; new ones are ciphertext."
28
+ },
29
+ {
30
+ "version": "0.357.0",
31
+ "type": "improvement",
32
+ "title": "Executor update accepts includeDeleted for soft-deleted rows",
33
+ "detail": "`executor.update(payload, user, db, { includeDeleted: true })` loads the row without the soft-delete filter (the tenant filter stays), so server code can move a trashed row together with its parent (for example a team change) and a later restore lands consistently. The row stays deleted. The default is unchanged (NotFound) and the generic HTTP update handler never sets the option. The projection apply was already unfiltered by `isDeleted`, so live and rebuild agree. The search consumer treats an update on a soft-deleted row as a removal, so the moved row does not reappear in search."
34
+ },
35
+ {
36
+ "version": "0.357.0",
37
+ "type": "improvement",
38
+ "title": "Boot validation covers multiSelect optionsAvailabilityQuery on entity and screen fields"
39
+ },
2
40
  {
3
41
  "version": "0.356.0",
4
42
  "type": "improvement",