@cosmicdrift/kumiko-framework 0.260.0 → 0.261.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 (31) hide show
  1. package/package.json +3 -3
  2. package/src/changes.json +13 -0
  3. package/src/db/__tests__/tenant-db-global.integration.test.ts +97 -0
  4. package/src/db/__tests__/tenant-db-global.test.ts +170 -0
  5. package/src/db/entity-table-meta.ts +8 -2
  6. package/src/db/table-builder.ts +37 -2
  7. package/src/db/tenant-db.ts +86 -4
  8. package/src/engine/__tests__/boot-validator.test.ts +16 -15
  9. package/src/engine/__tests__/build-app-schema.test.ts +14 -5
  10. package/src/engine/__tests__/engine.test.ts +10 -0
  11. package/src/engine/__tests__/entity-handlers.test.ts +48 -36
  12. package/src/engine/__tests__/extends-registrar.test.ts +27 -12
  13. package/src/engine/__tests__/registry.test.ts +16 -5
  14. package/src/engine/access.ts +2 -1
  15. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +200 -0
  16. package/src/engine/boot-validator/access-declarations.ts +141 -0
  17. package/src/engine/boot-validator/entity-handler.ts +1 -1
  18. package/src/engine/boot-validator/index.ts +2 -0
  19. package/src/engine/define-handler.ts +1 -0
  20. package/src/engine/entity-handlers.ts +29 -19
  21. package/src/engine/factories.ts +5 -3
  22. package/src/engine/feature-ast/extractors/hooks.ts +5 -1
  23. package/src/engine/feature-entity-handlers.ts +40 -27
  24. package/src/engine/index.ts +4 -0
  25. package/src/engine/types/handlers.ts +1 -0
  26. package/src/engine/types/index.ts +4 -0
  27. package/src/pipeline/__tests__/escape-hatch.integration.test.ts +93 -0
  28. package/src/pipeline/dispatch-shared.ts +3 -0
  29. package/src/secrets/__tests__/contains-secret.test.ts +5 -0
  30. package/src/testing/__tests__/e2e-generator.test.ts +11 -5
  31. package/src/ui-types/index.ts +6 -1
@@ -206,6 +206,8 @@ export async function buildHandlerContext(
206
206
  // but at this point we're the root of the pipeline — cast is safe.
207
207
  const dbSource = resolveDbSource(ctx, tx);
208
208
  const reqCtx = requestContext.get();
209
+ // db.global()'s write-gate — undefined for query/stream handlers, which can't declare escapeHatch.
210
+ const escapeHatch = registry.getWriteHandler(type)?.escapeHatch;
209
211
  const buildTenantScopedDb = (source: DbConnection | DbTx, signal: AbortSignal | undefined) =>
210
212
  createTenantDb(
211
213
  source,
@@ -214,6 +216,7 @@ export async function buildHandlerContext(
214
216
  context.tracer,
215
217
  context.meter,
216
218
  signal,
219
+ escapeHatch,
217
220
  );
218
221
  // Propagate the request's AbortSignal so every TenantDb query throws when
219
222
  // the client has disconnected — handlers with many sequential queries skip
@@ -54,6 +54,7 @@ declare const aSecret: Secret<string>;
54
54
  defineQueryHandler({
55
55
  name: "t:query:leak",
56
56
  schema,
57
+ access: { openToAll: true },
57
58
  handler: async () => ({ apiKey: aSecret }),
58
59
  });
59
60
 
@@ -64,6 +65,7 @@ function genericResponseHandler<K extends string>(kind: K) {
64
65
  return defineQueryHandler({
65
66
  name: "t:query:generic",
66
67
  schema,
68
+ access: { openToAll: true },
67
69
  handler: async () => ({ kind, ok: true }) as { kind: K; ok: boolean },
68
70
  });
69
71
  }
@@ -81,6 +83,7 @@ declare const leakingUnion: { ok: true } | { apiKey: Secret<string> };
81
83
  defineQueryHandler({
82
84
  name: "t:query:union-leak",
83
85
  schema,
86
+ access: { openToAll: true },
84
87
  handler: async () => leakingUnion,
85
88
  });
86
89
 
@@ -88,6 +91,7 @@ declare const cleanUnion: { ok: true } | { id: string };
88
91
  defineQueryHandler({
89
92
  name: "t:query:union-clean",
90
93
  schema,
94
+ access: { openToAll: true },
91
95
  handler: async () => cleanUnion,
92
96
  });
93
97
 
@@ -96,6 +100,7 @@ describe("R6 ContainsSecret", () => {
96
100
  const def = defineQueryHandler({
97
101
  name: "t:query:clean",
98
102
  schema,
103
+ access: { openToAll: true },
99
104
  handler: async () => ({ ok: true, value: createSecret("x").reveal() }),
100
105
  });
101
106
  expect(def.name).toBe("t:query:clean");
@@ -34,7 +34,7 @@ function createTasksFeature() {
34
34
  return defineFeature("tasks", (r) => {
35
35
  r.systemScope();
36
36
  r.entity("task", taskEntity);
37
- r.writeHandler(defineEntityCreateHandler("task", taskEntity));
37
+ r.writeHandler(defineEntityCreateHandler("task", taskEntity, { access: { openToAll: true } }));
38
38
  r.screen({
39
39
  id: "task-list",
40
40
  type: "entityList",
@@ -105,7 +105,7 @@ describe("generateE2ESpec", () => {
105
105
  const feature = defineFeature("events", (r) => {
106
106
  r.systemScope();
107
107
  r.entity("event", entity);
108
- r.writeHandler(defineEntityCreateHandler("event", entity));
108
+ r.writeHandler(defineEntityCreateHandler("event", entity, { access: { openToAll: true } }));
109
109
  r.screen({
110
110
  id: "event-edit",
111
111
  type: "entityEdit",
@@ -164,7 +164,9 @@ describe("generateE2ESpec", () => {
164
164
  const editOnly = defineFeature("edit-only", (r) => {
165
165
  r.systemScope();
166
166
  r.entity("task", taskEntity);
167
- r.writeHandler(defineEntityCreateHandler("task", taskEntity));
167
+ r.writeHandler(
168
+ defineEntityCreateHandler("task", taskEntity, { access: { openToAll: true } }),
169
+ );
168
170
  r.screen({
169
171
  id: "edit",
170
172
  type: "entityEdit",
@@ -198,7 +200,9 @@ describe("generateE2ESpec", () => {
198
200
  const feature = defineFeature("contacts", (r) => {
199
201
  r.systemScope();
200
202
  r.entity("contact", contactEntity);
201
- r.writeHandler(defineEntityCreateHandler("contact", contactEntity));
203
+ r.writeHandler(
204
+ defineEntityCreateHandler("contact", contactEntity, { access: { openToAll: true } }),
205
+ );
202
206
  r.screen({
203
207
  id: "list",
204
208
  type: "entityList",
@@ -220,7 +224,9 @@ describe("generateE2ESpec", () => {
220
224
  const mixed = defineFeature("mixed", (r) => {
221
225
  r.systemScope();
222
226
  r.entity("task", taskEntity);
223
- r.writeHandler(defineEntityCreateHandler("task", taskEntity));
227
+ r.writeHandler(
228
+ defineEntityCreateHandler("task", taskEntity, { access: { openToAll: true } }),
229
+ );
224
230
  r.screen({ id: "list", type: "entityList", entity: "task", columns: ["title"] });
225
231
  r.screen({
226
232
  id: "edit",
@@ -52,7 +52,12 @@ export type {
52
52
  SelectFieldDef,
53
53
  TextFieldDef,
54
54
  } from "../engine/types/fields";
55
- export type { AccessRule } from "../engine/types/handlers";
55
+ export type {
56
+ AccessRule,
57
+ OpenToAllAccessRule,
58
+ OpenToAllDeclaration,
59
+ } from "../engine/types/handlers";
60
+ export { isOpenToAllGranted } from "../engine/types/handlers";
56
61
  export type { IconKey, NavDefinition, NavIconKey } from "../engine/types/nav";
57
62
  export type {
58
63
  ActionFormRedirect,