@cosmicdrift/kumiko-framework 0.269.1 → 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 (99) 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-db-raw/global-fetch.expected.ts +9 -0
  6. package/src/__tests__/fixtures/migrate-db-raw/global-fetch.input.ts +10 -0
  7. package/src/__tests__/fixtures/migrate-db-raw/global-select.expected.ts +6 -0
  8. package/src/__tests__/fixtures/migrate-db-raw/global-select.input.ts +12 -0
  9. package/src/__tests__/fixtures/migrate-db-raw/import-pruning.expected.ts +12 -0
  10. package/src/__tests__/fixtures/migrate-db-raw/import-pruning.input.ts +12 -0
  11. package/src/__tests__/fixtures/migrate-db-raw/manual-only.expected.ts +26 -0
  12. package/src/__tests__/fixtures/migrate-db-raw/manual-only.input.ts +26 -0
  13. package/src/__tests__/fixtures/migrate-db-raw/own-tenant-const-shorthand.expected.ts +10 -0
  14. package/src/__tests__/fixtures/migrate-db-raw/own-tenant-const-shorthand.input.ts +11 -0
  15. package/src/__tests__/fixtures/migrate-db-raw/own-tenant-event.expected.ts +9 -0
  16. package/src/__tests__/fixtures/migrate-db-raw/own-tenant-event.input.ts +15 -0
  17. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +16 -0
  18. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +16 -0
  19. package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.expected.ts +12 -0
  20. package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.input.ts +12 -0
  21. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +7 -0
  22. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +7 -0
  23. package/src/__tests__/full-stack.integration.test.ts +2 -2
  24. package/src/__tests__/migrate-db-raw-codemod.test.ts +132 -0
  25. package/src/__tests__/migrate-open-to-all-codemod.test.ts +84 -0
  26. package/src/__tests__/transition-guard.integration.test.ts +7 -7
  27. package/src/__tests__/upgrade-cli.test.ts +54 -0
  28. package/src/api/__tests__/api.test.ts +2 -2
  29. package/src/api/__tests__/body-limit.test.ts +1 -1
  30. package/src/api/__tests__/pii-leak-guard.integration.test.ts +2 -2
  31. package/src/api/__tests__/request-locale.integration.test.ts +1 -1
  32. package/src/api/__tests__/server-error-logging.test.ts +1 -1
  33. package/src/changes.json +10 -2
  34. package/src/db/__tests__/list-filter-field-access.integration.test.ts +1 -1
  35. package/src/derivatives/__tests__/derivatives-context.integration.test.ts +2 -2
  36. package/src/engine/__tests__/boot-validator-dashboard.test.ts +5 -5
  37. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
  38. package/src/engine/__tests__/boot-validator-projection-list.test.ts +17 -17
  39. package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +18 -18
  40. package/src/engine/__tests__/boot-validator-query-refs.test.ts +3 -3
  41. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +2 -2
  42. package/src/engine/__tests__/boot-validator.test.ts +18 -18
  43. package/src/engine/__tests__/build-app-schema.test.ts +10 -6
  44. package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +48 -0
  45. package/src/engine/__tests__/build-config-feature-schema.test.ts +34 -5
  46. package/src/engine/__tests__/engine.test.ts +39 -25
  47. package/src/engine/__tests__/entity-handlers.test.ts +19 -8
  48. package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +2 -2
  49. package/src/engine/__tests__/extends-registrar.test.ts +3 -3
  50. package/src/engine/__tests__/feature-crud-shorthand.test.ts +9 -3
  51. package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +1 -1
  52. package/src/engine/__tests__/hook-phases.test.ts +5 -5
  53. package/src/engine/__tests__/lifecycle-hooks.test.ts +9 -3
  54. package/src/engine/__tests__/multiselect-filter.integration.test.ts +1 -1
  55. package/src/engine/__tests__/post-query-hook.test.ts +6 -2
  56. package/src/engine/__tests__/projection-detail-actions.test.ts +2 -2
  57. package/src/engine/__tests__/projection-detail-singleton.test.ts +3 -3
  58. package/src/engine/__tests__/projection-detail-tabs.test.ts +3 -3
  59. package/src/engine/__tests__/projection-list-filter.integration.test.ts +1 -1
  60. package/src/engine/__tests__/registry.test.ts +19 -13
  61. package/src/engine/__tests__/screen.test.ts +26 -20
  62. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +5 -4
  63. package/src/engine/boot-validator/__tests__/parent-ref.test.ts +18 -6
  64. package/src/engine/boot-validator/access-declarations.ts +3 -5
  65. package/src/engine/boot-validator/nav.ts +4 -3
  66. package/src/engine/build-config-feature-schema.ts +9 -2
  67. package/src/engine/entity-handlers.ts +1 -1
  68. package/src/engine/feature-ast/__tests__/canonical-form.test.ts +3 -1
  69. package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +2 -2
  70. package/src/engine/feature-ast/__tests__/parse.test.ts +106 -4
  71. package/src/engine/feature-ast/__tests__/patcher.test.ts +3 -3
  72. package/src/engine/feature-ast/__tests__/read-optional-access-rule.test.ts +4 -4
  73. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +5 -1
  74. package/src/engine/feature-ast/extractors/handlers.ts +16 -2
  75. package/src/engine/feature-ast/extractors/hooks.ts +25 -4
  76. package/src/engine/feature-ast/patcher.ts +15 -3
  77. package/src/engine/feature-ast/patterns.ts +4 -0
  78. package/src/engine/feature-ast/render.ts +3 -0
  79. package/src/engine/pattern-library/shared-fields.ts +7 -3
  80. package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
  81. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +3 -3
  82. package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
  83. package/src/es-ops/__tests__/runner.integration.test.ts +2 -2
  84. package/src/jobs/__tests__/job-event-trigger.integration.test.ts +2 -2
  85. package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +2 -2
  86. package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
  87. package/src/observability/__tests__/observability.integration.test.ts +7 -7
  88. package/src/pipeline/__tests__/archive-stream.integration.test.ts +2 -2
  89. package/src/pipeline/__tests__/dispatcher.test.ts +15 -15
  90. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +10 -10
  91. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
  92. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
  93. package/src/pipeline/__tests__/query-projection.integration.test.ts +3 -3
  94. package/src/scripts/codemod/migrate-db-raw.ts +397 -0
  95. package/src/scripts/codemod/migrate-open-to-all.ts +248 -0
  96. package/src/secrets/__tests__/contains-secret.test.ts +5 -5
  97. package/src/stack/__tests__/request-helper.integration.test.ts +3 -3
  98. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
  99. package/src/testing/__tests__/e2e-generator.test.ts +19 -5
@@ -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",
@@ -78,13 +78,17 @@ describe("getAllQueryHandlers", () => {
78
78
  const taskFeature = defineFeature("registry-test-task", (r) => {
79
79
  r.crud("task", taskEntity, {
80
80
  write: { access: { roles: ["Admin"] } },
81
- read: { access: { openToAll: true } },
81
+ read: {
82
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
83
+ },
82
84
  });
83
85
  });
84
86
  const noteFeature = defineFeature("registry-test-note", (r) => {
85
87
  r.crud("note", noteEntity, {
86
88
  write: { access: { roles: ["Admin"] } },
87
- read: { access: { openToAll: true } },
89
+ read: {
90
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
91
+ },
88
92
  });
89
93
  });
90
94
 
@@ -109,12 +113,12 @@ describe("getAllStreamHandlers", () => {
109
113
  test("returns every registered stream handler, qualified, across multiple features", () => {
110
114
  const aiFeature = defineFeature("registry-test-ai", (r) => {
111
115
  r.streamHandler("chat:complete", z.object({ prompt: z.string() }), async function* () {}, {
112
- access: { openToAll: true },
116
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
113
117
  });
114
118
  });
115
119
  const otherFeature = defineFeature("registry-test-other", (r) => {
116
120
  r.streamHandler("chat:complete", z.object({ prompt: z.string() }), async function* () {}, {
117
- access: { openToAll: true },
121
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
118
122
  });
119
123
  });
120
124
 
@@ -137,12 +141,12 @@ describe("getAllStreamHandlers", () => {
137
141
  test("duplicate stream-handler short-name across features qualifies independently, no collision", () => {
138
142
  const aiFeature = defineFeature("registry-test-dup-a", (r) => {
139
143
  r.streamHandler("chat:complete", z.object({}), async function* () {}, {
140
- access: { openToAll: true },
144
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
141
145
  });
142
146
  });
143
147
  const otherFeature = defineFeature("registry-test-dup-b", (r) => {
144
148
  r.streamHandler("chat:complete", z.object({}), async function* () {}, {
145
- access: { openToAll: true },
149
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
146
150
  });
147
151
  });
148
152
  expect(() => createRegistry([aiFeature, otherFeature])).not.toThrow();
@@ -154,12 +158,12 @@ describe("getAllStreamHandlers", () => {
154
158
  // fire) but toKebab() collapses both to the same qualified name.
155
159
  const featureA = defineFeature("registry-test-kebab-dup", (r) => {
156
160
  r.streamHandler("chat:complete", z.object({}), async function* () {}, {
157
- access: { openToAll: true },
161
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
158
162
  });
159
163
  });
160
164
  const featureB = defineFeature("registryTestKebabDup", (r) => {
161
165
  r.streamHandler("chat:complete", z.object({}), async function* () {}, {
162
- access: { openToAll: true },
166
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
163
167
  });
164
168
  });
165
169
  expect(() => createRegistry([featureA, featureB])).toThrow(/Duplicate stream handler/);
@@ -173,7 +177,7 @@ describe("getAllStreamHandlers", () => {
173
177
  name: "chat:complete",
174
178
  schema,
175
179
  handler: handlerFn,
176
- access: { openToAll: true },
180
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
177
181
  rateLimit: { per: "ip+handler", limit: 5, windowSeconds: 60 },
178
182
  });
179
183
  });
@@ -184,7 +188,9 @@ describe("getAllStreamHandlers", () => {
184
188
  expect(registered).toBeDefined();
185
189
  expect(registered?.schema).toBe(schema);
186
190
  expect(registered?.handler).toBe(handlerFn);
187
- expect(registered?.access).toEqual({ openToAll: true });
191
+ expect(registered?.access).toEqual({
192
+ openToAll: { reason: "test handler callable by any signed-in test user" },
193
+ });
188
194
  expect(registered?.rateLimit).toEqual({ per: "ip+handler", limit: 5, windowSeconds: 60 });
189
195
  });
190
196
  });
@@ -196,7 +202,7 @@ describe("double-qualified handler names (#1991)", () => {
196
202
  "registry-test-ai-orch:query:duplicate-candidates",
197
203
  z.object({}),
198
204
  async () => ({}),
199
- { access: { openToAll: true } },
205
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
200
206
  );
201
207
  });
202
208
 
@@ -206,7 +212,7 @@ describe("double-qualified handler names (#1991)", () => {
206
212
  test("createRegistry allows a sub-structured short name whose entity prefix merely resembles the feature name", () => {
207
213
  const feature = defineFeature("registry-test-invoices", (r) => {
208
214
  r.queryHandler("invoice:mark-paid", z.object({}), async () => ({}), {
209
- access: { openToAll: true },
215
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
210
216
  });
211
217
  });
212
218
 
@@ -309,7 +315,7 @@ describe("extensionSelector boot-validation", () => {
309
315
  "doSomething",
310
316
  z.object({}),
311
317
  async () => ({ isSuccess: true as const, data: {} }),
312
- { access: { openToAll: true } },
318
+ { access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
313
319
  );
314
320
  r.useExtension("credit-cap", "credit");
315
321
  });
@@ -211,7 +211,7 @@ describe("r.screen() — registration", () => {
211
211
  const features = [
212
212
  defineFeature("app", (r) => {
213
213
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
214
- access: { openToAll: true },
214
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
215
215
  });
216
216
  r.screen({
217
217
  id: "x",
@@ -281,10 +281,10 @@ describe("r.screen() — registration", () => {
281
281
  const features = [
282
282
  defineFeature("app", (r) => {
283
283
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
284
- access: { openToAll: true },
284
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
285
285
  });
286
286
  r.queryHandler("foo:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
287
- access: { openToAll: true },
287
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
288
288
  });
289
289
  r.screen({
290
290
  id: "x",
@@ -345,10 +345,10 @@ describe("r.screen() — registration", () => {
345
345
  const features = [
346
346
  defineFeature("app", (r) => {
347
347
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
348
- access: { openToAll: true },
348
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
349
349
  });
350
350
  r.queryHandler("foo:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
351
- access: { openToAll: true },
351
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
352
352
  });
353
353
  r.screen({
354
354
  id: "x",
@@ -370,10 +370,10 @@ describe("r.screen() — registration", () => {
370
370
  const features = [
371
371
  defineFeature("app", (r) => {
372
372
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
373
- access: { openToAll: true },
373
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
374
374
  });
375
375
  r.queryHandler("foo:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
376
- access: { openToAll: true },
376
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
377
377
  });
378
378
  r.screen({
379
379
  id: "x",
@@ -400,13 +400,15 @@ describe("r.screen() — registration", () => {
400
400
  const features = [
401
401
  defineFeature("app", (r) => {
402
402
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
403
- access: { openToAll: true },
403
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
404
404
  });
405
405
  r.queryHandler(
406
406
  "foo:list",
407
407
  z.object({ search: z.string().optional() }),
408
408
  async () => ({ rows: [], nextCursor: null }),
409
- { access: { openToAll: true } },
409
+ {
410
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
411
+ },
410
412
  );
411
413
  r.screen({
412
414
  id: "x",
@@ -433,10 +435,10 @@ describe("r.screen() — registration", () => {
433
435
  const features = [
434
436
  defineFeature("app", (r) => {
435
437
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
436
- access: { openToAll: true },
438
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
437
439
  });
438
440
  r.queryHandler("foo:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
439
- access: { openToAll: true },
441
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
440
442
  });
441
443
  r.screen({
442
444
  id: "x",
@@ -470,13 +472,15 @@ describe("r.screen() — registration", () => {
470
472
  const features = [
471
473
  defineFeature("app", (r) => {
472
474
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
473
- access: { openToAll: true },
475
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
474
476
  });
475
477
  r.queryHandler(
476
478
  "foo:list",
477
479
  z.object({ filters: z.array(z.object({})).optional() }),
478
480
  async () => ({ rows: [], nextCursor: null }),
479
- { access: { openToAll: true } },
481
+ {
482
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
483
+ },
480
484
  );
481
485
  r.screen({
482
486
  id: "x",
@@ -512,13 +516,15 @@ describe("r.screen() — registration", () => {
512
516
  const features = [
513
517
  defineFeature("app", (r) => {
514
518
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
515
- access: { openToAll: true },
519
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
516
520
  });
517
521
  r.queryHandler(
518
522
  "foo:list",
519
523
  z.object({ filters: z.array(z.object({})).optional() }),
520
524
  async () => ({ rows: [], nextCursor: null }),
521
- { access: { openToAll: true } },
525
+ {
526
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
527
+ },
522
528
  );
523
529
  r.screen({
524
530
  id: "x",
@@ -660,10 +666,10 @@ describe("r.screen() — registration", () => {
660
666
  },
661
667
  );
662
668
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
663
- access: { openToAll: true },
669
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
664
670
  });
665
671
  r.queryHandler("foo:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
666
- access: { openToAll: true },
672
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
667
673
  });
668
674
  r.screen({
669
675
  id: "x",
@@ -873,7 +879,7 @@ describe("r.screen() — registration", () => {
873
879
  const features = [
874
880
  defineFeature("app", (r) => {
875
881
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
876
- access: { openToAll: true },
882
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
877
883
  });
878
884
  r.writeHandler(
879
885
  "save",
@@ -917,7 +923,7 @@ describe("r.screen() — registration", () => {
917
923
  // here so the throw below isolates the one key this test is about.
918
924
  r.translations({ keys: { "screen:x.title": { en: "X" } } });
919
925
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
920
- access: { openToAll: true },
926
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
921
927
  });
922
928
  r.writeHandler(
923
929
  "save",
@@ -959,7 +965,7 @@ describe("r.screen() — registration", () => {
959
965
  },
960
966
  });
961
967
  r.queryHandler("foo:detail", z.object({}), async () => ({}), {
962
- access: { openToAll: true },
968
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
963
969
  });
964
970
  r.writeHandler(
965
971
  "save",
@@ -55,7 +55,8 @@ describe("validateAccessDeclarations", () => {
55
55
  });
56
56
 
57
57
  // 1b. malformed openToAll (from untyped sources) counts as invalid, not a crash.
58
- test.each([{ openToAll: false }, { openToAll: {} }])(
58
+ // `{ openToAll: true }` (deprecated) must fail boot too — it never grants access per isOpenToAllGranted.
59
+ test.each([{ openToAll: false }, { openToAll: {} }, { openToAll: true }])(
59
60
  "malformed openToAll %p throws, naming the handler",
60
61
  (access) => {
61
62
  const feature = defineFeature("notes", (r) => {
@@ -259,9 +260,9 @@ describe("validateAccessDeclarations", () => {
259
260
  expect(() => validateAccessDeclarations(feature)).toThrow(/"email"/);
260
261
  });
261
262
 
262
- test("the deprecated openToAll: true form cannot carry personalData", () => {
263
- // @ts-expect-error personalData lives only on the { reason } object form
264
- const access: AccessRule = { openToAll: true, personalData: "tenant-members" };
263
+ test("the deprecated openToAll: true form no longer type-checks against AccessRule", () => {
264
+ // @ts-expect-error `openToAll: true` was removed from AccessRule — see #2858
265
+ const access: AccessRule = { openToAll: true };
265
266
  expect(access).toBeDefined();
266
267
  });
267
268
 
@@ -52,8 +52,16 @@ describe("validateParentRefs", () => {
52
52
  const entity = joinEntity(validRef);
53
53
  const feature = defineFeature("joins", (r) => {
54
54
  r.entity("join", entity);
55
- r.queryHandler(defineEntityListHandler("join", entity, { access: { openToAll: true } }));
56
- r.queryHandler(defineEntityDetailHandler("join", entity, { access: { openToAll: true } }));
55
+ r.queryHandler(
56
+ defineEntityListHandler("join", entity, {
57
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
58
+ }),
59
+ );
60
+ r.queryHandler(
61
+ defineEntityDetailHandler("join", entity, {
62
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
63
+ }),
64
+ );
57
65
  });
58
66
  expect(() => validate(feature)).not.toThrow();
59
67
  });
@@ -65,7 +73,7 @@ describe("validateParentRefs", () => {
65
73
  name: "host:list",
66
74
  schema: z.object({}),
67
75
  handler: async () => ({ rows: [], nextCursor: null }),
68
- access: { openToAll: true },
76
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
69
77
  });
70
78
  });
71
79
  expect(() => validate(feature)).not.toThrow();
@@ -99,7 +107,11 @@ describe("validateParentRefs", () => {
99
107
  const entity = joinEntity({ ...validRef, allowedTypes: ["host"] });
100
108
  const feature = defineFeature("joins", (r) => {
101
109
  r.entity("join", entity);
102
- r.queryHandler(defineEntityListHandler("join", entity, { access: { openToAll: true } }));
110
+ r.queryHandler(
111
+ defineEntityListHandler("join", entity, {
112
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
113
+ }),
114
+ );
103
115
  });
104
116
  expect(() => validate(feature, [hosts])).not.toThrow();
105
117
  });
@@ -121,7 +133,7 @@ describe("validateParentRefs", () => {
121
133
  name: "join:list",
122
134
  schema: z.object({}),
123
135
  handler: async () => ({ rows: [], nextCursor: null }),
124
- access: { openToAll: true },
136
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
125
137
  });
126
138
  });
127
139
  expect(() => validate(feature)).toThrow(/join:list/);
@@ -134,7 +146,7 @@ describe("validateParentRefs", () => {
134
146
  name: "join:detail",
135
147
  schema: z.object({ id: z.uuid() }),
136
148
  handler: async () => null,
137
- access: { openToAll: true },
149
+ access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
138
150
  });
139
151
  });
140
152
  expect(() => validate(feature)).toThrow(/join:detail/);
@@ -88,12 +88,10 @@ function candidatePersonalFieldNames(
88
88
  return names;
89
89
  }
90
90
 
91
- // A value that is neither `true` nor an object with a non-empty string `reason`
92
- // counts as "empty" — catches malformed openToAll from untyped sources too.
91
+ // Not an object with a non-empty string `reason` (incl. deprecated `openToAll: true`) counts as "empty".
93
92
  function openToAllReasonIsEmpty(access: AccessRule): boolean {
94
93
  if (!("openToAll" in access)) return false;
95
94
  const openToAll: unknown = access.openToAll;
96
- if (openToAll === true) return false;
97
95
  if (typeof openToAll !== "object" || openToAll === null) return true;
98
96
  if (!("reason" in openToAll) || typeof openToAll.reason !== "string") return true;
99
97
  return openToAll.reason.trim().length === 0;
@@ -125,8 +123,8 @@ function validateOpenToAllReason(
125
123
  if (!openToAllReasonIsEmpty(access)) return;
126
124
  throw new Error(
127
125
  `[Feature ${feature.name}] ${kind} handler "${handlerName}" declares an invalid ` +
128
- '{ openToAll: ... } — must be `true` or { reason: "<non-empty explanation>" } ' +
129
- "for why any authenticated user may call this handler.",
126
+ '{ openToAll: ... } — must be { reason: "<non-empty explanation>" } for why any ' +
127
+ "authenticated user may call this handler. `{ openToAll: true }` is no longer accepted.",
130
128
  );
131
129
  }
132
130