@cosmicdrift/kumiko-framework 0.353.0 → 0.354.1
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.
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/redis-sse-broker.d.ts +1 -0
- package/dist/api/redis-sse-broker.js +16 -10
- package/dist/api/request-context.d.ts +1 -0
- package/dist/api/request-context.js +5 -0
- package/dist/api/routes.js +4 -2
- package/dist/api/server.js +28 -3
- package/dist/api/sse-broker.d.ts +7 -3
- package/dist/api/sse-route.js +2 -6
- package/dist/api/token-expiry-timer.d.ts +1 -0
- package/dist/api/token-expiry-timer.js +10 -0
- package/dist/bun-db/query.js +29 -2
- package/dist/crypto/event-pii.js +4 -2
- package/dist/crypto/kms-wiring.js +31 -5
- package/dist/db/eagerload.d.ts +8 -3
- package/dist/db/eagerload.js +85 -81
- package/dist/db/event-store-executor-context.d.ts +12 -0
- package/dist/db/event-store-executor-context.js +59 -18
- package/dist/db/event-store-executor-read.js +81 -31
- package/dist/db/event-store-executor-write.js +54 -3
- package/dist/db/index.d.ts +1 -1
- package/dist/db/migrate-generator.js +1 -1
- package/dist/db/queries/backfill-pii.d.ts +1 -0
- package/dist/db/queries/backfill-pii.js +11 -6
- package/dist/db/queries/event-consumer.d.ts +2 -3
- package/dist/db/queries/event-consumer.js +54 -43
- package/dist/db/queries/event-store.d.ts +5 -0
- package/dist/db/queries/event-store.js +51 -3
- package/dist/db/queries/pii-backfill-state.d.ts +8 -1
- package/dist/db/queries/pii-backfill-state.js +16 -5
- package/dist/db/queries/shadow-swap.js +32 -3
- package/dist/db/render-ddl.js +6 -6
- package/dist/db/table-builder.js +6 -7
- package/dist/db/tenant-db.d.ts +5 -2
- package/dist/db/tenant-db.js +47 -7
- package/dist/derivatives/variant-key.d.ts +1 -0
- package/dist/derivatives/variant-key.js +3 -0
- package/dist/engine/boot-validator/access-declarations.js +5 -0
- package/dist/engine/boot-validator/config-deps.js +3 -0
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +40 -0
- package/dist/engine/boot-validator/handler-field-references.d.ts +2 -0
- package/dist/engine/boot-validator/handler-field-references.js +25 -0
- package/dist/engine/boot-validator/index.js +6 -1
- package/dist/engine/boot-validator/nav.js +3 -1
- package/dist/engine/boot-validator/ownership.d.ts +2 -1
- package/dist/engine/boot-validator/ownership.js +32 -1
- package/dist/engine/boot-validator/pii-retention.js +8 -1
- package/dist/engine/boot-validator/query-output-columns.js +9 -9
- package/dist/engine/boot-validator/screens.js +98 -7
- package/dist/engine/boot-validator/transfer-graph.d.ts +2 -1
- package/dist/engine/boot-validator/transfer-graph.js +8 -63
- package/dist/engine/boot-validator/url-prefill-fields.d.ts +1 -0
- package/dist/engine/boot-validator/url-prefill-fields.js +35 -0
- package/dist/engine/build-app-schema.js +3 -0
- package/dist/engine/changeset-changes.d.ts +1 -0
- package/dist/engine/changeset-changes.js +49 -33
- package/dist/engine/define-feature.js +11 -0
- package/dist/engine/entity-handlers.d.ts +3 -3
- package/dist/engine/entity-handlers.js +55 -34
- package/dist/engine/escape-hatch-reason.d.ts +2 -0
- package/dist/engine/escape-hatch-reason.js +8 -0
- package/dist/engine/extension-names.d.ts +8 -9
- package/dist/engine/extension-names.js +8 -9
- package/dist/engine/extensions/tenant-data.d.ts +3 -1
- package/dist/engine/extensions/tenant-resource.d.ts +9 -1
- package/dist/engine/factories.d.ts +1 -1
- package/dist/engine/feature-changelog.d.ts +2 -0
- package/dist/engine/feature-changelog.js +6 -0
- package/dist/engine/feature-config-events-jobs.js +41 -4
- package/dist/engine/handler-field-references.d.ts +3 -0
- package/dist/engine/handler-field-references.js +31 -0
- package/dist/engine/i18n-key.js +2 -3
- package/dist/engine/index.d.ts +8 -2
- package/dist/engine/index.js +6 -1
- package/dist/engine/ownership.js +3 -4
- package/dist/engine/project-app-schema-for-roles.js +3 -1
- package/dist/engine/registry-ingest.d.ts +1 -0
- package/dist/engine/registry-ingest.js +8 -3
- package/dist/engine/run-pipeline.js +12 -1
- package/dist/engine/schema-builder.js +35 -2
- package/dist/engine/screen-helpers.d.ts +13 -0
- package/dist/engine/screen-helpers.js +31 -0
- package/dist/engine/soft-delete-cleanup.js +3 -2
- package/dist/engine/steps/_read-source.d.ts +4 -0
- package/dist/engine/steps/_read-source.js +22 -0
- package/dist/engine/steps/describe-workflow-step-error.js +3 -1
- package/dist/engine/steps/read-find-many.js +2 -1
- package/dist/engine/steps/read-find-one.js +2 -1
- package/dist/engine/steps/workflow-step-error.d.ts +5 -0
- package/dist/engine/steps/workflow-step-error.js +13 -0
- package/dist/engine/transfer-adjacency.d.ts +24 -0
- package/dist/engine/transfer-adjacency.js +107 -0
- package/dist/entrypoint/index.d.ts +2 -0
- package/dist/entrypoint/index.js +2 -0
- package/dist/env/index.js +45 -23
- package/dist/errors/classes.d.ts +8 -0
- package/dist/errors/classes.js +13 -0
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +2 -1
- package/dist/errors/reasons.d.ts +1 -0
- package/dist/errors/reasons.js +3 -0
- package/dist/errors/subject-erased.d.ts +2 -0
- package/dist/errors/subject-erased.js +9 -0
- package/dist/event-store/admin-api.js +14 -10
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +14 -9
- package/dist/event-store/events-schema.js +2 -1
- package/dist/event-store/index.d.ts +1 -1
- package/dist/event-store/index.js +1 -1
- package/dist/files/file-handle.d.ts +1 -1
- package/dist/files/file-handle.js +30 -6
- package/dist/files/file-routes.js +10 -4
- package/dist/files/resolve-content-type.d.ts +2 -0
- package/dist/files/resolve-content-type.js +8 -0
- package/dist/i18n/required-surface-keys.d.ts +2 -0
- package/dist/i18n/required-surface-keys.js +8 -0
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/job-runner.d.ts +13 -0
- package/dist/jobs/job-runner.js +128 -22
- package/dist/migrations/pii-event-backfill.js +46 -25
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/observability/standard-metrics.d.ts +4 -0
- package/dist/observability/standard-metrics.js +12 -0
- package/dist/pipeline/dispatch-shared.d.ts +2 -0
- package/dist/pipeline/dispatch-shared.js +74 -9
- package/dist/pipeline/dispatch-stream.d.ts +5 -1
- package/dist/pipeline/dispatch-stream.js +19 -12
- package/dist/pipeline/dispatcher.d.ts +3 -1
- package/dist/pipeline/dispatcher.js +10 -15
- package/dist/pipeline/event-consumer-state.js +0 -26
- package/dist/pipeline/event-dispatcher-admin.js +17 -11
- package/dist/pipeline/event-dispatcher-delivery.d.ts +1 -1
- package/dist/pipeline/event-dispatcher-delivery.js +3 -2
- package/dist/pipeline/event-dispatcher.js +48 -7
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/member-read-only-transaction.js +25 -5
- package/dist/pipeline/msp-rebuild.js +3 -3
- package/dist/pipeline/pending-gap-ranges.d.ts +1 -0
- package/dist/pipeline/pending-gap-ranges.js +22 -0
- package/dist/pipeline/system-hooks.js +4 -4
- package/dist/pipeline/system-identity-switch.d.ts +1 -1
- package/dist/pipeline/system-identity-switch.js +5 -4
- package/dist/pipeline/tenant-timezone-cache.js +5 -3
- package/dist/redis/cache-sync-topics.d.ts +1 -0
- package/dist/redis/cache-sync-topics.js +1 -0
- package/dist/redis/client.d.ts +1 -0
- package/dist/redis/client.js +7 -0
- package/dist/redis/index.d.ts +1 -1
- package/dist/redis/index.js +1 -1
- package/dist/redis/pubsub-signal.d.ts +1 -0
- package/dist/redis/pubsub-signal.js +3 -0
- package/dist/search/meilisearch-adapter.js +34 -4
- package/dist/stack/test-stack.d.ts +4 -1
- package/dist/stack/test-stack.js +9 -2
- package/dist/testing/e2e-generator.js +7 -2
- package/dist/ui-types/index.d.ts +3 -3
- package/dist/ui-types/index.js +2 -2
- package/dist/upgrade-cli.js +7 -2
- package/package.json +12 -4
- package/src/changes.json +209 -0
- package/src/scripts/codemod/migrate-cross-tenant.ts +2 -1
|
@@ -3,13 +3,15 @@ import * as z from "zod";
|
|
|
3
3
|
import { collectReferenceFields, enrichRowWithReferences, enrichWithReferences, } from "../db/eagerload.js";
|
|
4
4
|
import { createEventStoreExecutor } from "../db/event-store-executor.js";
|
|
5
5
|
import { buildEntityTable } from "../db/table-builder.js";
|
|
6
|
-
import { acknowledgeConventionCrossTenant } from "../db/tenant-db.js";
|
|
6
|
+
import { acknowledgeConventionCrossTenant, reportConventionCrossTenantUse, } from "../db/tenant-db.js";
|
|
7
7
|
import { isSystemIdentity } from "../pipeline/system-identity-switch.js";
|
|
8
8
|
import { assertUnreachable } from "../utils/index.js";
|
|
9
9
|
import { PAGED_QUERY_HANDLER_BRAND } from "./define-handler.js";
|
|
10
|
+
import { codemodPlaceholderReasonProblem } from "./escape-hatch-reason.js";
|
|
10
11
|
import { dropEmptyWriteOnlyValues } from "./field-access.js";
|
|
11
12
|
import { instructionFieldNames } from "./instruction-fields.js";
|
|
12
13
|
import { buildInsertSchema, buildUpdateSchema } from "./schema-builder.js";
|
|
14
|
+
import { parseTenantId } from "./types/identifiers.js";
|
|
13
15
|
// Convention-based handler factories for event-sourced aggregates.
|
|
14
16
|
//
|
|
15
17
|
// You register one handler per call (no auto-generation of a whole CRUD set),
|
|
@@ -46,26 +48,27 @@ import { buildInsertSchema, buildUpdateSchema } from "./schema-builder.js";
|
|
|
46
48
|
const WRITE_VERBS = ["create", "update", "delete", "restore"];
|
|
47
49
|
const QUERY_VERBS = ["list", "detail"];
|
|
48
50
|
const idSchema = z.object({ id: z.uuid() });
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const SYSTEM_ADMIN_ROLE = "SystemAdmin";
|
|
52
|
+
// A write handler with escapeHatch reads/writes rows of every tenant, so only an operator
|
|
53
|
+
// role may reach it — otherwise any tenant-level role holder could mutate foreign tenants.
|
|
54
|
+
function isSystemAdminOnly(access) {
|
|
55
|
+
return ("roles" in access &&
|
|
56
|
+
access.roles.length > 0 &&
|
|
57
|
+
access.roles.every((role) => role === SYSTEM_ADMIN_ROLE));
|
|
53
58
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
function resolveCrossTenantReason(name, options, kind) {
|
|
60
|
+
if (options.escapeHatch === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (options.escapeHatch.reason.trim().length === 0) {
|
|
63
|
+
throw new Error(`"${name}": escapeHatch requires a non-empty reason.`);
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
if (options.crossTenant === true) {
|
|
66
|
-
return `deprecated crossTenant: true on entity convention handler ${name}`;
|
|
65
|
+
const placeholderProblem = codemodPlaceholderReasonProblem(options.escapeHatch.reason);
|
|
66
|
+
if (placeholderProblem !== undefined)
|
|
67
|
+
throw new Error(`"${name}": ${placeholderProblem}`);
|
|
68
|
+
if (kind === "write" && !isSystemAdminOnly(options.access)) {
|
|
69
|
+
throw new Error(`"${name}": a write handler with escapeHatch reaches across every tenant, so its access must be SystemAdmin-only ({ roles: ["${SYSTEM_ADMIN_ROLE}"] }); got ${JSON.stringify(options.access)}.`);
|
|
67
70
|
}
|
|
68
|
-
return
|
|
71
|
+
return options.escapeHatch.reason;
|
|
69
72
|
}
|
|
70
73
|
// Upper bound on entity-list `limit`: unbounded/fractional values ran
|
|
71
74
|
// straight into the raw `LIMIT ${limit}` SQL string (event-store-executor-
|
|
@@ -194,10 +197,10 @@ export function defineEntityWriteHandler(name, entity, options) {
|
|
|
194
197
|
// behavior-preserving: ctx.db was already an unfiltered "system"-mode
|
|
195
198
|
// TenantDb for these handlers before the ctx.db cutover.
|
|
196
199
|
//
|
|
197
|
-
// crossTenantReason (escapeHatch
|
|
200
|
+
// crossTenantReason (escapeHatch) mirrors the
|
|
198
201
|
// query-handler branch below: this ONE write handler reads/writes across
|
|
199
202
|
// every tenant without making the whole feature r.systemScope().
|
|
200
|
-
const crossTenantReason = resolveCrossTenantReason(name, options);
|
|
203
|
+
const crossTenantReason = resolveCrossTenantReason(name, options, "write");
|
|
201
204
|
// create always writes into the acting user's own tenant, so its FK,
|
|
202
205
|
// unique and preSave lookups must stay tenant-filtered even when the
|
|
203
206
|
// handler declares cross-tenant access for the row-addressing verbs.
|
|
@@ -206,20 +209,31 @@ export function defineEntityWriteHandler(name, entity, options) {
|
|
|
206
209
|
return ctx.systemDb.acknowledgeCrossTenant(`entity convention handler for r.systemScope() feature (${name})`);
|
|
207
210
|
}
|
|
208
211
|
return crossTenantReason !== undefined && verb !== "create"
|
|
209
|
-
? acknowledgeConventionCrossTenant(ctx.db, crossTenantReason
|
|
212
|
+
? acknowledgeConventionCrossTenant(ctx.db, crossTenantReason, {
|
|
213
|
+
deferReport: reportsPerTouchedRow(ctx),
|
|
214
|
+
})
|
|
210
215
|
: ctx.db;
|
|
211
216
|
};
|
|
217
|
+
// actingUserFor reads the row it is about to touch anyway, so for these calls it reports the
|
|
218
|
+
// cross-tenant use per row (with that row as target) instead of dbFor reporting once up front.
|
|
219
|
+
const reportsPerTouchedRow = (ctx) => ctx.systemDb === undefined && crossTenantReason !== undefined && !entity.systemStream;
|
|
212
220
|
// The event stream is keyed by the acting user's tenantId (streamTenantFor in
|
|
213
221
|
// event-store-executor-context.ts), not by the db handed to the executor — an
|
|
214
222
|
// unfiltered "system"-mode db alone reads the foreign row but appends to an
|
|
215
223
|
// empty stream in the operator's own tenant, surfacing as a bogus
|
|
216
224
|
// version_conflict. Rewriting the acting tenant also keeps PII envelope keys
|
|
217
225
|
// derived from the row's tenant rather than the operator's.
|
|
218
|
-
const actingUserFor = async (user, id, db) => {
|
|
226
|
+
const actingUserFor = async (user, id, db, ctx) => {
|
|
219
227
|
if (crossTenantReason === undefined || entity.systemStream)
|
|
220
228
|
return user;
|
|
221
229
|
const row = await db.fetchOne(table, { id });
|
|
222
230
|
const rowTenantId = row?.["tenantId"];
|
|
231
|
+
if (reportsPerTouchedRow(ctx)) {
|
|
232
|
+
const targetTenantId = parseTenantId(rowTenantId);
|
|
233
|
+
reportConventionCrossTenantUse(db, targetTenantId !== null && typeof id === "string"
|
|
234
|
+
? { id, tenantId: targetTenantId }
|
|
235
|
+
: undefined);
|
|
236
|
+
}
|
|
223
237
|
if (typeof rowTenantId !== "string" || rowTenantId === user.tenantId)
|
|
224
238
|
return user;
|
|
225
239
|
return { ...user, tenantId: rowTenantId };
|
|
@@ -258,7 +272,7 @@ export function defineEntityWriteHandler(name, entity, options) {
|
|
|
258
272
|
...submitted,
|
|
259
273
|
changes: dropEmptyWriteOnlyValues(entity, submitted.changes),
|
|
260
274
|
};
|
|
261
|
-
const user = await actingUserFor(event.user, payload.id, db);
|
|
275
|
+
const user = await actingUserFor(event.user, payload.id, db, ctx);
|
|
262
276
|
// skipUnchanged (#464): API-driven updates diff against the stored
|
|
263
277
|
// row so a resubmitted-but-identical field doesn't force a fresh
|
|
264
278
|
// pii/encrypted ciphertext. Direct executor.update() callers (e.g.
|
|
@@ -277,7 +291,7 @@ export function defineEntityWriteHandler(name, entity, options) {
|
|
|
277
291
|
handler = async (event, ctx) => {
|
|
278
292
|
const db = dbFor(ctx);
|
|
279
293
|
const payload = event.payload; // @cast-boundary engine-payload
|
|
280
|
-
const user = await actingUserFor(event.user, payload.id, db);
|
|
294
|
+
const user = await actingUserFor(event.user, payload.id, db, ctx);
|
|
281
295
|
return executor.delete(payload, user, db);
|
|
282
296
|
};
|
|
283
297
|
break;
|
|
@@ -286,15 +300,13 @@ export function defineEntityWriteHandler(name, entity, options) {
|
|
|
286
300
|
handler = async (event, ctx) => {
|
|
287
301
|
const db = dbFor(ctx);
|
|
288
302
|
const payload = event.payload; // @cast-boundary engine-payload
|
|
289
|
-
const user = await actingUserFor(event.user, payload.id, db);
|
|
303
|
+
const user = await actingUserFor(event.user, payload.id, db, ctx);
|
|
290
304
|
return executor.restore(payload, user, db);
|
|
291
305
|
};
|
|
292
306
|
break;
|
|
293
307
|
default:
|
|
294
308
|
assertUnreachable(verb, "write verb");
|
|
295
309
|
}
|
|
296
|
-
if (options.crossTenant === true)
|
|
297
|
-
deprecatedCrossTenantHandlers.add(handler);
|
|
298
310
|
// escapeHatch stays off the def: WriteHandlerDef.escapeHatch also grants unsafeRaw, global writes, identity switches.
|
|
299
311
|
return {
|
|
300
312
|
name,
|
|
@@ -338,13 +350,13 @@ export function defineEntityQueryHandler(name, entity, options) {
|
|
|
338
350
|
// useReferenceLookup stays as a fallback (for apps that write custom
|
|
339
351
|
// handlers by hand without this wrapper).
|
|
340
352
|
const hasRefFields = collectReferenceFields(entity).length > 0;
|
|
341
|
-
const crossTenantReason = resolveCrossTenantReason(name, options);
|
|
353
|
+
const crossTenantReason = resolveCrossTenantReason(name, options, "query");
|
|
342
354
|
// Preference order:
|
|
343
355
|
// 1. ctx.systemDb — the feature declared r.systemScope() (whole-feature
|
|
344
356
|
// cutover, dispatch-shared.ts), so ctx.db is fail-closed. This is
|
|
345
357
|
// behavior-preserving: ctx.db was already an unfiltered "system"-mode
|
|
346
358
|
// TenantDb for these handlers before that cutover.
|
|
347
|
-
// 2. crossTenantReason (escapeHatch
|
|
359
|
+
// 2. crossTenantReason (escapeHatch) — this
|
|
348
360
|
// ONE handler reads across every tenant (e.g. a SystemAdmin-only
|
|
349
361
|
// operator inspector) without making the whole feature r.systemScope()
|
|
350
362
|
// — that would drop tenant isolation from every OTHER handler the
|
|
@@ -389,8 +401,12 @@ export function defineEntityQueryHandler(name, entity, options) {
|
|
|
389
401
|
},
|
|
390
402
|
parentVisibility: { entities: ctx.registry.getAllEntities() },
|
|
391
403
|
});
|
|
404
|
+
const eagerLoadViewer = {
|
|
405
|
+
user: query.user,
|
|
406
|
+
parentVisibility: { entities: ctx.registry.getAllEntities() },
|
|
407
|
+
};
|
|
392
408
|
const enrichedRows = hasRefFields
|
|
393
|
-
? await enrichWithReferences(result.rows, entity, (name) => ctx.registry.getEntity(name), db)
|
|
409
|
+
? await enrichWithReferences(result.rows, entity, (name) => ctx.registry.getEntity(name), db, eagerLoadViewer)
|
|
394
410
|
: result.rows;
|
|
395
411
|
return { ...result, rows: augmentDerivedFields(enrichedRows, entity) };
|
|
396
412
|
};
|
|
@@ -404,14 +420,15 @@ export function defineEntityQueryHandler(name, entity, options) {
|
|
|
404
420
|
}); // @cast-boundary engine-payload
|
|
405
421
|
if (row === null || !hasRefFields)
|
|
406
422
|
return row;
|
|
407
|
-
return enrichRowWithReferences(row, entity, (name) => ctx.registry.getEntity(name), db
|
|
423
|
+
return enrichRowWithReferences(row, entity, (name) => ctx.registry.getEntity(name), db, {
|
|
424
|
+
user: query.user,
|
|
425
|
+
parentVisibility: { entities: ctx.registry.getAllEntities() },
|
|
426
|
+
});
|
|
408
427
|
};
|
|
409
428
|
break;
|
|
410
429
|
default:
|
|
411
430
|
assertUnreachable(verb, "query verb");
|
|
412
431
|
}
|
|
413
|
-
if (options.crossTenant === true)
|
|
414
|
-
deprecatedCrossTenantHandlers.add(handler);
|
|
415
432
|
// escapeHatch stays off the def: QueryHandlerDef.escapeHatch also grants unsafeRaw and identity switches.
|
|
416
433
|
return {
|
|
417
434
|
name,
|
|
@@ -500,6 +517,10 @@ export function createEntityExecutor(entityName, entity) {
|
|
|
500
517
|
// For anything more involved (filters, joins, custom shaping), write the
|
|
501
518
|
// query-handler explicitly with ctx.queryProjection or a raw select.
|
|
502
519
|
export function defineProjectionQueryHandler(name, projectionQualifiedName, options) {
|
|
520
|
+
const escapeHatch = options.escapeHatch ??
|
|
521
|
+
(typeof options.unsafeAllTenants === "object"
|
|
522
|
+
? { reason: options.unsafeAllTenants.reason }
|
|
523
|
+
: undefined);
|
|
503
524
|
return {
|
|
504
525
|
name,
|
|
505
526
|
schema: z.object({}),
|
|
@@ -509,7 +530,7 @@ export function defineProjectionQueryHandler(name, projectionQualifiedName, opti
|
|
|
509
530
|
// pagination envelopes or added metadata.
|
|
510
531
|
handler: async (_query, ctx) => ctx.queryProjection(projectionQualifiedName, options.unsafeAllTenants ? { unsafeAllTenants: true } : undefined), // @wrapper-known semantic-alias
|
|
511
532
|
access: options.access,
|
|
512
|
-
...(
|
|
533
|
+
...(escapeHatch && { escapeHatch }),
|
|
513
534
|
};
|
|
514
535
|
}
|
|
515
536
|
function defaultCrudVerbs(entity) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Shared by the migrate-cross-tenant codemod (which writes it) and the escapeHatch reason
|
|
2
|
+
// validation (which rejects it), so an unedited codemod reason can never reach a running app.
|
|
3
|
+
export const CODEMOD_PLACEHOLDER_REASON_MARKER = "state the operator use case here";
|
|
4
|
+
export function codemodPlaceholderReasonProblem(reason) {
|
|
5
|
+
if (!reason.includes(CODEMOD_PLACEHOLDER_REASON_MARKER))
|
|
6
|
+
return undefined;
|
|
7
|
+
return `the reason still contains the migrate-cross-tenant codemod placeholder ("${CODEMOD_PLACEHOLDER_REASON_MARKER}") — replace the codemod placeholder with the real operator use case`;
|
|
8
|
+
}
|
|
@@ -23,19 +23,18 @@ export declare const EXT_USER_DATA_ORDER: {
|
|
|
23
23
|
readonly DEFAULT: 0;
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
|
-
* `tenantData` —
|
|
26
|
+
* `tenantData` — per-entity tenant-destroy hooks (GDPR + end of data processing agreement).
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
* - `destroy(tenantId, ctx) => Promise<void>`
|
|
28
|
+
* Expected hook method: `destroy(ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* Registered by: `tenant-lifecycle`.
|
|
31
|
+
* Consumed by: every feature with a tenantId field.
|
|
33
32
|
*/
|
|
34
33
|
export declare const EXT_TENANT_DATA: "tenantData";
|
|
35
34
|
/**
|
|
36
35
|
* `storageProvider` — file-storage-plugin tenant-destroy hook.
|
|
37
36
|
*
|
|
38
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
37
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
39
38
|
*
|
|
40
39
|
* Registered by: `files-tenant-data`.
|
|
41
40
|
* Consumed by: pluggable providers (Local, MinIO, S3, R2).
|
|
@@ -113,7 +112,7 @@ export declare const EXT_DERIVATIVE_OVERLAY_RESOLVER: "derivativeOverlayResolver
|
|
|
113
112
|
* `searchAdapter` — search-index tenant-destroy hook (Meilisearch index
|
|
114
113
|
* cleanup on tenant-destroy).
|
|
115
114
|
*
|
|
116
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
115
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
117
116
|
*
|
|
118
117
|
* Consumed by: Meilisearch and other search-adapter implementations.
|
|
119
118
|
*/
|
|
@@ -122,14 +121,14 @@ export declare const EXT_SEARCH_ADAPTER: "searchAdapter";
|
|
|
122
121
|
* `externalResource` — external-service tenant-destroy hook (webhook
|
|
123
122
|
* subscriptions, Brevo recipient lists, provider customer accounts).
|
|
124
123
|
*
|
|
125
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
124
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
126
125
|
*/
|
|
127
126
|
export declare const EXT_EXTERNAL_RESOURCE: "externalResource";
|
|
128
127
|
/**
|
|
129
128
|
* `infraResource` — Pulumi-managed per-tenant resource tenant-destroy hook
|
|
130
129
|
* (custom domain, cert-manager issuer, dedicated pod/volume).
|
|
131
130
|
*
|
|
132
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
131
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
133
132
|
*/
|
|
134
133
|
export declare const EXT_INFRA_RESOURCE: "infraResource";
|
|
135
134
|
/**
|
|
@@ -42,19 +42,18 @@ export const EXT_USER_DATA_ORDER = {
|
|
|
42
42
|
DEFAULT: 0,
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
|
-
* `tenantData` —
|
|
45
|
+
* `tenantData` — per-entity tenant-destroy hooks (GDPR + end of data processing agreement).
|
|
46
46
|
*
|
|
47
|
-
*
|
|
48
|
-
* - `destroy(tenantId, ctx) => Promise<void>`
|
|
47
|
+
* Expected hook method: `destroy(ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
49
48
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Registered by: `tenant-lifecycle`.
|
|
50
|
+
* Consumed by: every feature with a tenantId field.
|
|
52
51
|
*/
|
|
53
52
|
export const EXT_TENANT_DATA = "tenantData";
|
|
54
53
|
/**
|
|
55
54
|
* `storageProvider` — file-storage-plugin tenant-destroy hook.
|
|
56
55
|
*
|
|
57
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
56
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
58
57
|
*
|
|
59
58
|
* Registered by: `files-tenant-data`.
|
|
60
59
|
* Consumed by: pluggable providers (Local, MinIO, S3, R2).
|
|
@@ -142,7 +141,7 @@ export const EXT_DERIVATIVE_OVERLAY_RESOLVER = "derivativeOverlayResolver";
|
|
|
142
141
|
* `searchAdapter` — search-index tenant-destroy hook (Meilisearch index
|
|
143
142
|
* cleanup on tenant-destroy).
|
|
144
143
|
*
|
|
145
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
144
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
146
145
|
*
|
|
147
146
|
* Consumed by: Meilisearch and other search-adapter implementations.
|
|
148
147
|
*/
|
|
@@ -151,14 +150,14 @@ export const EXT_SEARCH_ADAPTER = "searchAdapter";
|
|
|
151
150
|
* `externalResource` — external-service tenant-destroy hook (webhook
|
|
152
151
|
* subscriptions, Brevo recipient lists, provider customer accounts).
|
|
153
152
|
*
|
|
154
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
153
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
155
154
|
*/
|
|
156
155
|
export const EXT_EXTERNAL_RESOURCE = "externalResource";
|
|
157
156
|
/**
|
|
158
157
|
* `infraResource` — Pulumi-managed per-tenant resource tenant-destroy hook
|
|
159
158
|
* (custom domain, cert-manager issuer, dedicated pod/volume).
|
|
160
159
|
*
|
|
161
|
-
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<void>`.
|
|
160
|
+
* Expected hook method: `destroyTenant(tenantId, ctx) => Promise<TenantDestroyHookResult | void>`.
|
|
162
161
|
*/
|
|
163
162
|
export const EXT_INFRA_RESOURCE = "infraResource";
|
|
164
163
|
/**
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-types/file-provider-resolver-types";
|
|
2
2
|
import type { TenantDb } from "../../db/tenant-db.js";
|
|
3
3
|
import type { Registry, TenantId } from "../types/index.js";
|
|
4
|
+
import type { TenantDestroyHookResult } from "./tenant-resource.js";
|
|
4
5
|
export interface TenantDataHookCtx {
|
|
5
6
|
readonly db: TenantDb;
|
|
6
7
|
readonly registry: Registry;
|
|
7
8
|
readonly tenantId: TenantId;
|
|
9
|
+
readonly deadlineAt?: number;
|
|
8
10
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
9
11
|
readonly log?: (message: string) => void;
|
|
10
12
|
}
|
|
11
|
-
export type TenantDataDestroyHook = (ctx: TenantDataHookCtx) => Promise<void>;
|
|
13
|
+
export type TenantDataDestroyHook = (ctx: TenantDataHookCtx) => Promise<TenantDestroyHookResult | void>;
|
|
12
14
|
export interface TenantDataExtensionHooks {
|
|
13
15
|
readonly destroy: TenantDataDestroyHook;
|
|
14
16
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import type { FileProviderResolver } from "@cosmicdrift/kumiko-types/file-provider-resolver-types";
|
|
2
2
|
import type { DbRunner } from "../../db/connection.js";
|
|
3
3
|
import type { TenantId } from "../types/index.js";
|
|
4
|
+
export type TenantDestroyHookResult = {
|
|
5
|
+
readonly done: true;
|
|
6
|
+
readonly processed?: number;
|
|
7
|
+
} | {
|
|
8
|
+
readonly done: false;
|
|
9
|
+
readonly processed: number;
|
|
10
|
+
};
|
|
4
11
|
export interface TenantResourceHookCtx {
|
|
5
12
|
readonly tenantId: TenantId;
|
|
13
|
+
readonly deadlineAt?: number;
|
|
6
14
|
readonly db: DbRunner;
|
|
7
15
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
8
16
|
readonly log?: (message: string) => void;
|
|
9
17
|
}
|
|
10
|
-
export type TenantResourceDestroyHook = (tenantId: TenantId, ctx: TenantResourceHookCtx) => Promise<void>;
|
|
18
|
+
export type TenantResourceDestroyHook = (tenantId: TenantId, ctx: TenantResourceHookCtx) => Promise<TenantDestroyHookResult | void>;
|
|
11
19
|
export interface TenantResourceExtensionHooks {
|
|
12
20
|
readonly destroyTenant: TenantResourceDestroyHook;
|
|
13
21
|
}
|
|
@@ -35,7 +35,7 @@ export declare function createBooleanField<R extends true | false = false>(overr
|
|
|
35
35
|
};
|
|
36
36
|
export declare function createSelectField<const TOptions extends readonly string[], R extends true | false = false>(opts: {
|
|
37
37
|
options: TOptions;
|
|
38
|
-
} & Partial<Omit<SelectFieldDef<TOptions
|
|
38
|
+
} & Partial<Omit<SelectFieldDef<NoInfer<TOptions>>, "type" | "options" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & {
|
|
39
39
|
required?: R;
|
|
40
40
|
}): SelectFieldDef<TOptions> & {
|
|
41
41
|
required: R;
|
|
@@ -8,6 +8,8 @@ export type ChangelogEntry = {
|
|
|
8
8
|
readonly migration?: string;
|
|
9
9
|
/** Path (relative to packages/framework/src/, under scripts/codemod/) run by `kumiko upgrade --apply`. */
|
|
10
10
|
readonly codemod?: string;
|
|
11
|
+
/** The codemod only covers part of the migration: the entry is also recorded as pending manual work after it ran. */
|
|
12
|
+
readonly manualAfterCodemod?: boolean;
|
|
11
13
|
};
|
|
12
14
|
export type FeatureChangelog = {
|
|
13
15
|
readonly feature: string;
|
|
@@ -33,6 +33,9 @@ function isChangelogEntry(value) {
|
|
|
33
33
|
return false;
|
|
34
34
|
if (typeof obj["title"] !== "string")
|
|
35
35
|
return false;
|
|
36
|
+
if (obj["manualAfterCodemod"] !== undefined && typeof obj["manualAfterCodemod"] !== "boolean") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
36
39
|
return true;
|
|
37
40
|
}
|
|
38
41
|
export function validateChangelog(entry) {
|
|
@@ -43,6 +46,9 @@ export function validateChangelog(entry) {
|
|
|
43
46
|
if (entry.type === "breaking" && entry.migration?.trim() === "") {
|
|
44
47
|
errors.push(`breaking change "${entry.title}" has empty migration field`);
|
|
45
48
|
}
|
|
49
|
+
if (entry.manualAfterCodemod && !entry.codemod) {
|
|
50
|
+
errors.push(`change "${entry.title}" sets manualAfterCodemod without a codemod field`);
|
|
51
|
+
}
|
|
46
52
|
return errors;
|
|
47
53
|
}
|
|
48
54
|
export function compareVersions(a, b) {
|
|
@@ -13,6 +13,39 @@ function isAbsentable(field) {
|
|
|
13
13
|
return parsed.success && (parsed.data === null || parsed.data === undefined);
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
+
const NON_STRING_SCHEMA_TYPES = new Set([
|
|
17
|
+
"number",
|
|
18
|
+
"int",
|
|
19
|
+
"boolean",
|
|
20
|
+
"bigint",
|
|
21
|
+
"date",
|
|
22
|
+
"object",
|
|
23
|
+
"array",
|
|
24
|
+
]);
|
|
25
|
+
// An owner id is resolved through "non-empty string or absent" at append time,
|
|
26
|
+
// so a schema that can never yield a string would make every append throw in
|
|
27
|
+
// prod. Wrappers (optional/nullable/default/...) are looked through.
|
|
28
|
+
function neverYieldsString(field) {
|
|
29
|
+
let current = field;
|
|
30
|
+
for (;;) {
|
|
31
|
+
const def = current._zod.def;
|
|
32
|
+
if (def.innerType === undefined)
|
|
33
|
+
return NON_STRING_SCHEMA_TYPES.has(def.type);
|
|
34
|
+
current = def.innerType;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function assertOwnerFieldYieldsPresentString(args) {
|
|
38
|
+
const { featureName, eventName, field, ownerField, owner } = args;
|
|
39
|
+
if (neverYieldsString(owner)) {
|
|
40
|
+
throw new Error(`[Feature ${featureName}] defineEvent("${eventName}"): piiFields."${field}" is owned by "${ownerField}", whose payload schema is not a string — the subject id must be a non-empty string, so every append would fail. ` +
|
|
41
|
+
`Use a string owner field, or declare whenAbsent ("tenant" | "plaintext").`);
|
|
42
|
+
}
|
|
43
|
+
if (isAbsentable(owner)) {
|
|
44
|
+
throw new Error(`[Feature ${featureName}] defineEvent("${eventName}"): piiFields."${field}" is owned by "${ownerField}", which the payload schema allows to be null/undefined. ` +
|
|
45
|
+
`Declare what happens then: { personal: { of: "${ownerField}", whenAbsent: "tenant" } } encrypts under the envelope tenant key, ` +
|
|
46
|
+
`whenAbsent: "plaintext" acknowledges that the value ships unencrypted and cannot be crypto-shredded (fw#2776).`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
16
49
|
// Builds config/secrets/claims/events/jobs/notifications registrar methods.
|
|
17
50
|
export function buildConfigEventsJobsMethods(state, name) {
|
|
18
51
|
function config(arg1, arg2) {
|
|
@@ -82,10 +115,14 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
82
115
|
}
|
|
83
116
|
}
|
|
84
117
|
const owner = shape?.[normalized.ownerField];
|
|
85
|
-
if (normalized.whenAbsent === undefined && owner !== undefined
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
118
|
+
if (normalized.whenAbsent === undefined && owner !== undefined) {
|
|
119
|
+
assertOwnerFieldYieldsPresentString({
|
|
120
|
+
featureName: name,
|
|
121
|
+
eventName,
|
|
122
|
+
field,
|
|
123
|
+
ownerField: normalized.ownerField,
|
|
124
|
+
owner,
|
|
125
|
+
});
|
|
89
126
|
}
|
|
90
127
|
}
|
|
91
128
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ZodObject } from "zod";
|
|
2
|
+
import { zodDef, zodMeta, zodShapeField } from "../env/_zod-introspect.js";
|
|
3
|
+
export const HANDLER_FIELD_REFERENCES_META_KEY = "references";
|
|
4
|
+
const MAX_WRAPPER_DEPTH = 8;
|
|
5
|
+
function referencesFromMeta(field) {
|
|
6
|
+
const meta = zodMeta(field);
|
|
7
|
+
if (typeof meta !== "object" || meta === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
const value = Reflect.get(meta, HANDLER_FIELD_REFERENCES_META_KEY);
|
|
10
|
+
return typeof value === "string" ? value : undefined;
|
|
11
|
+
}
|
|
12
|
+
// `.optional()` / `.nullable()` / `.default()` after `.meta()` hide the meta
|
|
13
|
+
// from the outer type, so every wrapper layer is probed on the way in.
|
|
14
|
+
export function handlerFieldReference(schema, field) {
|
|
15
|
+
let current = schema;
|
|
16
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH && current !== undefined; depth++) {
|
|
17
|
+
if (current instanceof ZodObject)
|
|
18
|
+
break;
|
|
19
|
+
current = zodDef(current)?.innerType;
|
|
20
|
+
}
|
|
21
|
+
if (!(current instanceof ZodObject))
|
|
22
|
+
return undefined;
|
|
23
|
+
let fieldSchema = zodShapeField(current, field);
|
|
24
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH && fieldSchema !== undefined; depth++) {
|
|
25
|
+
const reference = referencesFromMeta(fieldSchema);
|
|
26
|
+
if (reference !== undefined)
|
|
27
|
+
return reference;
|
|
28
|
+
fieldSchema = zodDef(fieldSchema)?.innerType;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
package/dist/engine/i18n-key.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// Dot-form labels are indistinguishable from literal display text ("actions.open"),
|
|
2
2
|
// so authors mark the i18n ones explicitly; the registry is what boot validation reads.
|
|
3
|
-
//
|
|
4
|
-
// globalThis so a second installed copy
|
|
5
|
-
// other copy wrote; otherwise they are silently ignored and #2313 reopens.
|
|
3
|
+
// A mark applies process-wide, by string value, across all features and framework copies.
|
|
4
|
+
// The registry sits on globalThis so a second installed copy reads the other's marks.
|
|
6
5
|
const REGISTRY_KEY = Symbol.for("kumiko.i18n.explicitDotFormKeys");
|
|
7
6
|
function explicitDotFormKeys() {
|
|
8
7
|
const existing = Reflect.get(globalThis, REGISTRY_KEY);
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { handlerFieldLabelKey, handlerTitleKey } from "../i18n/required-surface-keys.js";
|
|
1
2
|
export { hasAccess } from "./access.js";
|
|
2
3
|
export { isPrincipalStatusPlugin, isTenantLifecycleStatusPlugin, type PrincipalProfile, type PrincipalStatus, type PrincipalStatusPlugin, TENANT_TEARDOWN_STATUSES, type TenantLifecycleStatusPlugin, } from "./active-membership.js";
|
|
3
4
|
export { PII_DIRECT_NAME_HINTS, PII_USER_OWNED_NAME_HINTS, PII_USER_REFERENCE_NAME_HINTS, } from "./boot-validator/entity-handler.js";
|
|
@@ -34,7 +35,7 @@ export { EXTENSION_SELECTOR_HINT_KEY, extensionSelectorTargets, SELECTED_EXTENSI
|
|
|
34
35
|
export { extensionUsageEscapeHatchReason } from "./extensions/escape-hatch-usage.js";
|
|
35
36
|
export type { StorageProviderDestroyTenantHook, StorageProviderExtensionHooks, StorageProviderHookCtx, } from "./extensions/storage-provider.js";
|
|
36
37
|
export { isTenantDataExtensionHooks, type TenantDataDestroyHook, type TenantDataExtensionHooks, type TenantDataHookCtx, } from "./extensions/tenant-data.js";
|
|
37
|
-
export { isTenantResourceExtensionHooks, type TenantResourceDestroyHook, type TenantResourceExtensionHooks, type TenantResourceHookCtx, } from "./extensions/tenant-resource.js";
|
|
38
|
+
export { isTenantResourceExtensionHooks, type TenantDestroyHookResult, type TenantResourceDestroyHook, type TenantResourceExtensionHooks, type TenantResourceHookCtx, } from "./extensions/tenant-resource.js";
|
|
38
39
|
export type { TenantUserModel, UserDataDeleteHook, UserDataDeleteStrategy, UserDataExportHook, UserDataExportSnippet, UserDataExtensionHooks, UserDataExtensionOptions, UserDataHookCtx, UserDataStorageProvider, } from "./extensions/user-data.js";
|
|
39
40
|
export { createBigIntField, createBooleanField, createDateField, createDecimalField, createDerivedField, createEmbeddedField, createEmbeddedListField, createEntity, createFileField, createFilesField, createImageField, createImagesField, createJsonbField, createLocatedTimestampField, createLongTextField, createMoneyField, createMultiSelectField, createNumberField, createSelectField, createTextField, createTimestampField, createTzField, } from "./factories.js";
|
|
40
41
|
export type { AddEntityArgs, AddHookArgs, AddRelationArgs, AddWriteHandlerArgs, FeaturePatcher, FeaturePattern, FeaturePatternKind, FormFieldLabel, FormFieldSpec, FormInputType, HandlerHeaderUpdate, ParseError, ParseResult, PatternCategory, PatternChange, PatternChangeIssue, PatternChangesParseResult, PatternFormSchema, PatternId, RenderFeatureFileInput, SourceLocation, } from "./feature-ast/index.js";
|
|
@@ -42,6 +43,7 @@ export { addPattern, applyChanges, createFeaturePatcher, getPatternSchema, group
|
|
|
42
43
|
export { type ChangelogEntry, type ChangelogType, compareVersions, type FeatureChangelog, filterEntriesAfter, parseFeatureChangelog, sortEntries, validateChangelog, } from "./feature-changelog.js";
|
|
43
44
|
export { type BuildManifestOptions, buildManifestFromRegistry, type FeatureManifest, type ManifestConfigKey, type ManifestExtension, type ManifestFeature, type ManifestSecret, type ManifestSystemEvent, serializeManifest, } from "./feature-manifest.js";
|
|
44
45
|
export { checkWriteFieldOwnership, checkWriteFieldRoles, filterReadFields, maskWriteOnlyFields, } from "./field-access.js";
|
|
46
|
+
export { HANDLER_FIELD_REFERENCES_META_KEY, handlerFieldReference, } from "./handler-field-references.js";
|
|
45
47
|
export { resolveName, withResponseData } from "./handler-helpers.js";
|
|
46
48
|
export { i18nKey } from "./i18n-key.js";
|
|
47
49
|
export { buildSessionRoles, findForbiddenMembershipRole, isForbiddenMembershipRole, stripForbiddenMembershipRoles, } from "./membership-roles.js";
|
|
@@ -56,6 +58,7 @@ export type { BuiltinQnType, ParsedQn, QnType } from "./qualified-name.js";
|
|
|
56
58
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.js";
|
|
57
59
|
export { readClaim } from "./read-claim.js";
|
|
58
60
|
export { createRegistry } from "./registry.js";
|
|
61
|
+
export { qualifyTranslationKey } from "./registry-ingest.js";
|
|
59
62
|
export type { ClampInfo, ResolveOptions } from "./resolve-config-or-param.js";
|
|
60
63
|
export { resolveConfigOrParam } from "./resolve-config-or-param.js";
|
|
61
64
|
export type { AssignableAppRoles, AssignableFromRole } from "./role-assignment.js";
|
|
@@ -64,15 +67,18 @@ export { runsInLane } from "./run-in.js";
|
|
|
64
67
|
export type { StepListOutcome } from "./run-pipeline.js";
|
|
65
68
|
export { runPipeline, runStepList } from "./run-pipeline.js";
|
|
66
69
|
export { buildInsertSchema, buildUpdateSchema, fieldToZod, SYSTEM_ONLY_JSON_SCHEMA_KEY, } from "./schema-builder.js";
|
|
67
|
-
export {
|
|
70
|
+
export type { UnavailableSelectOption } from "./screen-helpers.js";
|
|
71
|
+
export { availableSelectOptions, findUnavailableSelectOptions, isExtensionEditSection, isFieldsEditSection, isWriteFormEditSection, normalizeEditField, normalizeListColumn, sectionFieldSpecs, } from "./screen-helpers.js";
|
|
68
72
|
export type { TransitionGraph } from "./state-machine.js";
|
|
69
73
|
export { defineTransitions, guardTransition } from "./state-machine.js";
|
|
70
74
|
export { evaluateEventMatch } from "./steps/_event-match.js";
|
|
71
75
|
export { STEP_DISPATCH_AGGREGATE_TYPE, STEP_DISPATCH_FAILED_TYPE, STEP_DISPATCH_REQUESTED_TYPE, STEP_DISPATCHED_TYPE, SUSPEND_SENTINEL, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RESUMED_TYPE, WORKFLOW_RETRY_SCHEDULED_TYPE, WORKFLOW_RUN_COMPLETED_TYPE, WORKFLOW_RUN_FAILED_TYPE, WORKFLOW_RUN_STARTED_TYPE, WORKFLOW_WAITING_FOR_EVENT_TYPE, WORKFLOW_WAITING_TYPE, } from "./steps/_step-dispatch-constants.js";
|
|
72
76
|
export { describeWorkflowStepError } from "./steps/describe-workflow-step-error.js";
|
|
77
|
+
export { unwrapWorkflowStepError, WorkflowStepError } from "./steps/workflow-step-error.js";
|
|
73
78
|
export { WORKFLOW_SYSTEM_EVENT_DEFS } from "./steps/workflow-system-events.js";
|
|
74
79
|
export { ANONYMOUS_ROLE, ANONYMOUS_USER_ID, createAnonymousUser, createSystemUser, SYSTEM_ROLE, SYSTEM_USER_ID, } from "./system-user.js";
|
|
75
80
|
export { type EffectiveFeaturesResolver, findTierResolverUsage, isTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, type TierResolverPlugin, type TrialGate, } from "./tier-resolver-extension.js";
|
|
81
|
+
export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTransferAdjacency, type TransferAdjacency, type TransferEdge, type TransferEdgeLink, } from "./transfer-adjacency.js";
|
|
76
82
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
77
83
|
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, ConcurrencyMode, ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigCascade, ConfigCascadeLevel, ConfigDefinition, ConfigEditScreenDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigScope, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, ContentCollectionDefinition, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, CustomScreenDefinition, CustomScreenRoute, DashboardChartPanel, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextParam, DashboardValueFormat, DateFieldDef, DecimalFieldDef, DeleteContext, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EntityDefinition, EntityEditScreenDefinition, EntityId, EntityListExpandableRow, EntityListScreenDefinition, EntityRef, EntityRelations, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, ExtensionOptionsArgs, ExtensionOptionsFor, FeatureDefinition, FeatureRegistrar, FieldAccess, FieldCondition, FieldDefinition, FieldFormatRegistry, FieldIconKey, FieldRenderer, FileFieldDef, FilesFieldDef, Findability, FormatSpec, FormWidth, HandlerContext, HasManyRelation, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MemberReader, MetricNavigate, MetricSpec, MspErrorMode, MspErrorPolicy, MultiSelectFieldDef, MultiStreamApplyFn, MultiStreamProjectionDefinition, NameOrRef, NavDefinition, NavIconKey, NotificationDataFn, NotificationDefinition, NotificationRecipientFn, NotificationTemplateFn, NotifyDelivery, NotifyDeliveryStatus, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, ProjectionTable, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitPer, RecordHeaderSpec, RecordHeaderSubtitlePart, ReferenceDataDef, Registry, RelationDefinition, ResolvedPiiFlags, RetentionDef, RoleAccessPersonalData, RoleAccessRule, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, SaveContext, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretKeyDefinition, SecretKeyHandle, SecretMintConfirmStep, SecretMintScreenDefinition, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, SelectFieldDef, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, Subscribe, TargetRef, TenantId, TextFieldDef, ToolbarAction, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, UnitKey, UnsafeAppendEventFn, ValidationError, ValidationHookFn, WorkspaceDefinition, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "./types/index.js";
|
|
78
84
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
package/dist/engine/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Public API
|
|
2
|
+
export { handlerFieldLabelKey, handlerTitleKey } from "../i18n/required-surface-keys.js";
|
|
2
3
|
export { hasAccess } from "./access.js";
|
|
3
4
|
export { isPrincipalStatusPlugin, isTenantLifecycleStatusPlugin, TENANT_TEARDOWN_STATUSES, } from "./active-membership.js";
|
|
4
5
|
export { PII_DIRECT_NAME_HINTS, PII_USER_OWNED_NAME_HINTS, PII_USER_REFERENCE_NAME_HINTS, } from "./boot-validator/entity-handler.js";
|
|
@@ -37,6 +38,7 @@ export { addPattern, applyChanges, createFeaturePatcher, getPatternSchema, group
|
|
|
37
38
|
export { compareVersions, filterEntriesAfter, parseFeatureChangelog, sortEntries, validateChangelog, } from "./feature-changelog.js";
|
|
38
39
|
export { buildManifestFromRegistry, serializeManifest, } from "./feature-manifest.js";
|
|
39
40
|
export { checkWriteFieldOwnership, checkWriteFieldRoles, filterReadFields, maskWriteOnlyFields, } from "./field-access.js";
|
|
41
|
+
export { HANDLER_FIELD_REFERENCES_META_KEY, handlerFieldReference, } from "./handler-field-references.js";
|
|
40
42
|
export { resolveName, withResponseData } from "./handler-helpers.js";
|
|
41
43
|
export { i18nKey } from "./i18n-key.js";
|
|
42
44
|
// findForbiddenMembershipRole/isForbiddenMembershipRole/
|
|
@@ -54,19 +56,22 @@ export { defineApply, defineMspApply, setFields } from "./projection-helpers.js"
|
|
|
54
56
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.js";
|
|
55
57
|
export { readClaim } from "./read-claim.js";
|
|
56
58
|
export { createRegistry } from "./registry.js";
|
|
59
|
+
export { qualifyTranslationKey } from "./registry-ingest.js";
|
|
57
60
|
export { resolveConfigOrParam } from "./resolve-config-or-param.js";
|
|
58
61
|
export { assignableAppRolesFromUsages, assignableAppRolesOf, BUILT_IN_MEMBERSHIP_ROLES, canActorAssignRole, findForbiddenRoleAssignment, isAssignableByRole, mergeAssignedRoles, } from "./role-assignment.js";
|
|
59
62
|
export { runsInLane } from "./run-in.js";
|
|
60
63
|
export { runPipeline, runStepList } from "./run-pipeline.js";
|
|
61
64
|
export { buildInsertSchema, buildUpdateSchema, fieldToZod, SYSTEM_ONLY_JSON_SCHEMA_KEY, } from "./schema-builder.js";
|
|
62
|
-
export { isExtensionEditSection, isFieldsEditSection, isWriteFormEditSection, normalizeEditField, normalizeListColumn, sectionFieldSpecs, } from "./screen-helpers.js";
|
|
65
|
+
export { availableSelectOptions, findUnavailableSelectOptions, isExtensionEditSection, isFieldsEditSection, isWriteFormEditSection, normalizeEditField, normalizeListColumn, sectionFieldSpecs, } from "./screen-helpers.js";
|
|
63
66
|
export { defineTransitions, guardTransition } from "./state-machine.js";
|
|
64
67
|
export { evaluateEventMatch } from "./steps/_event-match.js";
|
|
65
68
|
export { STEP_DISPATCH_AGGREGATE_TYPE, STEP_DISPATCH_FAILED_TYPE, STEP_DISPATCH_REQUESTED_TYPE, STEP_DISPATCHED_TYPE, SUSPEND_SENTINEL, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RESUMED_TYPE, WORKFLOW_RETRY_SCHEDULED_TYPE, WORKFLOW_RUN_COMPLETED_TYPE, WORKFLOW_RUN_FAILED_TYPE, WORKFLOW_RUN_STARTED_TYPE, WORKFLOW_WAITING_FOR_EVENT_TYPE, WORKFLOW_WAITING_TYPE, } from "./steps/_step-dispatch-constants.js";
|
|
66
69
|
export { describeWorkflowStepError } from "./steps/describe-workflow-step-error.js";
|
|
70
|
+
export { unwrapWorkflowStepError, WorkflowStepError } from "./steps/workflow-step-error.js";
|
|
67
71
|
export { WORKFLOW_SYSTEM_EVENT_DEFS } from "./steps/workflow-system-events.js";
|
|
68
72
|
export { ANONYMOUS_ROLE, ANONYMOUS_USER_ID, createAnonymousUser, createSystemUser, SYSTEM_ROLE, SYSTEM_USER_ID, } from "./system-user.js";
|
|
69
73
|
export { findTierResolverUsage, isTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, } from "./tier-resolver-extension.js";
|
|
74
|
+
export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTransferAdjacency, } from "./transfer-adjacency.js";
|
|
70
75
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
71
76
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
|
72
77
|
export { runValidation } from "./validation.js";
|