@cosmicdrift/kumiko-framework 0.269.2 → 0.270.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 (84) hide show
  1. package/README.md +5 -1
  2. package/package.json +3 -3
  3. package/src/__tests__/anonymous-access.integration.test.ts +1 -1
  4. package/src/__tests__/error-contract.integration.test.ts +1 -1
  5. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +16 -0
  6. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +16 -0
  7. package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.expected.ts +12 -0
  8. package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.input.ts +12 -0
  9. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +7 -0
  10. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +7 -0
  11. package/src/__tests__/full-stack.integration.test.ts +2 -2
  12. package/src/__tests__/migrate-open-to-all-codemod.test.ts +84 -0
  13. package/src/__tests__/transition-guard.integration.test.ts +7 -7
  14. package/src/api/__tests__/api.test.ts +2 -2
  15. package/src/api/__tests__/body-limit.test.ts +1 -1
  16. package/src/api/__tests__/pii-leak-guard.integration.test.ts +2 -2
  17. package/src/api/__tests__/request-locale.integration.test.ts +1 -1
  18. package/src/api/__tests__/server-error-logging.test.ts +1 -1
  19. package/src/changes.json +8 -0
  20. package/src/db/__tests__/list-filter-field-access.integration.test.ts +1 -1
  21. package/src/derivatives/__tests__/derivatives-context.integration.test.ts +2 -2
  22. package/src/engine/__tests__/boot-validator-dashboard.test.ts +5 -5
  23. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
  24. package/src/engine/__tests__/boot-validator-projection-list.test.ts +17 -17
  25. package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +18 -18
  26. package/src/engine/__tests__/boot-validator-query-refs.test.ts +3 -3
  27. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +2 -2
  28. package/src/engine/__tests__/boot-validator.test.ts +18 -18
  29. package/src/engine/__tests__/build-app-schema.test.ts +10 -6
  30. package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +48 -0
  31. package/src/engine/__tests__/build-config-feature-schema.test.ts +34 -5
  32. package/src/engine/__tests__/engine.test.ts +39 -25
  33. package/src/engine/__tests__/entity-handlers.test.ts +19 -8
  34. package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +2 -2
  35. package/src/engine/__tests__/extends-registrar.test.ts +3 -3
  36. package/src/engine/__tests__/feature-crud-shorthand.test.ts +9 -3
  37. package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +1 -1
  38. package/src/engine/__tests__/hook-phases.test.ts +5 -5
  39. package/src/engine/__tests__/lifecycle-hooks.test.ts +9 -3
  40. package/src/engine/__tests__/multiselect-filter.integration.test.ts +1 -1
  41. package/src/engine/__tests__/post-query-hook.test.ts +6 -2
  42. package/src/engine/__tests__/projection-detail-actions.test.ts +2 -2
  43. package/src/engine/__tests__/projection-detail-singleton.test.ts +3 -3
  44. package/src/engine/__tests__/projection-detail-tabs.test.ts +3 -3
  45. package/src/engine/__tests__/projection-list-filter.integration.test.ts +1 -1
  46. package/src/engine/__tests__/registry.test.ts +19 -13
  47. package/src/engine/__tests__/screen.test.ts +26 -20
  48. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +5 -4
  49. package/src/engine/boot-validator/__tests__/parent-ref.test.ts +18 -6
  50. package/src/engine/boot-validator/access-declarations.ts +3 -5
  51. package/src/engine/boot-validator/nav.ts +4 -3
  52. package/src/engine/build-config-feature-schema.ts +9 -2
  53. package/src/engine/entity-handlers.ts +1 -1
  54. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +3 -1
  55. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +2 -2
  56. package/src/engine/feature-ast/__tests__/parse.test.ts +106 -4
  57. package/src/engine/feature-ast/__tests__/patcher.test.ts +3 -3
  58. package/src/engine/feature-ast/__tests__/read-optional-access-rule.test.ts +4 -4
  59. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +5 -1
  60. package/src/engine/feature-ast/extractors/handlers.ts +16 -2
  61. package/src/engine/feature-ast/extractors/hooks.ts +25 -4
  62. package/src/engine/feature-ast/patcher.ts +15 -3
  63. package/src/engine/feature-ast/patterns.ts +4 -0
  64. package/src/engine/feature-ast/render.ts +3 -0
  65. package/src/engine/pattern-library/shared-fields.ts +7 -3
  66. package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
  67. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +3 -3
  68. package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
  69. package/src/es-ops/__tests__/runner.integration.test.ts +2 -2
  70. package/src/jobs/__tests__/job-event-trigger.integration.test.ts +2 -2
  71. package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +2 -2
  72. package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
  73. package/src/observability/__tests__/observability.integration.test.ts +7 -7
  74. package/src/pipeline/__tests__/archive-stream.integration.test.ts +2 -2
  75. package/src/pipeline/__tests__/dispatcher.test.ts +15 -15
  76. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +10 -10
  77. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
  78. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
  79. package/src/pipeline/__tests__/query-projection.integration.test.ts +3 -3
  80. package/src/scripts/codemod/migrate-open-to-all.ts +248 -0
  81. package/src/secrets/__tests__/contains-secret.test.ts +5 -5
  82. package/src/stack/__tests__/request-helper.integration.test.ts +3 -3
  83. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
  84. package/src/testing/__tests__/e2e-generator.test.ts +19 -5
@@ -87,7 +87,7 @@ describe("defineFeature", () => {
87
87
  const _email: string = event.payload.email;
88
88
  return { isSuccess: true, data: { id: 1 } };
89
89
  },
90
- { access: { openToAll: true } },
90
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
91
91
  );
92
92
  });
93
93
 
@@ -105,7 +105,7 @@ describe("defineFeature", () => {
105
105
  isSuccess: true,
106
106
  data: null,
107
107
  }),
108
- { access: { openToAll: true } },
108
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
109
109
  );
110
110
  });
111
111
  expect(ref?.name).toBe("order:create");
@@ -115,7 +115,7 @@ describe("defineFeature", () => {
115
115
  let ref: { name: string } | undefined;
116
116
  defineFeature("test", (r) => {
117
117
  ref = r.queryHandler("order:list", z.object({}), async () => [], {
118
- access: { openToAll: true },
118
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
119
119
  });
120
120
  });
121
121
  expect(ref?.name).toBe("order:list");
@@ -130,13 +130,15 @@ describe("defineFeature", () => {
130
130
  async function* (query) {
131
131
  yield `echo:${query.payload.prompt}`;
132
132
  },
133
- { access: { openToAll: true } },
133
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
134
134
  );
135
135
  });
136
136
  expect(ref?.name).toBe("chat:complete");
137
137
 
138
138
  const stored = feature.streamHandlers["chat:complete"];
139
- expect(stored?.access).toEqual({ openToAll: true });
139
+ expect(stored?.access).toEqual({
140
+ openToAll: { reason: "test handler callable by any signed-in test user" },
141
+ });
140
142
 
141
143
  const chunks: string[] = [];
142
144
  for await (const chunk of stored!.handler(
@@ -225,7 +227,7 @@ describe("defineFeature", () => {
225
227
  const _id: number = query.payload.userId;
226
228
  return { id: _id, email: "test@test.de" };
227
229
  },
228
- { access: { openToAll: true } },
230
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
229
231
  );
230
232
  });
231
233
 
@@ -239,7 +241,7 @@ describe("defineFeature", () => {
239
241
  handler: async () => {
240
242
  return [{ id: 1, email: "test@test.de" }];
241
243
  },
242
- access: { openToAll: true },
244
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
243
245
  });
244
246
 
245
247
  const feature = defineFeature("test", (r) => {
@@ -402,12 +404,12 @@ describe("createRegistry", () => {
402
404
  test("looks up handlers across features", () => {
403
405
  const f1 = defineFeature("admin", (r) => {
404
406
  r.writeHandler("user:invite", z.object({}), async () => ({ isSuccess: true, data: null }), {
405
- access: { openToAll: true },
407
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
406
408
  });
407
409
  });
408
410
  const f2 = defineFeature("profile", (r) => {
409
411
  r.queryHandler("profile:me", z.object({}), async () => ({ id: 1 }), {
410
- access: { openToAll: true },
412
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
411
413
  });
412
414
  });
413
415
 
@@ -442,12 +444,12 @@ describe("createRegistry", () => {
442
444
  test("different features can have same handler short name (prefixed differently)", () => {
443
445
  const f1 = defineFeature("a", (r) => {
444
446
  r.writeHandler("user:invite", z.object({}), async () => ({ isSuccess: true, data: null }), {
445
- access: { openToAll: true },
447
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
446
448
  });
447
449
  });
448
450
  const f2 = defineFeature("b", (r) => {
449
451
  r.writeHandler("user:invite", z.object({}), async () => ({ isSuccess: true, data: null }), {
450
- access: { openToAll: true },
452
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
451
453
  });
452
454
  });
453
455
 
@@ -513,7 +515,7 @@ describe("createRegistry", () => {
513
515
  );
514
516
  // Handler name "promote" has no entity prefix → can't be mapped
515
517
  r.writeHandler("promote", z.object({}), async () => ({ isSuccess: true, data: null }), {
516
- access: { openToAll: true },
518
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
517
519
  });
518
520
  });
519
521
 
@@ -531,7 +533,7 @@ describe("createRegistry", () => {
531
533
  );
532
534
  // No field-access rules on entity → "reset" without entity prefix is fine
533
535
  r.writeHandler("reset", z.object({}), async () => ({ isSuccess: true, data: null }), {
534
- access: { openToAll: true },
536
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
535
537
  });
536
538
  });
537
539
 
@@ -562,7 +564,7 @@ describe("createRegistry", () => {
562
564
  isSuccess: true,
563
565
  data: null,
564
566
  }),
565
- { access: { openToAll: true } },
567
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
566
568
  );
567
569
  });
568
570
 
@@ -591,10 +593,12 @@ describe("createRegistry", () => {
591
593
  isSuccess: true,
592
594
  data: null,
593
595
  }),
594
- { access: { openToAll: true } },
596
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
595
597
  );
596
598
  // Typo: "emp" instead of "employee"
597
- r.queryHandler("emp:list", z.object({}), async () => [], { access: { openToAll: true } });
599
+ r.queryHandler("emp:list", z.object({}), async () => [], {
600
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
601
+ });
598
602
  });
599
603
 
600
604
  expect(() => createRegistry([feature])).toThrow(/emp:list.*entity-bound.*no matching entity/i);
@@ -622,13 +626,15 @@ describe("createRegistry", () => {
622
626
  isSuccess: true,
623
627
  data: null,
624
628
  }),
625
- { access: { openToAll: true } },
629
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
626
630
  );
627
631
  // Standalone queries — no dot, intentionally not entity-bound
628
632
  r.queryHandler("dashboard", z.object({}), async () => ({ total: 42 }), {
629
- access: { openToAll: true },
633
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
634
+ });
635
+ r.queryHandler("orgChart", z.object({}), async () => [], {
636
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
630
637
  });
631
- r.queryHandler("orgChart", z.object({}), async () => [], { access: { openToAll: true } });
632
638
  });
633
639
 
634
640
  expect(() => createRegistry([feature])).not.toThrow();
@@ -924,12 +930,20 @@ describe("hasAccess", () => {
924
930
 
925
931
  test("openToAll grants access to any authenticated user", () => {
926
932
  const user = createTestUser({ roles: ["Employee"] });
927
- expect(hasAccess(user, { openToAll: true })).toBe(true);
933
+ expect(
934
+ hasAccess(user, {
935
+ openToAll: { reason: "test handler callable by any signed-in test user" },
936
+ }),
937
+ ).toBe(true);
928
938
  });
929
939
 
930
940
  test("openToAll grants access even to user with no roles", () => {
931
941
  const user = createTestUser({ roles: [] });
932
- expect(hasAccess(user, { openToAll: true })).toBe(true);
942
+ expect(
943
+ hasAccess(user, {
944
+ openToAll: { reason: "test handler callable by any signed-in test user" },
945
+ }),
946
+ ).toBe(true);
933
947
  });
934
948
 
935
949
  test.each([{ openToAll: false }, { openToAll: {} }])(
@@ -2105,10 +2119,10 @@ describe("registry boot validation", () => {
2105
2119
  const feature = defineFeature("shop", (r) => {
2106
2120
  r.entity("order", createEntity({ table: "Orders", fields: {} }));
2107
2121
  r.writeHandler("order:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
2108
- access: { openToAll: true },
2122
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
2109
2123
  });
2110
2124
  r.writeHandler("order:cancel", z.object({}), async () => ({ isSuccess: true, data: null }), {
2111
- access: { openToAll: true },
2125
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
2112
2126
  });
2113
2127
  // Ein Job-Body, zwei Trigger — DRY-Pattern für Fanout-Cases.
2114
2128
  r.job(
@@ -2129,7 +2143,7 @@ describe("registry boot validation", () => {
2129
2143
  const feature = defineFeature("shop", (r) => {
2130
2144
  r.entity("order", createEntity({ table: "Orders", fields: {} }));
2131
2145
  r.writeHandler("order:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
2132
- access: { openToAll: true },
2146
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
2133
2147
  });
2134
2148
  // create existiert, cancel nicht — zweiter Trigger ist Geist
2135
2149
  r.job(
@@ -2153,7 +2167,7 @@ describe("registry boot validation", () => {
2153
2167
  const feature = defineFeature("test", (r) => {
2154
2168
  r.entity("item", createEntity({ table: "Items", fields: {} }));
2155
2169
  r.writeHandler("item.create", z.object({}), async () => ({ isSuccess: true, data: null }), {
2156
- access: { openToAll: true },
2170
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
2157
2171
  });
2158
2172
  r.hook("postSave", "item.create", async () => {});
2159
2173
  });
@@ -149,9 +149,11 @@ describe("defineEntityQueryHandler", () => {
149
149
 
150
150
  test("access option is forwarded", () => {
151
151
  const def = defineEntityListHandler("note", noteEntity, {
152
- access: { openToAll: true },
152
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
153
+ });
154
+ expect(def.access).toEqual({
155
+ openToAll: { reason: "test handler callable by any signed-in test user" },
153
156
  });
154
- expect(def.access).toEqual({ openToAll: true });
155
157
  });
156
158
  });
157
159
 
@@ -180,10 +182,12 @@ describe("defineProjectionQueryHandler", () => {
180
182
  const def = defineProjectionQueryHandler(
181
183
  "revenue:list",
182
184
  "showcase:projection:customer-revenue",
183
- { access: { openToAll: true } },
185
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
184
186
  );
185
187
  expect(def.name).toBe("revenue:list");
186
- expect(def.access).toEqual({ openToAll: true });
188
+ expect(def.access).toEqual({
189
+ openToAll: { reason: "test handler callable by any signed-in test user" },
190
+ });
187
191
  // Empty-object schema — handler takes no payload fields.
188
192
  expect(def.schema.safeParse({}).success).toBe(true);
189
193
  });
@@ -192,7 +196,7 @@ describe("defineProjectionQueryHandler", () => {
192
196
  const def = defineProjectionQueryHandler(
193
197
  "revenue:list",
194
198
  "showcase:projection:customer-revenue",
195
- { access: { openToAll: true } },
199
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
196
200
  );
197
201
  const fakeRows = [{ customer: "a", totalCents: 100 }];
198
202
  const ctx = {
@@ -215,7 +219,10 @@ describe("defineProjectionQueryHandler", () => {
215
219
  const def = defineProjectionQueryHandler(
216
220
  "revenue:list",
217
221
  "showcase:projection:customer-revenue",
218
- { access: { openToAll: true }, unsafeAllTenants: true },
222
+ {
223
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
224
+ unsafeAllTenants: true,
225
+ },
219
226
  );
220
227
  const ctx = { queryProjection: mock().mockResolvedValue([]) };
221
228
  await def.handler(
@@ -328,7 +335,9 @@ describe("registerEntityCrud", () => {
328
335
  const { r, entities, writes, queries } = createCrudRegistrarMock();
329
336
  registerEntityCrud(r, "note", noteEntitySoftDelete, {
330
337
  write: { access: { roles: ["Admin"] } },
331
- read: { access: { openToAll: true } },
338
+ read: {
339
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
340
+ },
332
341
  });
333
342
  expect(entities["note"]).toBe(noteEntitySoftDelete);
334
343
  expect(writes.map((w) => w.name)).toEqual([
@@ -339,7 +348,9 @@ describe("registerEntityCrud", () => {
339
348
  ]);
340
349
  expect(queries.map((q) => q.name)).toEqual(["note:list", "note:detail"]);
341
350
  expect(writes[0]?.access).toEqual({ roles: ["Admin"] });
342
- expect(queries[0]?.access).toEqual({ openToAll: true });
351
+ expect(queries[0]?.access).toEqual({
352
+ openToAll: { reason: "test handler callable by any signed-in test user" },
353
+ });
343
354
  });
344
355
 
345
356
  test("verbs.delete: false skips delete handler", () => {
@@ -50,7 +50,7 @@ const throwOnPreSave: import("../types").PreSaveHookFn = async () => {
50
50
  const contactFeature = defineFeature("presave-wiring", (r) => {
51
51
  r.crud("contact", contactEntity, {
52
52
  write: { access: { roles: ["User"] } },
53
- read: { access: { openToAll: true } },
53
+ read: { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
54
54
  });
55
55
 
56
56
  // preSave has no entity-wide `{ allOf }` shorthand (unlike postSave/
@@ -70,7 +70,7 @@ const throwingEntity = createEntity({
70
70
  const throwingFeature = defineFeature("presave-wiring-throw", (r) => {
71
71
  r.crud("thing", throwingEntity, {
72
72
  write: { access: { roles: ["User"] } },
73
- read: { access: { openToAll: true } },
73
+ read: { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
74
74
  });
75
75
  r.hook("preSave", "thing:create", throwOnPreSave);
76
76
  });
@@ -142,7 +142,7 @@ describe("extendsRegistrar", () => {
142
142
  isSuccess: true as const,
143
143
  data: { id: "c1" },
144
144
  }),
145
- { access: { openToAll: true } },
145
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
146
146
  );
147
147
  r.useExtension("audited", "credit");
148
148
  });
@@ -181,7 +181,7 @@ describe("extendsRegistrar", () => {
181
181
  isSuccess: true as const,
182
182
  data: { id: "v1" },
183
183
  }),
184
- { access: { openToAll: true } },
184
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
185
185
  );
186
186
  r.writeHandler(
187
187
  "vehicle:update",
@@ -190,7 +190,7 @@ describe("extendsRegistrar", () => {
190
190
  isSuccess: true as const,
191
191
  data: { id: "v1" },
192
192
  }),
193
- { access: { openToAll: true } },
193
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
194
194
  );
195
195
  r.useExtension("audited", "vehicle");
196
196
  });
@@ -11,7 +11,9 @@ const taskEntity = createEntity({
11
11
  describe("r.crud", () => {
12
12
  test("registers the entity + full CRUD handler set, same as registerEntityCrud", () => {
13
13
  const write = { access: { roles: ["Admin"] } } as const;
14
- const read = { access: { openToAll: true } } as const;
14
+ const read = {
15
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
16
+ } as const;
15
17
 
16
18
  const feature = defineFeature("via-crud", (r) => {
17
19
  r.crud("task", taskEntity, { write, read });
@@ -28,7 +30,9 @@ describe("r.crud", () => {
28
30
 
29
31
  test("without verbAccess, write handlers keep write.access (never fall back to the broader read.access)", () => {
30
32
  const write = { access: { roles: ["Manager"] } } as const;
31
- const read = { access: { openToAll: true } } as const;
33
+ const read = {
34
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
35
+ } as const;
32
36
 
33
37
  const feature = defineFeature("via-crud-no-verb-access", (r) => {
34
38
  r.crud("task", taskEntity, { write, read });
@@ -44,7 +48,9 @@ describe("r.crud", () => {
44
48
 
45
49
  test("verbAccess overrides access per verb, other verbs keep write/read.access", () => {
46
50
  const write = { access: { roles: ["Manager"] } } as const;
47
- const read = { access: { openToAll: true } } as const;
51
+ const read = {
52
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
53
+ } as const;
48
54
  const deleteAccess = { roles: ["Operator"] } as const;
49
55
  const restoreAccess = { roles: ["Operator"] } as const;
50
56
  const listAccess = { roles: ["Auditor"] } as const;
@@ -13,7 +13,7 @@ const propertyEntity = createEntity({
13
13
  const propertyFeature = defineFeature("crud-verb-access", (r) => {
14
14
  r.crud("property", propertyEntity, {
15
15
  write: { access: { roles: ["User"] } },
16
- read: { access: { openToAll: true } },
16
+ read: { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
17
17
  verbAccess: {
18
18
  delete: { roles: ["Admin"] },
19
19
  restore: { roles: ["Admin"] },
@@ -27,7 +27,7 @@ describe("HookPhases defaults", () => {
27
27
  const feature = defineFeature("test", (r) => {
28
28
  r.entity("thing", createEntity({ table: "things", fields: {} }));
29
29
  r.writeHandler("thing:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
30
- access: { openToAll: true },
30
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
31
31
  });
32
32
  r.hook("postSave", "thing:create", noopSave);
33
33
  });
@@ -41,7 +41,7 @@ describe("HookPhases defaults", () => {
41
41
  const feature = defineFeature("test", (r) => {
42
42
  r.entity("thing", createEntity({ table: "things", fields: {} }));
43
43
  r.writeHandler("thing:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
44
- access: { openToAll: true },
44
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
45
45
  });
46
46
  r.hook("postSave", "thing:create", noopSave, { phase: HookPhases.inTransaction });
47
47
  });
@@ -60,7 +60,7 @@ describe("HookPhases defaults", () => {
60
60
  isSuccess: true,
61
61
  data: null,
62
62
  }),
63
- { access: { openToAll: true } },
63
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
64
64
  );
65
65
  r.hook("preDelete", "thing:delete", async () => undefined);
66
66
  });
@@ -95,7 +95,7 @@ describe("Registry phase filtering", () => {
95
95
  const feature = defineFeature("test", (r) => {
96
96
  r.entity("thing", createEntity({ table: "things", fields: {} }));
97
97
  r.writeHandler("thing:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
98
- access: { openToAll: true },
98
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
99
99
  });
100
100
  r.hook("postSave", "thing:create", inTxFn, { phase: HookPhases.inTransaction });
101
101
  r.hook("postSave", "thing:create", afterFn); // default afterCommit
@@ -127,7 +127,7 @@ describe("Registry phase filtering", () => {
127
127
  const feature = defineFeature("test", (r) => {
128
128
  r.entity("thing", createEntity({ table: "things", fields: {} }));
129
129
  r.writeHandler("thing:create", z.object({}), async () => ({ isSuccess: true, data: null }), {
130
- access: { openToAll: true },
130
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
131
131
  });
132
132
  });
133
133
 
@@ -69,7 +69,9 @@ describe("lifecycle hooks in registry", () => {
69
69
  test("merges preSave hooks within same feature", () => {
70
70
  const f1 = defineFeature("a", (r) => {
71
71
  r.entity("user", createEntity({ table: "Users", fields: {} }));
72
- r.writeHandler("user", z.object({}), stubHandler, { access: { openToAll: true } });
72
+ r.writeHandler("user", z.object({}), stubHandler, {
73
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
74
+ });
73
75
  r.hook("preSave", "user", async (changes) => changes);
74
76
  r.hook("preSave", "user", async (changes) => changes);
75
77
  });
@@ -81,12 +83,16 @@ describe("lifecycle hooks in registry", () => {
81
83
  test("cross-feature hooks use full prefixed name", () => {
82
84
  const f1 = defineFeature("a", (r) => {
83
85
  r.entity("user", createEntity({ table: "Users", fields: {} }));
84
- r.writeHandler("user", z.object({}), stubHandler, { access: { openToAll: true } });
86
+ r.writeHandler("user", z.object({}), stubHandler, {
87
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
88
+ });
85
89
  r.hook("postSave", "user", async () => {});
86
90
  });
87
91
  // Feature b hooks into a.user by using the full prefixed name
88
92
  const f2 = defineFeature("b", (r) => {
89
- r.writeHandler("a.user", z.object({}), stubHandler, { access: { openToAll: true } });
93
+ r.writeHandler("a.user", z.object({}), stubHandler, {
94
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
95
+ });
90
96
  r.hook("postSave", "a.user", async () => {});
91
97
  r.hook("postSave", "a.user", async () => {});
92
98
  });
@@ -28,7 +28,7 @@ const LIST_QN = "checklist:query:equipment:list";
28
28
  const checklistFeature = defineFeature("checklist", (r) => {
29
29
  r.crud("equipment", equipmentEntity, {
30
30
  write: { access: { roles: ["Admin"] } },
31
- read: { access: { openToAll: true } },
31
+ read: { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
32
32
  });
33
33
  });
34
34
 
@@ -24,7 +24,9 @@ describe("postQuery hook registration", () => {
24
24
  test("r.hook('postQuery', handlerQn, fn) lands in handler-keyed lifecycleHooks map", () => {
25
25
  const feature = defineFeature("test", (r) => {
26
26
  r.entity("thing", createEntity({ table: "things", fields: {} }));
27
- r.queryHandler("thing:list", z.object({}), async () => [], { access: { openToAll: true } });
27
+ r.queryHandler("thing:list", z.object({}), async () => [], {
28
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
29
+ });
28
30
  r.hook("postQuery", "thing:list", noop);
29
31
  });
30
32
 
@@ -70,7 +72,9 @@ describe("Registry getters", () => {
70
72
 
71
73
  const feature = defineFeature("test", (r) => {
72
74
  r.entity("thing", createEntity({ table: "things", fields: {} }));
73
- r.queryHandler("thing:list", z.object({}), async () => [], { access: { openToAll: true } });
75
+ r.queryHandler("thing:list", z.object({}), async () => [], {
76
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
77
+ });
74
78
  r.hook("postQuery", "thing:list", fn);
75
79
  });
76
80
 
@@ -42,13 +42,13 @@ describe("validateBoot — projectionDetail actions (fw#2166)", () => {
42
42
  test("valid navigate + writeHandler actions boot cleanly", () => {
43
43
  const feature = defineFeature("app", (r) => {
44
44
  r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
45
- access: { openToAll: true },
45
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
46
46
  });
47
47
  r.writeHandler(
48
48
  "archive",
49
49
  z.object({ id: z.string() }),
50
50
  async () => ({ isSuccess: true as const, data: {} }),
51
- { access: { openToAll: true } },
51
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
52
52
  );
53
53
  r.screen({
54
54
  id: "rent-detail",
@@ -21,7 +21,7 @@ describe("validateBoot — projectionDetail singleton (fw#2312)", () => {
21
21
  test("singleton + idParam throws", () => {
22
22
  const feature = defineFeature("app", (r) => {
23
23
  r.queryHandler("me", z.object({}), async () => ({ id: "u1" }), {
24
- access: { openToAll: true },
24
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
25
25
  });
26
26
  r.screen({
27
27
  id: "profile",
@@ -46,7 +46,7 @@ describe("validateBoot — projectionDetail singleton (fw#2312)", () => {
46
46
  }),
47
47
  );
48
48
  r.queryHandler("me", z.object({}), async () => ({ id: "u1" }), {
49
- access: { openToAll: true },
49
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
50
50
  });
51
51
  r.screen({
52
52
  id: "profile",
@@ -71,7 +71,7 @@ describe("validateBoot — projectionDetail singleton (fw#2312)", () => {
71
71
  test("singleton without idParam/detailFor boots cleanly", () => {
72
72
  const feature = defineFeature("app", (r) => {
73
73
  r.queryHandler("me", z.object({}), async () => ({ id: "u1" }), {
74
- access: { openToAll: true },
74
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
75
75
  });
76
76
  r.screen({
77
77
  id: "profile",
@@ -140,7 +140,7 @@ describe("validateBoot — projectionDetail tabs (fw record-layout)", () => {
140
140
  test("object-form metric with its own label needs no fieldLabels entry", () => {
141
141
  const feature = defineFeature("app", (r) => {
142
142
  r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
143
- access: { openToAll: true },
143
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
144
144
  });
145
145
  r.screen({
146
146
  id: "rent-detail",
@@ -192,7 +192,7 @@ describe("validateBoot — projectionDetail tabs (fw record-layout)", () => {
192
192
  test("metric navigate.tab with no screen/entity — a known section id boots cleanly", () => {
193
193
  const feature = defineFeature("app", (r) => {
194
194
  r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
195
- access: { openToAll: true },
195
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
196
196
  });
197
197
  r.screen({
198
198
  id: "rent-detail",
@@ -214,7 +214,7 @@ describe("validateBoot — projectionDetail tabs (fw record-layout)", () => {
214
214
  test("valid tabs + header + metrics declaration boots cleanly", () => {
215
215
  const feature = defineFeature("app", (r) => {
216
216
  r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
217
- access: { openToAll: true },
217
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
218
218
  });
219
219
  r.screen({
220
220
  id: "rent-detail",
@@ -37,7 +37,7 @@ const LIST_QN = "roster:query:member:list";
37
37
  const rosterFeature = defineFeature("roster", (r) => {
38
38
  r.crud("member", memberEntity, {
39
39
  write: { access: { roles: ["Admin"] } },
40
- read: { access: { openToAll: true } },
40
+ read: { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
41
41
  });
42
42
  r.screen({
43
43
  id: "member-list",