@fuzdev/fuz_app 0.140.0 → 0.141.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 (182) hide show
  1. package/dist/actions/CLAUDE.md +62 -17
  2. package/dist/actions/action_rpc.d.ts +5 -1
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +9 -1
  5. package/dist/actions/connection_closer.d.ts +25 -2
  6. package/dist/actions/connection_closer.d.ts.map +1 -1
  7. package/dist/actions/connection_closer.js +8 -1
  8. package/dist/actions/register_action_ws.d.ts +42 -6
  9. package/dist/actions/register_action_ws.d.ts.map +1 -1
  10. package/dist/actions/register_action_ws.js +87 -19
  11. package/dist/actions/register_ws_endpoint.d.ts +15 -6
  12. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  13. package/dist/actions/register_ws_endpoint.js +5 -2
  14. package/dist/actions/socket.svelte.d.ts +19 -4
  15. package/dist/actions/socket.svelte.d.ts.map +1 -1
  16. package/dist/actions/socket.svelte.js +31 -4
  17. package/dist/actions/transports.d.ts +30 -1
  18. package/dist/actions/transports.d.ts.map +1 -1
  19. package/dist/actions/transports.js +30 -1
  20. package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
  21. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  22. package/dist/actions/transports_ws_auth_guard.js +24 -10
  23. package/dist/actions/transports_ws_backend.d.ts +51 -5
  24. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  25. package/dist/actions/transports_ws_backend.js +62 -9
  26. package/dist/actions/ws_endpoint_spec.d.ts +11 -10
  27. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  28. package/dist/auth/CLAUDE.md +11 -9
  29. package/dist/auth/account_route_schema.d.ts +1 -0
  30. package/dist/auth/account_route_schema.d.ts.map +1 -1
  31. package/dist/auth/account_route_schema.js +4 -0
  32. package/dist/auth/api_token.d.ts +20 -0
  33. package/dist/auth/api_token.d.ts.map +1 -1
  34. package/dist/auth/api_token.js +21 -1
  35. package/dist/auth/api_token_queries.d.ts +22 -4
  36. package/dist/auth/api_token_queries.d.ts.map +1 -1
  37. package/dist/auth/api_token_queries.js +11 -6
  38. package/dist/auth/audit_log_routes.d.ts +10 -0
  39. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  40. package/dist/auth/audit_log_routes.js +20 -0
  41. package/dist/auth/audit_log_schema.d.ts +30 -5
  42. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  43. package/dist/auth/audit_log_schema.js +30 -0
  44. package/dist/auth/bearer_auth.d.ts +4 -2
  45. package/dist/auth/bearer_auth.d.ts.map +1 -1
  46. package/dist/auth/bearer_auth.js +19 -8
  47. package/dist/auth/bootstrap_route_schema.d.ts +1 -0
  48. package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
  49. package/dist/auth/bootstrap_route_schema.js +1 -0
  50. package/dist/auth/cleanup.d.ts +9 -9
  51. package/dist/auth/cleanup.js +9 -9
  52. package/dist/auth/credential_deadline.d.ts +95 -0
  53. package/dist/auth/credential_deadline.d.ts.map +1 -0
  54. package/dist/auth/credential_deadline.js +139 -0
  55. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  56. package/dist/auth/daemon_token_middleware.js +4 -1
  57. package/dist/auth/middleware.d.ts +90 -12
  58. package/dist/auth/middleware.d.ts.map +1 -1
  59. package/dist/auth/middleware.js +115 -16
  60. package/dist/auth/request_context.d.ts +4 -2
  61. package/dist/auth/request_context.d.ts.map +1 -1
  62. package/dist/auth/request_context.js +14 -3
  63. package/dist/auth/resolved_auth.d.ts +13 -2
  64. package/dist/auth/resolved_auth.d.ts.map +1 -1
  65. package/dist/auth/resolved_auth.js +5 -4
  66. package/dist/auth/session_cookie.d.ts +4 -0
  67. package/dist/auth/session_cookie.d.ts.map +1 -1
  68. package/dist/auth/session_cookie.js +3 -2
  69. package/dist/auth/session_middleware.d.ts +1 -1
  70. package/dist/auth/session_middleware.d.ts.map +1 -1
  71. package/dist/auth/session_middleware.js +4 -1
  72. package/dist/auth/session_queries.d.ts +19 -2
  73. package/dist/auth/session_queries.d.ts.map +1 -1
  74. package/dist/auth/session_queries.js +8 -2
  75. package/dist/auth/signup_route_schema.d.ts.map +1 -1
  76. package/dist/auth/signup_route_schema.js +1 -0
  77. package/dist/hono_context.d.ts +23 -0
  78. package/dist/hono_context.d.ts.map +1 -1
  79. package/dist/hono_context.js +19 -0
  80. package/dist/http/CLAUDE.md +41 -15
  81. package/dist/http/middleware_spec.d.ts +16 -0
  82. package/dist/http/middleware_spec.d.ts.map +1 -1
  83. package/dist/http/origin.d.ts +113 -18
  84. package/dist/http/origin.d.ts.map +1 -1
  85. package/dist/http/origin.js +247 -127
  86. package/dist/http/route_spec.d.ts +21 -0
  87. package/dist/http/route_spec.d.ts.map +1 -1
  88. package/dist/http/schema_helpers.d.ts +22 -0
  89. package/dist/http/schema_helpers.d.ts.map +1 -1
  90. package/dist/http/schema_helpers.js +24 -0
  91. package/dist/http/surface.d.ts +6 -1
  92. package/dist/http/surface.d.ts.map +1 -1
  93. package/dist/http/surface.js +12 -5
  94. package/dist/realtime/sse_auth_guard.d.ts +22 -6
  95. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  96. package/dist/realtime/sse_auth_guard.js +31 -12
  97. package/dist/realtime/subscriber_registry.d.ts +10 -1
  98. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  99. package/dist/realtime/subscriber_registry.js +10 -1
  100. package/dist/server/app_server.d.ts +20 -7
  101. package/dist/server/app_server.d.ts.map +1 -1
  102. package/dist/server/app_server.js +8 -3
  103. package/dist/server/env.d.ts +2 -1
  104. package/dist/server/env.d.ts.map +1 -1
  105. package/dist/server/env.js +7 -17
  106. package/dist/server/serve_fact_route.d.ts.map +1 -1
  107. package/dist/server/serve_fact_route.js +1 -0
  108. package/dist/testing/CLAUDE.md +154 -22
  109. package/dist/testing/adversarial_headers.d.ts.map +1 -1
  110. package/dist/testing/adversarial_headers.js +13 -6
  111. package/dist/testing/connection_closer_helpers.d.ts +6 -1
  112. package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
  113. package/dist/testing/connection_closer_helpers.js +4 -0
  114. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  115. package/dist/testing/cross_backend/body_size.js +14 -65
  116. package/dist/testing/cross_backend/capabilities.d.ts +26 -2
  117. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  118. package/dist/testing/cross_backend/capabilities.js +9 -3
  119. package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
  120. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  121. package/dist/testing/cross_backend/conformance_case.js +6 -0
  122. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  123. package/dist/testing/cross_backend/conformance_table.js +20 -19
  124. package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
  125. package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
  126. package/dist/testing/cross_backend/credential_deadline.js +185 -0
  127. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/default_backend_configs.js +13 -2
  129. package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
  130. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/full_spine_mount.js +7 -1
  132. package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
  133. package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
  134. package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
  135. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/origin.js +6 -26
  137. package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
  138. package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
  139. package/dist/testing/cross_backend/origin_conformance.js +136 -0
  140. package/dist/testing/cross_backend/origin_conformance.json +1679 -0
  141. package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
  142. package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
  143. package/dist/testing/cross_backend/origin_scope.js +140 -0
  144. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
  145. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
  146. package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
  147. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
  148. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
  150. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  151. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -4
  152. package/dist/testing/cross_backend/spine_surface_constants.d.ts +19 -0
  153. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  154. package/dist/testing/cross_backend/spine_surface_constants.js +22 -0
  155. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  156. package/dist/testing/cross_backend/testing_backdoor.js +20 -7
  157. package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
  158. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
  160. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  161. package/dist/testing/cross_backend/token_scope_surface.js +25 -21
  162. package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
  163. package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
  164. package/dist/testing/cross_backend/transport_composition.js +364 -0
  165. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  166. package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
  167. package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
  168. package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
  169. package/dist/testing/cross_backend/ws_role_gated.js +142 -0
  170. package/dist/testing/entities.d.ts +23 -0
  171. package/dist/testing/entities.d.ts.map +1 -1
  172. package/dist/testing/entities.js +37 -0
  173. package/dist/testing/integration.d.ts.map +1 -1
  174. package/dist/testing/integration.js +3 -1
  175. package/dist/testing/middleware.d.ts +10 -0
  176. package/dist/testing/middleware.d.ts.map +1 -1
  177. package/dist/testing/middleware.js +31 -7
  178. package/dist/testing/stubs.d.ts +16 -3
  179. package/dist/testing/stubs.d.ts.map +1 -1
  180. package/dist/testing/stubs.js +20 -7
  181. package/dist/testing/surface_invariants.js +2 -2
  182. package/package.json +1 -1
@@ -0,0 +1,152 @@
1
+ import '../assert_dev_env.js';
2
+ import { z } from 'zod';
3
+ import type { RpcPathCrossSuiteOptions } from './setup.js';
4
+ import { TestingOriginCheckOutput, type TestingOriginCheckInput } from './testing_reset_actions.js';
5
+ /** One expected refusal of a parse row. */
6
+ export declare const OriginConformanceError: z.ZodObject<{
7
+ index: z.ZodOptional<z.ZodNumber>;
8
+ kind: z.ZodEnum<{
9
+ empty_entry: "empty_entry";
10
+ not_printable_ascii: "not_printable_ascii";
11
+ missing_scheme: "missing_scheme";
12
+ unsupported_scheme: "unsupported_scheme";
13
+ has_path: "has_path";
14
+ has_userinfo: "has_userinfo";
15
+ missing_host: "missing_host";
16
+ invalid_host_char: "invalid_host_char";
17
+ unbracketed_ipv6: "unbracketed_ipv6";
18
+ partial_wildcard: "partial_wildcard";
19
+ non_canonical_ipv4: "non_canonical_ipv4";
20
+ unclosed_ipv6: "unclosed_ipv6";
21
+ invalid_ipv6: "invalid_ipv6";
22
+ invalid_after_ipv6: "invalid_after_ipv6";
23
+ invalid_port: "invalid_port";
24
+ default_port: "default_port";
25
+ no_origins: "no_origins";
26
+ }>;
27
+ }, z.core.$strict>;
28
+ export type OriginConformanceError = z.infer<typeof OriginConformanceError>;
29
+ /** A parse row: the value parses (`ok: true`), or is refused with `errors`. */
30
+ export declare const OriginConformanceParseRow: z.ZodUnion<readonly [z.ZodObject<{
31
+ raw: z.ZodString;
32
+ ok: z.ZodLiteral<true>;
33
+ note: z.ZodOptional<z.ZodString>;
34
+ }, z.core.$strict>, z.ZodObject<{
35
+ raw: z.ZodString;
36
+ errors: z.ZodArray<z.ZodObject<{
37
+ index: z.ZodOptional<z.ZodNumber>;
38
+ kind: z.ZodEnum<{
39
+ empty_entry: "empty_entry";
40
+ not_printable_ascii: "not_printable_ascii";
41
+ missing_scheme: "missing_scheme";
42
+ unsupported_scheme: "unsupported_scheme";
43
+ has_path: "has_path";
44
+ has_userinfo: "has_userinfo";
45
+ missing_host: "missing_host";
46
+ invalid_host_char: "invalid_host_char";
47
+ unbracketed_ipv6: "unbracketed_ipv6";
48
+ partial_wildcard: "partial_wildcard";
49
+ non_canonical_ipv4: "non_canonical_ipv4";
50
+ unclosed_ipv6: "unclosed_ipv6";
51
+ invalid_ipv6: "invalid_ipv6";
52
+ invalid_after_ipv6: "invalid_after_ipv6";
53
+ invalid_port: "invalid_port";
54
+ default_port: "default_port";
55
+ no_origins: "no_origins";
56
+ }>;
57
+ }, z.core.$strict>>;
58
+ note: z.ZodOptional<z.ZodString>;
59
+ }, z.core.$strict>]>;
60
+ export type OriginConformanceParseRow = z.infer<typeof OriginConformanceParseRow>;
61
+ /** A match row: a valid allowlist, one `Origin` value or `null`, and the answer. */
62
+ export declare const OriginConformanceMatchRow: z.ZodObject<{
63
+ patterns: z.ZodString;
64
+ origin: z.ZodNullable<z.ZodString>;
65
+ allowed: z.ZodBoolean;
66
+ note: z.ZodOptional<z.ZodString>;
67
+ }, z.core.$strict>;
68
+ export type OriginConformanceMatchRow = z.infer<typeof OriginConformanceMatchRow>;
69
+ /** The conformance table. */
70
+ export declare const OriginConformance: z.ZodObject<{
71
+ parse: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
72
+ raw: z.ZodString;
73
+ ok: z.ZodLiteral<true>;
74
+ note: z.ZodOptional<z.ZodString>;
75
+ }, z.core.$strict>, z.ZodObject<{
76
+ raw: z.ZodString;
77
+ errors: z.ZodArray<z.ZodObject<{
78
+ index: z.ZodOptional<z.ZodNumber>;
79
+ kind: z.ZodEnum<{
80
+ empty_entry: "empty_entry";
81
+ not_printable_ascii: "not_printable_ascii";
82
+ missing_scheme: "missing_scheme";
83
+ unsupported_scheme: "unsupported_scheme";
84
+ has_path: "has_path";
85
+ has_userinfo: "has_userinfo";
86
+ missing_host: "missing_host";
87
+ invalid_host_char: "invalid_host_char";
88
+ unbracketed_ipv6: "unbracketed_ipv6";
89
+ partial_wildcard: "partial_wildcard";
90
+ non_canonical_ipv4: "non_canonical_ipv4";
91
+ unclosed_ipv6: "unclosed_ipv6";
92
+ invalid_ipv6: "invalid_ipv6";
93
+ invalid_after_ipv6: "invalid_after_ipv6";
94
+ invalid_port: "invalid_port";
95
+ default_port: "default_port";
96
+ no_origins: "no_origins";
97
+ }>;
98
+ }, z.core.$strict>>;
99
+ note: z.ZodOptional<z.ZodString>;
100
+ }, z.core.$strict>]>>;
101
+ match: z.ZodArray<z.ZodObject<{
102
+ patterns: z.ZodString;
103
+ origin: z.ZodNullable<z.ZodString>;
104
+ allowed: z.ZodBoolean;
105
+ note: z.ZodOptional<z.ZodString>;
106
+ }, z.core.$strict>>;
107
+ }, z.core.$strict>;
108
+ export type OriginConformance = z.infer<typeof OriginConformance>;
109
+ /** The committed table, resolved relative to this module. */
110
+ export declare const ORIGIN_CONFORMANCE_URL: URL;
111
+ /**
112
+ * Read and validate the committed conformance table.
113
+ *
114
+ * @returns the parsed table
115
+ * @throws Error when the file doesn't match `OriginConformance`
116
+ */
117
+ export declare const load_origin_conformance: () => OriginConformance;
118
+ /** One table row as the action input that checks it. */
119
+ export interface OriginConformanceCheck {
120
+ /** A short label naming the row, for a failure message. */
121
+ readonly label: string;
122
+ readonly input: TestingOriginCheckInput;
123
+ readonly expected: TestingOriginCheckOutput;
124
+ }
125
+ /**
126
+ * Flatten the table into action inputs and the answers they must get. A parse
127
+ * row sends a `null` origin, so a value that parses answers
128
+ * `{ok: true, allowed: true}`.
129
+ *
130
+ * @param table - the conformance table
131
+ * @returns one check per row, parse rows first
132
+ */
133
+ export declare const to_origin_conformance_checks: (table: OriginConformance) => Array<OriginConformanceCheck>;
134
+ /**
135
+ * Compare each check's answer with its expectation and fail once, listing
136
+ * every row that disagrees.
137
+ *
138
+ * @param checks - the table's checks
139
+ * @param answer - the answer each check got, in order
140
+ * @param backend - the name the failure message gives the answerer
141
+ */
142
+ export declare const assert_origin_conformance: (checks: ReadonlyArray<OriginConformanceCheck>, answer: ReadonlyArray<unknown>, backend: string) => void;
143
+ /** Options for `describe_origin_conformance_cross_tests`. */
144
+ export type OriginConformanceCrossTestOptions = RpcPathCrossSuiteOptions;
145
+ /**
146
+ * Register the origin conformance suite: every row of the committed table,
147
+ * sent to the backend's `_testing_origin_check` action over the keeper's
148
+ * daemon-token channel, must get the answer the table names. The action is on
149
+ * every spine test binary, so the suite is ungated.
150
+ */
151
+ export declare const describe_origin_conformance_cross_tests: (options: OriginConformanceCrossTestOptions) => void;
152
+ //# sourceMappingURL=origin_conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin_conformance.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/origin_conformance.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0B9B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EACN,wBAAwB,EAExB,KAAK,uBAAuB,EAC5B,MAAM,4BAA4B,CAAC;AAEpC,2CAA2C;AAC3C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;kBAGjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAOpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,oFAAoF;AACpF,eAAO,MAAM,yBAAyB;;;;;kBAKpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,6BAA6B;AAC7B,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAG5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,6DAA6D;AAC7D,eAAO,MAAM,sBAAsB,EAAE,GAA2D,CAAC;AAEjG;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAAO,iBACuC,CAAC;AAEnF,wDAAwD;AACxD,MAAM,WAAW,sBAAsB;IACtC,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,iBAAiB,KACtB,KAAK,CAAC,sBAAsB,CAc9B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GACrC,QAAQ,aAAa,CAAC,sBAAsB,CAAC,EAC7C,QAAQ,aAAa,CAAC,OAAO,CAAC,EAC9B,SAAS,MAAM,KACb,IAiBF,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,iCAAiC,GAAG,wBAAwB,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,uCAAuC,GACnD,SAAS,iCAAiC,KACxC,IA4BF,CAAC"}
@@ -0,0 +1,136 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * The origin allowlist conformance table, and the cross-backend suite that
4
+ * runs it against each backend over the wire.
5
+ *
6
+ * `origin_conformance.json` (beside this module) is the one statement of the
7
+ * allowlist grammar and matcher both spines implement. It holds two tables:
8
+ *
9
+ * - **parse** rows — `{raw, ok: true}` or `{raw, errors: [{index?, kind}]}`:
10
+ * an allowlist value (as `FUZ_ALLOWED_ORIGINS` would carry it) and whether
11
+ * it parses, or every refusal with its 1-based entry position (absent for a
12
+ * list-level refusal) and its `AllowedOriginsErrorKind`.
13
+ * - **match** rows — `{patterns, origin, allowed}`: a valid allowlist, one
14
+ * `Origin` value (`null` for none), and the request-level answer.
15
+ *
16
+ * A row may carry a `note` for the reader. The TS unit test reads the table
17
+ * directly; `describe_origin_conformance_cross_tests` sends every row to a
18
+ * backend's `_testing_origin_check` action and compares the answer, so the
19
+ * Rust spine is held to the same rows without reading this file.
20
+ *
21
+ * @module
22
+ */
23
+ import { readFileSync } from 'node:fs';
24
+ import { assert, describe, test } from 'vitest';
25
+ import { z } from 'zod';
26
+ import { AllowedOriginsErrorKind } from '../../http/origin.js';
27
+ import { cross_rpc_call } from './cell_cross_helpers.js';
28
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
29
+ import { TestingOriginCheckOutput, testing_origin_check_action_spec } from './testing_reset_actions.js';
30
+ /** One expected refusal of a parse row. */
31
+ export const OriginConformanceError = z.strictObject({
32
+ index: z.number().int().min(1).optional(),
33
+ kind: AllowedOriginsErrorKind
34
+ });
35
+ /** A parse row: the value parses (`ok: true`), or is refused with `errors`. */
36
+ export const OriginConformanceParseRow = z.union([
37
+ z.strictObject({ raw: z.string(), ok: z.literal(true), note: z.string().optional() }),
38
+ z.strictObject({
39
+ raw: z.string(),
40
+ errors: z.array(OriginConformanceError).min(1),
41
+ note: z.string().optional()
42
+ })
43
+ ]);
44
+ /** A match row: a valid allowlist, one `Origin` value or `null`, and the answer. */
45
+ export const OriginConformanceMatchRow = z.strictObject({
46
+ patterns: z.string(),
47
+ origin: z.string().nullable(),
48
+ allowed: z.boolean(),
49
+ note: z.string().optional()
50
+ });
51
+ /** The conformance table. */
52
+ export const OriginConformance = z.strictObject({
53
+ parse: z.array(OriginConformanceParseRow).min(1),
54
+ match: z.array(OriginConformanceMatchRow).min(1)
55
+ });
56
+ /** The committed table, resolved relative to this module. */
57
+ export const ORIGIN_CONFORMANCE_URL = new URL('./origin_conformance.json', import.meta.url);
58
+ /**
59
+ * Read and validate the committed conformance table.
60
+ *
61
+ * @returns the parsed table
62
+ * @throws Error when the file doesn't match `OriginConformance`
63
+ */
64
+ export const load_origin_conformance = () => OriginConformance.parse(JSON.parse(readFileSync(ORIGIN_CONFORMANCE_URL, 'utf8')));
65
+ /**
66
+ * Flatten the table into action inputs and the answers they must get. A parse
67
+ * row sends a `null` origin, so a value that parses answers
68
+ * `{ok: true, allowed: true}`.
69
+ *
70
+ * @param table - the conformance table
71
+ * @returns one check per row, parse rows first
72
+ */
73
+ export const to_origin_conformance_checks = (table) => [
74
+ ...table.parse.map((row) => ({
75
+ label: `parse ${JSON.stringify(row.raw)}`,
76
+ input: { patterns: row.raw, origin: null },
77
+ expected: 'errors' in row
78
+ ? { ok: false, errors: row.errors.map((e) => ({ ...e })) }
79
+ : { ok: true, allowed: true }
80
+ })),
81
+ ...table.match.map((row) => ({
82
+ label: `match ${JSON.stringify(row.patterns)} × ${JSON.stringify(row.origin)}`,
83
+ input: { patterns: row.patterns, origin: row.origin },
84
+ expected: { ok: true, allowed: row.allowed }
85
+ }))
86
+ ];
87
+ /**
88
+ * Compare each check's answer with its expectation and fail once, listing
89
+ * every row that disagrees.
90
+ *
91
+ * @param checks - the table's checks
92
+ * @param answer - the answer each check got, in order
93
+ * @param backend - the name the failure message gives the answerer
94
+ */
95
+ export const assert_origin_conformance = (checks, answer, backend) => {
96
+ const failures = [];
97
+ checks.forEach((check, i) => {
98
+ const got = answer[i];
99
+ try {
100
+ assert.deepStrictEqual(got, check.expected);
101
+ }
102
+ catch {
103
+ failures.push(`${check.label}: expected ${JSON.stringify(check.expected)}, got ${JSON.stringify(got)}`);
104
+ }
105
+ });
106
+ assert.ok(failures.length === 0, `${backend}: ${failures.length} of ${checks.length} origin conformance rows disagree:\n` +
107
+ failures.join('\n'));
108
+ };
109
+ /**
110
+ * Register the origin conformance suite: every row of the committed table,
111
+ * sent to the backend's `_testing_origin_check` action over the keeper's
112
+ * daemon-token channel, must get the answer the table names. The action is on
113
+ * every spine test binary, so the suite is ungated.
114
+ */
115
+ export const describe_origin_conformance_cross_tests = (options) => {
116
+ const { setup_test } = options;
117
+ const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
118
+ const checks = to_origin_conformance_checks(load_origin_conformance());
119
+ describe('origin allowlist conformance', () => {
120
+ test('every table row gets the answer it names', async () => {
121
+ const fixture = await setup_test();
122
+ // Origin-free: the daemon-token middleware discards the credential in a
123
+ // browser context, and the cross-process transport adds an `Origin`
124
+ const transport = fixture.fresh_transport({ origin: null });
125
+ const headers = fixture.create_daemon_token_headers();
126
+ const answers = [];
127
+ for (const check of checks) {
128
+ const res = await cross_rpc_call(transport, rpc_path, testing_origin_check_action_spec.method, check.input, headers);
129
+ assert.ok(res.ok, `${check.label}: RPC failed: ${JSON.stringify(res.error)}`);
130
+ const parsed = TestingOriginCheckOutput.safeParse(res.result);
131
+ answers.push(parsed.success ? parsed.data : res.result);
132
+ }
133
+ assert_origin_conformance(checks, answers, 'backend');
134
+ });
135
+ });
136
+ };