@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.
- package/README.md +5 -1
- package/package.json +3 -3
- package/src/__tests__/anonymous-access.integration.test.ts +1 -1
- package/src/__tests__/error-contract.integration.test.ts +1 -1
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +16 -0
- package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +16 -0
- package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.expected.ts +12 -0
- package/src/__tests__/fixtures/migrate-open-to-all/test-nested-shorthand.input.ts +12 -0
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +7 -0
- package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +7 -0
- package/src/__tests__/full-stack.integration.test.ts +2 -2
- package/src/__tests__/migrate-open-to-all-codemod.test.ts +84 -0
- package/src/__tests__/transition-guard.integration.test.ts +7 -7
- package/src/api/__tests__/api.test.ts +2 -2
- package/src/api/__tests__/body-limit.test.ts +1 -1
- package/src/api/__tests__/pii-leak-guard.integration.test.ts +2 -2
- package/src/api/__tests__/request-locale.integration.test.ts +1 -1
- package/src/api/__tests__/server-error-logging.test.ts +1 -1
- package/src/changes.json +8 -0
- package/src/db/__tests__/list-filter-field-access.integration.test.ts +1 -1
- package/src/derivatives/__tests__/derivatives-context.integration.test.ts +2 -2
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +5 -5
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-projection-list.test.ts +17 -17
- package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +18 -18
- package/src/engine/__tests__/boot-validator-query-refs.test.ts +3 -3
- package/src/engine/__tests__/boot-validator-s0-integration.test.ts +2 -2
- package/src/engine/__tests__/boot-validator.test.ts +18 -18
- package/src/engine/__tests__/build-app-schema.test.ts +10 -6
- package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +48 -0
- package/src/engine/__tests__/build-config-feature-schema.test.ts +34 -5
- package/src/engine/__tests__/engine.test.ts +39 -25
- package/src/engine/__tests__/entity-handlers.test.ts +19 -8
- package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +2 -2
- package/src/engine/__tests__/extends-registrar.test.ts +3 -3
- package/src/engine/__tests__/feature-crud-shorthand.test.ts +9 -3
- package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +1 -1
- package/src/engine/__tests__/hook-phases.test.ts +5 -5
- package/src/engine/__tests__/lifecycle-hooks.test.ts +9 -3
- package/src/engine/__tests__/multiselect-filter.integration.test.ts +1 -1
- package/src/engine/__tests__/post-query-hook.test.ts +6 -2
- package/src/engine/__tests__/projection-detail-actions.test.ts +2 -2
- package/src/engine/__tests__/projection-detail-singleton.test.ts +3 -3
- package/src/engine/__tests__/projection-detail-tabs.test.ts +3 -3
- package/src/engine/__tests__/projection-list-filter.integration.test.ts +1 -1
- package/src/engine/__tests__/registry.test.ts +19 -13
- package/src/engine/__tests__/screen.test.ts +26 -20
- package/src/engine/boot-validator/__tests__/access-declarations.test.ts +5 -4
- package/src/engine/boot-validator/__tests__/parent-ref.test.ts +18 -6
- package/src/engine/boot-validator/access-declarations.ts +3 -5
- package/src/engine/boot-validator/nav.ts +4 -3
- package/src/engine/build-config-feature-schema.ts +9 -2
- package/src/engine/entity-handlers.ts +1 -1
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +3 -1
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +2 -2
- package/src/engine/feature-ast/__tests__/parse.test.ts +106 -4
- package/src/engine/feature-ast/__tests__/patcher.test.ts +3 -3
- package/src/engine/feature-ast/__tests__/read-optional-access-rule.test.ts +4 -4
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +5 -1
- package/src/engine/feature-ast/extractors/handlers.ts +16 -2
- package/src/engine/feature-ast/extractors/hooks.ts +25 -4
- package/src/engine/feature-ast/patcher.ts +15 -3
- package/src/engine/feature-ast/patterns.ts +4 -0
- package/src/engine/feature-ast/render.ts +3 -0
- package/src/engine/pattern-library/shared-fields.ts +7 -3
- package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +3 -3
- package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
- package/src/es-ops/__tests__/runner.integration.test.ts +2 -2
- package/src/jobs/__tests__/job-event-trigger.integration.test.ts +2 -2
- package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +2 -2
- package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
- package/src/observability/__tests__/observability.integration.test.ts +7 -7
- package/src/pipeline/__tests__/archive-stream.integration.test.ts +2 -2
- package/src/pipeline/__tests__/dispatcher.test.ts +15 -15
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +10 -10
- package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
- package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
- package/src/pipeline/__tests__/query-projection.integration.test.ts +3 -3
- package/src/scripts/codemod/migrate-open-to-all.ts +248 -0
- package/src/secrets/__tests__/contains-secret.test.ts +5 -5
- package/src/stack/__tests__/request-helper.integration.test.ts +3 -3
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
- package/src/testing/__tests__/e2e-generator.test.ts +19 -5
|
@@ -20,7 +20,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
20
20
|
test("projectionList column not in the paged query's row shape throws", () => {
|
|
21
21
|
const feature = defineFeature("catalog", (r) => {
|
|
22
22
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
23
|
-
access: { openToAll:
|
|
23
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
24
24
|
outputSchema: pagedSchema,
|
|
25
25
|
});
|
|
26
26
|
r.screen({
|
|
@@ -39,7 +39,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
39
39
|
test("projectionList column present in the row shape does not throw", () => {
|
|
40
40
|
const feature = defineFeature("catalog", (r) => {
|
|
41
41
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
42
|
-
access: { openToAll:
|
|
42
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
43
43
|
outputSchema: pagedSchema,
|
|
44
44
|
});
|
|
45
45
|
r.screen({
|
|
@@ -56,7 +56,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
56
56
|
test("projectionList column with a label is exempt as a virtual/computed column", () => {
|
|
57
57
|
const feature = defineFeature("catalog", (r) => {
|
|
58
58
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
59
|
-
access: { openToAll:
|
|
59
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
60
60
|
outputSchema: pagedSchema,
|
|
61
61
|
});
|
|
62
62
|
r.screen({
|
|
@@ -78,7 +78,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
78
78
|
test("projectionList without a declared outputSchema skips the column check entirely", () => {
|
|
79
79
|
const feature = defineFeature("catalog", (r) => {
|
|
80
80
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
81
|
-
access: { openToAll:
|
|
81
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
82
82
|
});
|
|
83
83
|
r.screen({
|
|
84
84
|
id: "items",
|
|
@@ -94,7 +94,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
94
94
|
test("projectionList outputSchema that isn't a ZodObject (e.g. a union) skips the column check", () => {
|
|
95
95
|
const feature = defineFeature("catalog", (r) => {
|
|
96
96
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
97
|
-
access: { openToAll:
|
|
97
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
98
98
|
outputSchema: z.union([pagedSchema, z.null()]),
|
|
99
99
|
});
|
|
100
100
|
r.screen({
|
|
@@ -111,11 +111,11 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
111
111
|
test("relatedList column not in the query's row shape throws", () => {
|
|
112
112
|
const feature = defineFeature("app", (r) => {
|
|
113
113
|
r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
|
|
114
|
-
access: { openToAll:
|
|
114
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
115
115
|
outputSchema: z.object({ description: z.string() }),
|
|
116
116
|
});
|
|
117
117
|
r.queryHandler("rent:payments", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
118
|
-
access: { openToAll:
|
|
118
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
119
119
|
outputSchema: z.object({
|
|
120
120
|
rows: z.array(z.object({ amount: z.number() })),
|
|
121
121
|
nextCursor: z.string().nullable(),
|
|
@@ -145,7 +145,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
145
145
|
test("projectionDetail header.title referencing an unknown field throws", () => {
|
|
146
146
|
const feature = defineFeature("app", (r) => {
|
|
147
147
|
r.queryHandler("tenant:detail", z.object({}), async () => ({ id: "1", name: "x" }), {
|
|
148
|
-
access: { openToAll:
|
|
148
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
149
149
|
outputSchema: z.object({ id: z.string(), name: z.string() }),
|
|
150
150
|
});
|
|
151
151
|
r.screen({
|
|
@@ -164,7 +164,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
164
164
|
test("projectionDetail header.status referencing an unknown field throws", () => {
|
|
165
165
|
const feature = defineFeature("app", (r) => {
|
|
166
166
|
r.queryHandler("tenant:detail", z.object({}), async () => ({ id: "1", name: "x" }), {
|
|
167
|
-
access: { openToAll:
|
|
167
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
168
168
|
outputSchema: z.object({ id: z.string(), name: z.string() }),
|
|
169
169
|
});
|
|
170
170
|
r.screen({
|
|
@@ -181,7 +181,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
181
181
|
test("projectionDetail metrics referencing an unknown field throws", () => {
|
|
182
182
|
const feature = defineFeature("app", (r) => {
|
|
183
183
|
r.queryHandler("tenant:detail", z.object({}), async () => ({ id: "1", balance: 0 }), {
|
|
184
|
-
access: { openToAll:
|
|
184
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
185
185
|
outputSchema: z.object({ id: z.string(), balance: z.number() }),
|
|
186
186
|
});
|
|
187
187
|
r.screen({
|
|
@@ -214,7 +214,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
214
214
|
z.object({}),
|
|
215
215
|
async () => ({ id: "1", name: "x", balance: 0 }),
|
|
216
216
|
{
|
|
217
|
-
access: { openToAll:
|
|
217
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
218
218
|
outputSchema: z.object({
|
|
219
219
|
id: z.string(),
|
|
220
220
|
name: z.string(),
|
|
@@ -241,7 +241,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
241
241
|
test("dashboard stat panel valueField referencing an unknown field throws", () => {
|
|
242
242
|
const feature = defineFeature("demo", (r) => {
|
|
243
243
|
r.queryHandler("open-count", z.object({}), async () => ({ value: "0" }), {
|
|
244
|
-
access: { openToAll:
|
|
244
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
245
245
|
outputSchema: z.object({ value: z.string() }),
|
|
246
246
|
});
|
|
247
247
|
r.screen({
|
|
@@ -269,7 +269,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
269
269
|
test("dashboard stat-group child deltaField referencing an unknown field throws", () => {
|
|
270
270
|
const feature = defineFeature("demo", (r) => {
|
|
271
271
|
r.queryHandler("net-worth", z.object({}), async () => ({ value: "0" }), {
|
|
272
|
-
access: { openToAll:
|
|
272
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
273
273
|
outputSchema: z.object({ value: z.string() }),
|
|
274
274
|
});
|
|
275
275
|
r.screen({
|
|
@@ -308,7 +308,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
308
308
|
test("dashboard list panel column not in the query's row shape throws", () => {
|
|
309
309
|
const feature = defineFeature("demo", (r) => {
|
|
310
310
|
r.queryHandler("next-events", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
311
|
-
access: { openToAll:
|
|
311
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
312
312
|
outputSchema: z.object({
|
|
313
313
|
rows: z.array(z.object({ title: z.string() })),
|
|
314
314
|
nextCursor: z.string().nullable(),
|
|
@@ -342,7 +342,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
342
342
|
defineQueryHandler({
|
|
343
343
|
name: "items:detail",
|
|
344
344
|
schema: z.object({ id: z.string() }),
|
|
345
|
-
access: { openToAll:
|
|
345
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
346
346
|
outputSchema: z.object({ id: z.string(), name: z.string() }),
|
|
347
347
|
handler: async () => ({ id: "1", name: "x" }),
|
|
348
348
|
}),
|
|
@@ -366,7 +366,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
366
366
|
definePagedQueryHandler({
|
|
367
367
|
name: "items:list",
|
|
368
368
|
schema: z.object({}),
|
|
369
|
-
access: { openToAll:
|
|
369
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
370
370
|
outputSchema: pagedSchema,
|
|
371
371
|
handler: async () => ({ rows: [], nextCursor: null }),
|
|
372
372
|
}),
|
|
@@ -390,7 +390,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
390
390
|
definePagedQueryHandler({
|
|
391
391
|
name: "items:list",
|
|
392
392
|
schema: z.object({}),
|
|
393
|
-
access: { openToAll:
|
|
393
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
394
394
|
// Row schema passed directly instead of wrapping it in
|
|
395
395
|
// { rows: [...], nextCursor } — the mistake fw#2493's follow-up
|
|
396
396
|
// check catches instead of silently skipping every column check.
|
|
@@ -417,7 +417,7 @@ describe("validateBoot — query output schema column refs (fw#2493)", () => {
|
|
|
417
417
|
definePagedQueryHandler({
|
|
418
418
|
name: "items:list",
|
|
419
419
|
schema: z.object({}),
|
|
420
|
-
access: { openToAll:
|
|
420
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
421
421
|
outputSchema: pagedSchema,
|
|
422
422
|
handler: async () => ({ rows: [], nextCursor: null }),
|
|
423
423
|
}),
|
|
@@ -36,7 +36,7 @@ describe("validateBoot — query QN refs (fw#2178)", () => {
|
|
|
36
36
|
test("projectionDetail relatedList section with a dead query QN throws (b)", () => {
|
|
37
37
|
const feature = defineFeature("app", (r) => {
|
|
38
38
|
r.queryHandler("rent:detail", z.object({}), async () => ({ description: "x" }), {
|
|
39
|
-
access: { openToAll:
|
|
39
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
40
40
|
});
|
|
41
41
|
r.screen({
|
|
42
42
|
id: "rent-detail",
|
|
@@ -96,7 +96,7 @@ describe("validateBoot — query QN refs (fw#2178)", () => {
|
|
|
96
96
|
test("dashboard filter optionsQuery with a dead query QN throws", () => {
|
|
97
97
|
const feature = defineFeature("demo", (r) => {
|
|
98
98
|
r.queryHandler("open-count", z.object({}), async () => ({ value: "0" }), {
|
|
99
|
-
access: { openToAll:
|
|
99
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
100
100
|
});
|
|
101
101
|
r.screen({
|
|
102
102
|
id: "overview",
|
|
@@ -132,7 +132,7 @@ describe("validateBoot — query QN refs (fw#2178)", () => {
|
|
|
132
132
|
test("a screen referencing a query registered by a different mounted feature does not throw (d)", () => {
|
|
133
133
|
const provider = defineFeature("catalog", (r) => {
|
|
134
134
|
r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
135
|
-
access: { openToAll:
|
|
135
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
136
136
|
});
|
|
137
137
|
});
|
|
138
138
|
const consumer = defineFeature("storefront", (r) => {
|
|
@@ -44,7 +44,7 @@ describe("S0 Integration — full surface stack", () => {
|
|
|
44
44
|
name: "compliance:query:for-tenant",
|
|
45
45
|
schema: z.object({}),
|
|
46
46
|
handler: async () => ({ profile: "eu-dsgvo" }) as never,
|
|
47
|
-
access: { openToAll:
|
|
47
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -101,7 +101,7 @@ describe("S0 Integration — full surface stack", () => {
|
|
|
101
101
|
name: "user:list",
|
|
102
102
|
schema: z.object({}),
|
|
103
103
|
handler: async () => ({ rows: [], nextCursor: null }) as never,
|
|
104
|
-
access: { openToAll:
|
|
104
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
r.useExtension(EXT_USER_DATA, "user", {});
|
|
@@ -42,7 +42,7 @@ function stubListHandler(
|
|
|
42
42
|
name: `${entityName}:list`,
|
|
43
43
|
schema: z.object({}),
|
|
44
44
|
handler: async () => ({ rows: [], nextCursor: null }) as never,
|
|
45
|
-
access: { openToAll:
|
|
45
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -534,7 +534,7 @@ describe("boot-validator", () => {
|
|
|
534
534
|
isSuccess: true as const,
|
|
535
535
|
data: { id: "1" },
|
|
536
536
|
}),
|
|
537
|
-
{ access: { openToAll:
|
|
537
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
538
538
|
);
|
|
539
539
|
}),
|
|
540
540
|
];
|
|
@@ -566,7 +566,7 @@ describe("boot-validator", () => {
|
|
|
566
566
|
isSuccess: true as const,
|
|
567
567
|
data: {},
|
|
568
568
|
}),
|
|
569
|
-
{ access: { openToAll:
|
|
569
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
570
570
|
);
|
|
571
571
|
r.useExtension("credit-cap", "credit");
|
|
572
572
|
});
|
|
@@ -631,7 +631,7 @@ describe("boot-validator", () => {
|
|
|
631
631
|
const features = [
|
|
632
632
|
defineFeature("a", (r) => {
|
|
633
633
|
r.queryHandler("list", z.object({}), async () => [], {
|
|
634
|
-
access: { openToAll:
|
|
634
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
635
635
|
});
|
|
636
636
|
}),
|
|
637
637
|
];
|
|
@@ -711,7 +711,7 @@ describe("boot-validator", () => {
|
|
|
711
711
|
const features = [
|
|
712
712
|
defineFeature("a", (r) => {
|
|
713
713
|
r.streamHandler("chat:complete", z.object({}), async function* () {}, {
|
|
714
|
-
access: { openToAll:
|
|
714
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
715
715
|
});
|
|
716
716
|
}),
|
|
717
717
|
];
|
|
@@ -2430,7 +2430,7 @@ describe("boot-validator", () => {
|
|
|
2430
2430
|
name: "invoice:approve",
|
|
2431
2431
|
schema: { _type: "stub" } as never,
|
|
2432
2432
|
handler: async () => ({ isSuccess: true, data: {} }) as never,
|
|
2433
|
-
access: { openToAll:
|
|
2433
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
2434
2434
|
});
|
|
2435
2435
|
r.screen({
|
|
2436
2436
|
id: "approve-invoice",
|
|
@@ -2713,14 +2713,14 @@ describe("boot-validator", () => {
|
|
|
2713
2713
|
name: "token:mint",
|
|
2714
2714
|
schema: { _type: "stub" } as never,
|
|
2715
2715
|
handler: async () => ({ isSuccess: true, data: {} }) as never,
|
|
2716
|
-
access: { openToAll:
|
|
2716
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
2717
2717
|
});
|
|
2718
2718
|
if (confirmOverride !== undefined) {
|
|
2719
2719
|
r.writeHandler({
|
|
2720
2720
|
name: "token:confirm",
|
|
2721
2721
|
schema: { _type: "stub" } as never,
|
|
2722
2722
|
handler: async () => ({ isSuccess: true, data: {} }) as never,
|
|
2723
|
-
access: { openToAll:
|
|
2723
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
2724
2724
|
});
|
|
2725
2725
|
}
|
|
2726
2726
|
r.screen({
|
|
@@ -4177,7 +4177,7 @@ describe("boot-validator", () => {
|
|
|
4177
4177
|
}),
|
|
4178
4178
|
);
|
|
4179
4179
|
r.queryHandler("products", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
4180
|
-
access: { openToAll:
|
|
4180
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
4181
4181
|
});
|
|
4182
4182
|
r.screen({
|
|
4183
4183
|
id: "product-projection",
|
|
@@ -4207,7 +4207,7 @@ describe("boot-validator", () => {
|
|
|
4207
4207
|
}),
|
|
4208
4208
|
);
|
|
4209
4209
|
r.queryHandler("products", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
4210
|
-
access: { openToAll:
|
|
4210
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
4211
4211
|
});
|
|
4212
4212
|
r.screen({
|
|
4213
4213
|
id: "product-projection",
|
|
@@ -4426,13 +4426,13 @@ describe("boot-validator", () => {
|
|
|
4426
4426
|
test("projectionDetail relatedList defaultSort.field sortable → kein Throw", () => {
|
|
4427
4427
|
const feature = defineFeature("shop", (r) => {
|
|
4428
4428
|
r.queryHandler("order-detail", z.object({}), async () => ({ id: "o1" }), {
|
|
4429
|
-
access: { openToAll:
|
|
4429
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
4430
4430
|
});
|
|
4431
4431
|
r.queryHandler(
|
|
4432
4432
|
"order-positions",
|
|
4433
4433
|
z.object({}),
|
|
4434
4434
|
async () => ({ rows: [], nextCursor: null }),
|
|
4435
|
-
{ access: { openToAll:
|
|
4435
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
4436
4436
|
);
|
|
4437
4437
|
r.screen({
|
|
4438
4438
|
id: "order-detail",
|
|
@@ -4993,7 +4993,7 @@ describe("boot-validator", () => {
|
|
|
4993
4993
|
const targetId = opts.targetId ?? "restock-form";
|
|
4994
4994
|
return defineFeature("shop", (r) => {
|
|
4995
4995
|
r.queryHandler("products", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
4996
|
-
access: { openToAll:
|
|
4996
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
4997
4997
|
});
|
|
4998
4998
|
r.writeHandler(
|
|
4999
4999
|
"restock",
|
|
@@ -5223,7 +5223,7 @@ describe("boot-validator — config key backing × scope", () => {
|
|
|
5223
5223
|
],
|
|
5224
5224
|
});
|
|
5225
5225
|
r.queryHandler("count", z.object({}), async () => ({ total: 0 }), {
|
|
5226
|
-
access: { openToAll:
|
|
5226
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
5227
5227
|
});
|
|
5228
5228
|
r.screen({
|
|
5229
5229
|
id: "product-dashboard",
|
|
@@ -5268,7 +5268,7 @@ describe("boot-validator — config key backing × scope", () => {
|
|
|
5268
5268
|
],
|
|
5269
5269
|
});
|
|
5270
5270
|
r.queryHandler("count", z.object({}), async () => ({ total: 0 }), {
|
|
5271
|
-
access: { openToAll:
|
|
5271
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
5272
5272
|
});
|
|
5273
5273
|
r.screen({
|
|
5274
5274
|
id: "product-dashboard",
|
|
@@ -5317,7 +5317,7 @@ describe("boot-validator — config key backing × scope", () => {
|
|
|
5317
5317
|
],
|
|
5318
5318
|
});
|
|
5319
5319
|
r.queryHandler("count", z.object({}), async () => ({ total: 0 }), {
|
|
5320
|
-
access: { openToAll:
|
|
5320
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
5321
5321
|
});
|
|
5322
5322
|
r.screen({
|
|
5323
5323
|
id: "product-dashboard",
|
|
@@ -5542,7 +5542,7 @@ describe("boot-validator — config key backing × scope", () => {
|
|
|
5542
5542
|
}),
|
|
5543
5543
|
);
|
|
5544
5544
|
r.queryHandler("products", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
5545
|
-
access: { openToAll:
|
|
5545
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
5546
5546
|
});
|
|
5547
5547
|
r.screen({
|
|
5548
5548
|
id: "product-projection",
|
|
@@ -5624,7 +5624,7 @@ describe("boot-validator — config key backing × scope", () => {
|
|
|
5624
5624
|
"create",
|
|
5625
5625
|
z.object({ name: z.string() }),
|
|
5626
5626
|
async () => ({ isSuccess: true as const, data: { id: "1" } }),
|
|
5627
|
-
{ access: { openToAll:
|
|
5627
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
5628
5628
|
);
|
|
5629
5629
|
r.screen({
|
|
5630
5630
|
id: "product-list",
|
|
@@ -209,7 +209,7 @@ describe("buildAppSchema", () => {
|
|
|
209
209
|
r.workspace({
|
|
210
210
|
id: "admin",
|
|
211
211
|
label: "Admin",
|
|
212
|
-
access: { openToAll:
|
|
212
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
213
213
|
nav: ["orders:nav:list", "fleet:nav:vehicles"],
|
|
214
214
|
default: true,
|
|
215
215
|
});
|
|
@@ -832,7 +832,11 @@ describe("buildAppSchema", () => {
|
|
|
832
832
|
const f = defineFeature("ent", (r) => {
|
|
833
833
|
r.entity("thing", entity);
|
|
834
834
|
r.nav({ id: "n", label: "N" });
|
|
835
|
-
r.workspace({
|
|
835
|
+
r.workspace({
|
|
836
|
+
id: "ws",
|
|
837
|
+
label: "Ws",
|
|
838
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
839
|
+
});
|
|
836
840
|
});
|
|
837
841
|
const app = buildAppSchema(createRegistry([f]));
|
|
838
842
|
const json = JSON.stringify(app);
|
|
@@ -980,7 +984,7 @@ describe("buildAppSchema", () => {
|
|
|
980
984
|
cursor: z.string().optional(),
|
|
981
985
|
}),
|
|
982
986
|
async () => ({ rows: [], nextCursor: null }),
|
|
983
|
-
{ access: { openToAll:
|
|
987
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
984
988
|
);
|
|
985
989
|
r.screen({
|
|
986
990
|
id: "schedule-list",
|
|
@@ -1000,7 +1004,7 @@ describe("buildAppSchema", () => {
|
|
|
1000
1004
|
test("projectionList: a query schema without search/sort/cursor derives no capability", () => {
|
|
1001
1005
|
const f = defineFeature("ledger", (r) => {
|
|
1002
1006
|
r.queryHandler("schedule:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
|
|
1003
|
-
access: { openToAll:
|
|
1007
|
+
access: { openToAll: { reason: "test handler callable by any signed-in test user" } },
|
|
1004
1008
|
});
|
|
1005
1009
|
r.screen({
|
|
1006
1010
|
id: "schedule-list",
|
|
@@ -1023,7 +1027,7 @@ describe("buildAppSchema", () => {
|
|
|
1023
1027
|
"schedule:list",
|
|
1024
1028
|
z.union([z.object({ a: z.string() }), z.object({ b: z.string() })]),
|
|
1025
1029
|
async () => ({ rows: [], nextCursor: null }),
|
|
1026
|
-
{ access: { openToAll:
|
|
1030
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
1027
1031
|
);
|
|
1028
1032
|
r.screen({
|
|
1029
1033
|
id: "schedule-list",
|
|
@@ -1052,7 +1056,7 @@ describe("buildAppSchema", () => {
|
|
|
1052
1056
|
rows: [],
|
|
1053
1057
|
nextCursor: null,
|
|
1054
1058
|
}),
|
|
1055
|
-
{ access: { openToAll:
|
|
1059
|
+
{ access: { openToAll: { reason: "test handler callable by any signed-in test user" } } },
|
|
1056
1060
|
);
|
|
1057
1061
|
r.screen({
|
|
1058
1062
|
id: "schedule-list",
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { buildConfigFeatureSchema } from "../build-config-feature-schema";
|
|
4
|
+
import { createTenantConfig } from "../config-helpers";
|
|
4
5
|
import { defineFeature } from "../define-feature";
|
|
5
6
|
import { createRegistry } from "../registry";
|
|
7
|
+
import type { AccessRule } from "../types/handlers";
|
|
6
8
|
import type { NavDefinition } from "../types/nav";
|
|
7
9
|
import type { ScreenDefinition, SecretsEditScreenDefinition } from "../types/screen";
|
|
8
10
|
|
|
@@ -114,6 +116,52 @@ describe("buildConfigFeatureSchema — secrets derivation", () => {
|
|
|
114
116
|
expect(navById(schema, "audience-tenant")?.access).toEqual({ roles: customRoles });
|
|
115
117
|
});
|
|
116
118
|
|
|
119
|
+
test("audience nav union keeps a granted openToAll's own reason, not the generic ALL_ROLE reason", () => {
|
|
120
|
+
const customReason = "billing key rotation is safe for any signed-in tenant member";
|
|
121
|
+
const secretsOpenToAll = defineFeature("secrets", (r) => {
|
|
122
|
+
r.writeHandler(
|
|
123
|
+
"set",
|
|
124
|
+
z.object({ key: z.string(), value: z.string() }),
|
|
125
|
+
async () => ({ isSuccess: true, data: null }),
|
|
126
|
+
{ access: { openToAll: { reason: customReason } } },
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
const billing = defineFeature("billing", (r) => {
|
|
130
|
+
r.config({
|
|
131
|
+
keys: { apiKey: createTenantConfig("text", { mask: { title: "billing.api-key" } }) },
|
|
132
|
+
});
|
|
133
|
+
r.secret("apiKey", { label: { en: "Billing API Key" }, scope: "tenant" });
|
|
134
|
+
});
|
|
135
|
+
const schema = buildConfigFeatureSchema(createRegistry([secretsOpenToAll, billing]));
|
|
136
|
+
|
|
137
|
+
expect(navById(schema, "audience-tenant")?.access).toEqual({
|
|
138
|
+
openToAll: { reason: customReason },
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("audience nav union ignores a malformed openToAll — falls back to the role union", () => {
|
|
143
|
+
const secretsMalformedOpenToAll = defineFeature("secrets", (r) => {
|
|
144
|
+
r.writeHandler(
|
|
145
|
+
"set",
|
|
146
|
+
z.object({ key: z.string(), value: z.string() }),
|
|
147
|
+
async () => ({ isSuccess: true, data: null }),
|
|
148
|
+
// @cast-boundary test — simulates JSON/Designer input that doesn't match the static union
|
|
149
|
+
{ access: { openToAll: {} } as unknown as AccessRule },
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
const billing = defineFeature("billing", (r) => {
|
|
153
|
+
r.config({
|
|
154
|
+
keys: { apiKey: createTenantConfig("text", { mask: { title: "billing.api-key" } }) },
|
|
155
|
+
});
|
|
156
|
+
r.secret("apiKey", { label: { en: "Billing API Key" }, scope: "tenant" });
|
|
157
|
+
});
|
|
158
|
+
const schema = buildConfigFeatureSchema(createRegistry([secretsMalformedOpenToAll, billing]));
|
|
159
|
+
|
|
160
|
+
expect(navById(schema, "audience-tenant")?.access).toEqual({
|
|
161
|
+
roles: ["TenantAdmin", "Admin", "SystemAdmin"],
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
117
165
|
test("declared secrets without a mounted secrets feature yield no secretsEdit screen and no secrets nav", () => {
|
|
118
166
|
const stripe = defineFeature("stripe", (r) => {
|
|
119
167
|
r.secret("apiKey", { label: { en: "Stripe API Key" }, scope: "tenant" });
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "../config-helpers";
|
|
9
9
|
import { defineFeature } from "../define-feature";
|
|
10
10
|
import { createRegistry } from "../registry";
|
|
11
|
+
import { isOpenToAllGranted } from "../types/handlers";
|
|
11
12
|
import type { NavDefinition } from "../types/nav";
|
|
12
13
|
import type { ConfigEditScreenDefinition, ScreenDefinition } from "../types/screen";
|
|
13
14
|
|
|
@@ -265,7 +266,15 @@ describe("buildConfigFeatureSchema — access + workspace", () => {
|
|
|
265
266
|
roles: ["TenantAdmin", "Admin", "SystemAdmin"],
|
|
266
267
|
});
|
|
267
268
|
// digest is createUserConfig → write access.all (["all"]) → openToAll
|
|
268
|
-
|
|
269
|
+
const notifyUserAccess = configScreen("notify-user").access;
|
|
270
|
+
expect(notifyUserAccess).toEqual({
|
|
271
|
+
openToAll: {
|
|
272
|
+
reason:
|
|
273
|
+
'config key declares the "all" role, so every signed-in user of the tenant may read/write it',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
// The synthesized reason must actually grant access, not just look configured.
|
|
277
|
+
expect(notifyUserAccess !== undefined && isOpenToAllGranted(notifyUserAccess)).toBe(true);
|
|
269
278
|
});
|
|
270
279
|
|
|
271
280
|
test("a screen mixing an openToAll key with role-restricted keys collapses to openToAll", () => {
|
|
@@ -283,8 +292,18 @@ describe("buildConfigFeatureSchema — access + workspace", () => {
|
|
|
283
292
|
const screen = out.screens.find((s) => s.id === "mixedwrite-tenant");
|
|
284
293
|
if (screen?.type !== "configEdit")
|
|
285
294
|
throw new Error('expected configEdit screen "mixedwrite-tenant"');
|
|
286
|
-
expect(screen.access).toEqual({
|
|
287
|
-
|
|
295
|
+
expect(screen.access).toEqual({
|
|
296
|
+
openToAll: {
|
|
297
|
+
reason:
|
|
298
|
+
'config key declares the "all" role, so every signed-in user of the tenant may read/write it',
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
expect(out.navs.find((n) => n.id === "audience-tenant")?.access).toEqual({
|
|
302
|
+
openToAll: {
|
|
303
|
+
reason:
|
|
304
|
+
'config key declares the "all" role, so every signed-in user of the tenant may read/write it',
|
|
305
|
+
},
|
|
306
|
+
});
|
|
288
307
|
});
|
|
289
308
|
|
|
290
309
|
test("openToAll collapse holds regardless of key declaration order (516/2)", () => {
|
|
@@ -305,7 +324,12 @@ describe("buildConfigFeatureSchema — access + workspace", () => {
|
|
|
305
324
|
const screen = out.screens.find((s) => s.id === "mixedwritereversed-tenant");
|
|
306
325
|
if (screen?.type !== "configEdit")
|
|
307
326
|
throw new Error('expected configEdit screen "mixedwritereversed-tenant"');
|
|
308
|
-
expect(screen.access).toEqual({
|
|
327
|
+
expect(screen.access).toEqual({
|
|
328
|
+
openToAll: {
|
|
329
|
+
reason:
|
|
330
|
+
'config key declares the "all" role, so every signed-in user of the tenant may read/write it',
|
|
331
|
+
},
|
|
332
|
+
});
|
|
309
333
|
});
|
|
310
334
|
|
|
311
335
|
test("returns empty (no workspace) when no key opts into the hub via mask", () => {
|
|
@@ -343,7 +367,12 @@ describe("buildConfigFeatureSchema — access + workspace", () => {
|
|
|
343
367
|
test("workspace access is the union of write roles across all hub keys", () => {
|
|
344
368
|
// billing/notify tenant keys → admin write; notify-user digest → write all.
|
|
345
369
|
// Any `all`-writable key collapses the whole switcher entry to openToAll.
|
|
346
|
-
expect(schema.workspace?.definition.access).toEqual({
|
|
370
|
+
expect(schema.workspace?.definition.access).toEqual({
|
|
371
|
+
openToAll: {
|
|
372
|
+
reason:
|
|
373
|
+
'config key declares the "all" role, so every signed-in user of the tenant may read/write it',
|
|
374
|
+
},
|
|
375
|
+
});
|
|
347
376
|
});
|
|
348
377
|
|
|
349
378
|
test("workspace access stays role-gated when no hub key is world-writable", () => {
|