@fuzdev/fuz_app 0.135.0 → 0.137.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/dist/actions/CLAUDE.md +2 -2
- package/dist/actions/action_spec.d.ts +0 -48
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +8 -2
- package/dist/auth/account_action_specs.d.ts +7 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +12 -3
- package/dist/auth/account_schema.d.ts +1 -6
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +1 -0
- package/dist/auth/actor_search_action_specs.d.ts +1 -1
- package/dist/auth/actor_search_action_specs.d.ts.map +1 -1
- package/dist/auth/actor_search_action_specs.js +3 -6
- package/dist/auth/audit_emitter.d.ts +4 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +18 -2
- package/dist/auth/cell_action_specs.d.ts +11 -1
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +18 -5
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +2 -4
- package/dist/auth/password.d.ts +2 -2
- package/dist/auth/password.d.ts.map +1 -1
- package/dist/auth/password.js +10 -12
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +9 -9
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_notifications.js +2 -1
- package/dist/auth/role_grant_offer_schema.d.ts +1 -1
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_schema.js +4 -7
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +5 -4
- package/dist/auth/scope_kind_schema.d.ts +10 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -1
- package/dist/auth/scope_kind_schema.js +15 -3
- package/dist/cli/args.d.ts +21 -15
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +40 -16
- package/dist/cli/daemon.d.ts +0 -5
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/help.d.ts +45 -6
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +81 -4
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/http/CLAUDE.md +1 -1
- package/dist/http/auth_shape.d.ts +0 -27
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/body_limit.d.ts +27 -14
- package/dist/http/body_limit.d.ts.map +1 -1
- package/dist/http/body_limit.js +29 -16
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +5 -3
- package/dist/primitive_schemas.d.ts +38 -4
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +77 -13
- package/dist/server/x_accel.d.ts +0 -1
- package/dist/server/x_accel.d.ts.map +1 -1
- package/dist/testing/CLAUDE.md +36 -19
- package/dist/testing/audit_drift_guard.d.ts +18 -5
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +24 -8
- package/dist/testing/cross_backend/action_manifest.d.ts +0 -14
- package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +18 -5
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +233 -34
- package/dist/testing/cross_backend/body_size_smuggling.d.ts +0 -12
- package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size_smuggling.js +92 -147
- package/dist/testing/cross_backend/capabilities.d.ts +3 -24
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +1 -4
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +31 -2
- package/dist/testing/cross_backend/conformance_case.d.ts +3 -3
- package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_header_robustness.js +18 -54
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +2 -8
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +2 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -9
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +5 -0
- package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +34 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +7 -14
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_outbound_overflow.js +65 -0
- package/dist/testing/schema_introspect.d.ts +24 -4
- package/dist/testing/schema_introspect.d.ts.map +1 -1
- package/dist/testing/schema_introspect.js +27 -4
- package/dist/testing/transports/raw_http.d.ts +78 -0
- package/dist/testing/transports/raw_http.d.ts.map +1 -0
- package/dist/testing/transports/raw_http.js +141 -0
- package/package.json +7 -5
- package/dist/env/resolve.d.ts +0 -163
- package/dist/env/resolve.d.ts.map +0 -1
- package/dist/env/resolve.js +0 -267
|
@@ -21,10 +21,14 @@ import './assert_dev_env.js';
|
|
|
21
21
|
* Index and constraint definitions are compared as the engine renders them,
|
|
22
22
|
* so both sides of a parity comparison need engines that render alike. pglet
|
|
23
23
|
* leaves an index's modifiers out of `pg_indexes.indexdef` — per-key sort
|
|
24
|
-
* direction, `NULLS` placement, operator classes, `NULLS NOT DISTINCT` —
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* direction, `NULLS` placement, operator classes, `NULLS NOT DISTINCT` — and
|
|
25
|
+
* that misleads a comparison in both directions: a pglet snapshot diffed
|
|
26
|
+
* against a PostgreSQL one reports every index that uses a modifier, with
|
|
27
|
+
* identical DDL on both sides (a false difference), and two pglet snapshots
|
|
28
|
+
* agree when the two schemas differ only in a modifier (a false agreement).
|
|
29
|
+
* So `query_schema_snapshot` throws on pglet when the snapshot holds an
|
|
30
|
+
* index, unless the caller passes `accept_pglet_index_definitions`. Take both
|
|
31
|
+
* snapshots of a parity comparison on PostgreSQL.
|
|
28
32
|
*
|
|
29
33
|
* Paired with `testing/schema_parity.ts` for comparison + assertion helpers.
|
|
30
34
|
*
|
|
@@ -170,6 +174,17 @@ export interface QuerySchemaSnapshotOptions {
|
|
|
170
174
|
* consumer's domain schema.
|
|
171
175
|
*/
|
|
172
176
|
readonly exclude_tables?: ReadonlyArray<string>;
|
|
177
|
+
/**
|
|
178
|
+
* Take the snapshot on pglet even though it holds indexes. Default `false`:
|
|
179
|
+
* pglet's `pg_indexes.indexdef` omits index modifiers (sort direction,
|
|
180
|
+
* `NULLS` placement, operator classes, `NULLS NOT DISTINCT`), so the
|
|
181
|
+
* snapshot's index definitions cannot be compared with another engine's,
|
|
182
|
+
* and do not tell two pglet schemas apart when they differ only in a
|
|
183
|
+
* modifier. Pass `true` only where no index definition is compared —
|
|
184
|
+
* reading tables, columns, constraints, sequences or enums off the
|
|
185
|
+
* snapshot. No effect on PostgreSQL or PGlite.
|
|
186
|
+
*/
|
|
187
|
+
readonly accept_pglet_index_definitions?: boolean;
|
|
173
188
|
}
|
|
174
189
|
/**
|
|
175
190
|
* Introspect a live database into a deterministic `SchemaSnapshot`.
|
|
@@ -180,6 +195,11 @@ export interface QuerySchemaSnapshotOptions {
|
|
|
180
195
|
* The `schema_version` migration tracker never appears in the `tables`
|
|
181
196
|
* field — it's framework bookkeeping created by the migration runner,
|
|
182
197
|
* identical across consumers, and would only add noise.
|
|
198
|
+
*
|
|
199
|
+
* @param db - the database to introspect
|
|
200
|
+
* @param options - schema, table excludes, and the pglet opt-in
|
|
201
|
+
* @returns the snapshot, every collection sorted
|
|
202
|
+
* @throws Error on pglet when the snapshot holds an index and `accept_pglet_index_definitions` is not set — its index definitions omit modifiers, so a comparison built on them is wrong in both directions
|
|
183
203
|
*/
|
|
184
204
|
export declare const query_schema_snapshot: (db: Db, options?: QuerySchemaSnapshotOptions) => Promise<SchemaSnapshot>;
|
|
185
205
|
//# sourceMappingURL=schema_introspect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_introspect.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_introspect.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B
|
|
1
|
+
{"version":3,"file":"schema_introspect.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_introspect.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;iBAWzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,qCAAqC;AACrC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;iBAOxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,gBAAgB;;iBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;iBAG3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;iBAOzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB;;;;iBAOhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;iBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,gBAAgB,CAa9E,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,0BAA0B;IAC1C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAChD;;;;;;;;;OASG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAClD;AAiED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,GACjC,IAAI,EAAE,EACN,UAAS,0BAA+B,KACtC,OAAO,CAAC,cAAc,CA0IxB,CAAC"}
|
|
@@ -21,10 +21,14 @@ import './assert_dev_env.js';
|
|
|
21
21
|
* Index and constraint definitions are compared as the engine renders them,
|
|
22
22
|
* so both sides of a parity comparison need engines that render alike. pglet
|
|
23
23
|
* leaves an index's modifiers out of `pg_indexes.indexdef` — per-key sort
|
|
24
|
-
* direction, `NULLS` placement, operator classes, `NULLS NOT DISTINCT` —
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* direction, `NULLS` placement, operator classes, `NULLS NOT DISTINCT` — and
|
|
25
|
+
* that misleads a comparison in both directions: a pglet snapshot diffed
|
|
26
|
+
* against a PostgreSQL one reports every index that uses a modifier, with
|
|
27
|
+
* identical DDL on both sides (a false difference), and two pglet snapshots
|
|
28
|
+
* agree when the two schemas differ only in a modifier (a false agreement).
|
|
29
|
+
* So `query_schema_snapshot` throws on pglet when the snapshot holds an
|
|
30
|
+
* index, unless the caller passes `accept_pglet_index_definitions`. Take both
|
|
31
|
+
* snapshots of a parity comparison on PostgreSQL.
|
|
28
32
|
*
|
|
29
33
|
* Paired with `testing/schema_parity.ts` for comparison + assertion helpers.
|
|
30
34
|
*
|
|
@@ -136,6 +140,8 @@ export const query_migration_tracker = async (db) => {
|
|
|
136
140
|
}))
|
|
137
141
|
};
|
|
138
142
|
};
|
|
143
|
+
/** The marker pglet's `version()` carries. */
|
|
144
|
+
const PGLET_VERSION_MARKER = '(pglet)';
|
|
139
145
|
const sort_keys = (record) => {
|
|
140
146
|
const sorted = {};
|
|
141
147
|
for (const key of Object.keys(record).sort()) {
|
|
@@ -170,6 +176,11 @@ const contype_to_kind = (contype) => {
|
|
|
170
176
|
* The `schema_version` migration tracker never appears in the `tables`
|
|
171
177
|
* field — it's framework bookkeeping created by the migration runner,
|
|
172
178
|
* identical across consumers, and would only add noise.
|
|
179
|
+
*
|
|
180
|
+
* @param db - the database to introspect
|
|
181
|
+
* @param options - schema, table excludes, and the pglet opt-in
|
|
182
|
+
* @returns the snapshot, every collection sorted
|
|
183
|
+
* @throws Error on pglet when the snapshot holds an index and `accept_pglet_index_definitions` is not set — its index definitions omit modifiers, so a comparison built on them is wrong in both directions
|
|
173
184
|
*/
|
|
174
185
|
export const query_schema_snapshot = async (db, options = {}) => {
|
|
175
186
|
const schema = options.schema ?? 'public';
|
|
@@ -260,6 +271,18 @@ export const query_schema_snapshot = async (db, options = {}) => {
|
|
|
260
271
|
for (const row of enum_rows) {
|
|
261
272
|
(enums[row.enum_name] ??= { labels: [] }).labels.push(row.label);
|
|
262
273
|
}
|
|
274
|
+
if (!options.accept_pglet_index_definitions &&
|
|
275
|
+
Object.values(tables).some((table) => table.indexes.length > 0)) {
|
|
276
|
+
const [version_row] = await db.query(`SELECT version() AS version`);
|
|
277
|
+
if (version_row?.version.includes(PGLET_VERSION_MARKER)) {
|
|
278
|
+
throw new Error('query_schema_snapshot: this database is pglet, whose pg_indexes.indexdef omits index ' +
|
|
279
|
+
'modifiers (sort direction, NULLS placement, operator classes, NULLS NOT DISTINCT), so ' +
|
|
280
|
+
"the snapshot's index definitions report false differences against PostgreSQL and false " +
|
|
281
|
+
'agreement between two pglet schemas. Take the snapshot on PostgreSQL (a TS backend ' +
|
|
282
|
+
'config defaults to memory://, which is pglet), or pass ' +
|
|
283
|
+
'{accept_pglet_index_definitions: true} when no index definition is compared.');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
263
286
|
return {
|
|
264
287
|
tables: sort_keys(tables),
|
|
265
288
|
sequences: sort_keys(sequences),
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/** What a raw exchange read back, and how it ended. */
|
|
3
|
+
export interface RawHttpExchange {
|
|
4
|
+
/** Every byte the server sent, `latin1`-decoded. */
|
|
5
|
+
readonly received: string;
|
|
6
|
+
/**
|
|
7
|
+
* `true` when the connection ended before the exchange stopped reading —
|
|
8
|
+
* the server closed it, or it never opened (a refused or unresolvable
|
|
9
|
+
* address reads as closed, with nothing received).
|
|
10
|
+
*/
|
|
11
|
+
readonly closed_by_server: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Options for `raw_http_exchange`. */
|
|
14
|
+
export interface RawHttpExchangeOptions {
|
|
15
|
+
/** How long to read before giving up on the server. */
|
|
16
|
+
readonly read_timeout_ms: number;
|
|
17
|
+
/**
|
|
18
|
+
* When the exchange stops reading. `'close'` reads until the server closes
|
|
19
|
+
* the connection; `'response'` stops as soon as one whole response has
|
|
20
|
+
* arrived (`raw_http_parse_response`), for a server that answers and then
|
|
21
|
+
* keeps the connection open. Either way the read timeout bounds it.
|
|
22
|
+
*
|
|
23
|
+
* `'response'` stops at the first response the bytes frame, so it is for a
|
|
24
|
+
* final response carrying `Content-Length` or chunked framing: a `1xx`
|
|
25
|
+
* interim response ends the read, and so does the head of a response
|
|
26
|
+
* delimited by the connection closing, before its body.
|
|
27
|
+
*
|
|
28
|
+
* @default 'close'
|
|
29
|
+
*/
|
|
30
|
+
readonly until?: 'close' | 'response';
|
|
31
|
+
}
|
|
32
|
+
/** One parsed HTTP/1.1 response. */
|
|
33
|
+
export interface RawHttpResponse {
|
|
34
|
+
readonly status: number;
|
|
35
|
+
/** Response headers, names lowercased; a repeated name keeps its last value. */
|
|
36
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
37
|
+
/** The response body, with any chunked framing removed. */
|
|
38
|
+
readonly body: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Open a raw TCP socket to `base_url`, write `request_bytes` once, and collect
|
|
42
|
+
* what the server sends back until it closes the connection, a whole response
|
|
43
|
+
* has arrived (`until: 'response'`), or `read_timeout_ms` elapses.
|
|
44
|
+
*
|
|
45
|
+
* Write errors are swallowed: a server that refuses a request and closes
|
|
46
|
+
* mid-upload surfaces as `EPIPE` / `ECONNRESET` on the unfinished write, and
|
|
47
|
+
* what a caller asserts on is what was read back. A write error also ends the
|
|
48
|
+
* socket, so bytes the server sent just before it can go unread — a caller
|
|
49
|
+
* that needs to read a refusal reliably sends nothing the server will leave
|
|
50
|
+
* unread.
|
|
51
|
+
*
|
|
52
|
+
* @param base_url - the server's base URL; only its host and port are used
|
|
53
|
+
* @param request_bytes - the bytes to send, `latin1`-encoded
|
|
54
|
+
*/
|
|
55
|
+
export declare const raw_http_exchange: (base_url: string, request_bytes: string, options: RawHttpExchangeOptions) => Promise<RawHttpExchange>;
|
|
56
|
+
/**
|
|
57
|
+
* Count HTTP response status lines in a raw byte stream. Deliberately
|
|
58
|
+
* **unanchored** (no `^`/`m`): a second pipelined response is concatenated
|
|
59
|
+
* straight after the first response's body, which carries no trailing newline,
|
|
60
|
+
* so a line-anchored match would miss it — and missing a smuggled second
|
|
61
|
+
* response is a silent false negative. It counts exactly the status lines as
|
|
62
|
+
* long as no response header or body contains the literal `HTTP/1.x NNN`.
|
|
63
|
+
*/
|
|
64
|
+
export declare const raw_http_count_responses: (raw: string) => number;
|
|
65
|
+
/** The status code of the first HTTP response in a raw byte stream, or `null` if none. */
|
|
66
|
+
export declare const raw_http_first_status: (raw: string) => number | null;
|
|
67
|
+
/**
|
|
68
|
+
* Parse the first response in a raw byte stream, once all of it has arrived.
|
|
69
|
+
*
|
|
70
|
+
* The body is delimited by `Transfer-Encoding: chunked` or `Content-Length`.
|
|
71
|
+
* A response carrying neither is delimited by the connection closing, which
|
|
72
|
+
* the bytes alone don't show, so its body is whatever followed the head.
|
|
73
|
+
*
|
|
74
|
+
* @param raw - the bytes read so far, `latin1`-decoded
|
|
75
|
+
* @returns the response, or `null` when the head or the framed body is incomplete
|
|
76
|
+
*/
|
|
77
|
+
export declare const raw_http_parse_response: (raw: string) => RawHttpResponse | null;
|
|
78
|
+
//# sourceMappingURL=raw_http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw_http.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/raw_http.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC/B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACnC;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACtC,uDAAuD;IACvD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;CACtC;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC7B,UAAU,MAAM,EAChB,eAAe,MAAM,EACrB,SAAS,sBAAsB,KAC7B,OAAO,CAAC,eAAe,CAwBvB,CAAC;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,MAAM,KAAG,MACN,CAAC;AAElD,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAG5D,CAAC;AAyBF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAI,KAAK,MAAM,KAAG,eAAe,GAAG,IAyBvE,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Raw-socket HTTP/1.1 exchanges, for tests that need to control exactly which
|
|
4
|
+
* bytes reach the server.
|
|
5
|
+
*
|
|
6
|
+
* `fetch` frames a request itself and sends all of it, so some server
|
|
7
|
+
* behaviors can't be reached through it: what a server does with two requests
|
|
8
|
+
* in one write, with duplicate or malformed headers, with a request head whose
|
|
9
|
+
* body never follows, or with a chunked body that is never terminated. This
|
|
10
|
+
* writes caller-built bytes over `node:net` and hands back what the server
|
|
11
|
+
* sent, plus just enough parsing to read a status or one whole response.
|
|
12
|
+
*
|
|
13
|
+
* Everything is `latin1`-decoded, so a string's length is its byte length.
|
|
14
|
+
*
|
|
15
|
+
* Plain TCP only: an `https:` URL is not spoken to over TLS, and the host must
|
|
16
|
+
* be a name or an IPv4 address — a bracketed IPv6 literal does not connect.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { connect } from 'node:net';
|
|
21
|
+
/**
|
|
22
|
+
* Open a raw TCP socket to `base_url`, write `request_bytes` once, and collect
|
|
23
|
+
* what the server sends back until it closes the connection, a whole response
|
|
24
|
+
* has arrived (`until: 'response'`), or `read_timeout_ms` elapses.
|
|
25
|
+
*
|
|
26
|
+
* Write errors are swallowed: a server that refuses a request and closes
|
|
27
|
+
* mid-upload surfaces as `EPIPE` / `ECONNRESET` on the unfinished write, and
|
|
28
|
+
* what a caller asserts on is what was read back. A write error also ends the
|
|
29
|
+
* socket, so bytes the server sent just before it can go unread — a caller
|
|
30
|
+
* that needs to read a refusal reliably sends nothing the server will leave
|
|
31
|
+
* unread.
|
|
32
|
+
*
|
|
33
|
+
* @param base_url - the server's base URL; only its host and port are used
|
|
34
|
+
* @param request_bytes - the bytes to send, `latin1`-encoded
|
|
35
|
+
*/
|
|
36
|
+
export const raw_http_exchange = (base_url, request_bytes, options) => new Promise((resolve) => {
|
|
37
|
+
const { read_timeout_ms, until = 'close' } = options;
|
|
38
|
+
const url = new URL(base_url);
|
|
39
|
+
const port = Number(url.port) || (url.protocol === 'https:' ? 443 : 80);
|
|
40
|
+
const socket = connect({ host: url.hostname, port });
|
|
41
|
+
let received = '';
|
|
42
|
+
let settled = false;
|
|
43
|
+
const finish = (closed_by_server) => {
|
|
44
|
+
if (settled)
|
|
45
|
+
return;
|
|
46
|
+
settled = true;
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
socket.destroy();
|
|
49
|
+
resolve({ received, closed_by_server });
|
|
50
|
+
};
|
|
51
|
+
const timer = setTimeout(() => finish(false), read_timeout_ms);
|
|
52
|
+
socket.setEncoding('latin1');
|
|
53
|
+
socket.on('connect', () => socket.write(request_bytes, 'latin1'));
|
|
54
|
+
socket.on('data', (chunk) => {
|
|
55
|
+
received += chunk;
|
|
56
|
+
if (until === 'response' && raw_http_parse_response(received))
|
|
57
|
+
finish(false);
|
|
58
|
+
});
|
|
59
|
+
socket.on('error', () => { }); // EPIPE/ECONNRESET on a mid-write close is expected
|
|
60
|
+
socket.on('close', () => finish(true));
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Count HTTP response status lines in a raw byte stream. Deliberately
|
|
64
|
+
* **unanchored** (no `^`/`m`): a second pipelined response is concatenated
|
|
65
|
+
* straight after the first response's body, which carries no trailing newline,
|
|
66
|
+
* so a line-anchored match would miss it — and missing a smuggled second
|
|
67
|
+
* response is a silent false negative. It counts exactly the status lines as
|
|
68
|
+
* long as no response header or body contains the literal `HTTP/1.x NNN`.
|
|
69
|
+
*/
|
|
70
|
+
export const raw_http_count_responses = (raw) => (raw.match(/HTTP\/1\.[01] \d{3}/g) ?? []).length;
|
|
71
|
+
/** The status code of the first HTTP response in a raw byte stream, or `null` if none. */
|
|
72
|
+
export const raw_http_first_status = (raw) => {
|
|
73
|
+
const match = /HTTP\/1\.[01] (\d{3})/.exec(raw);
|
|
74
|
+
return match ? Number(match[1]) : null;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Remove chunked framing from a body.
|
|
78
|
+
*
|
|
79
|
+
* @returns the decoded body, or `null` when the terminating chunk hasn't arrived
|
|
80
|
+
*/
|
|
81
|
+
const decode_chunked = (raw) => {
|
|
82
|
+
let body = '';
|
|
83
|
+
let offset = 0;
|
|
84
|
+
for (;;) {
|
|
85
|
+
const line_end = raw.indexOf('\r\n', offset);
|
|
86
|
+
if (line_end === -1)
|
|
87
|
+
return null;
|
|
88
|
+
// a chunk-size line may carry `;extension`s after the hex size
|
|
89
|
+
const size = Number.parseInt(raw.slice(offset, line_end), 16);
|
|
90
|
+
if (!Number.isFinite(size))
|
|
91
|
+
return null;
|
|
92
|
+
const data_start = line_end + 2;
|
|
93
|
+
// the last chunk is followed by optional trailers and an empty line
|
|
94
|
+
if (size === 0)
|
|
95
|
+
return raw.includes('\r\n\r\n', line_end) ? body : null;
|
|
96
|
+
if (raw.length < data_start + size + 2)
|
|
97
|
+
return null;
|
|
98
|
+
body += raw.slice(data_start, data_start + size);
|
|
99
|
+
offset = data_start + size + 2;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Parse the first response in a raw byte stream, once all of it has arrived.
|
|
104
|
+
*
|
|
105
|
+
* The body is delimited by `Transfer-Encoding: chunked` or `Content-Length`.
|
|
106
|
+
* A response carrying neither is delimited by the connection closing, which
|
|
107
|
+
* the bytes alone don't show, so its body is whatever followed the head.
|
|
108
|
+
*
|
|
109
|
+
* @param raw - the bytes read so far, `latin1`-decoded
|
|
110
|
+
* @returns the response, or `null` when the head or the framed body is incomplete
|
|
111
|
+
*/
|
|
112
|
+
export const raw_http_parse_response = (raw) => {
|
|
113
|
+
const head_end = raw.indexOf('\r\n\r\n');
|
|
114
|
+
if (head_end === -1)
|
|
115
|
+
return null;
|
|
116
|
+
const [status_line, ...header_lines] = raw.slice(0, head_end).split('\r\n');
|
|
117
|
+
const status_match = /^HTTP\/1\.[01] (\d{3})/.exec(status_line);
|
|
118
|
+
if (!status_match)
|
|
119
|
+
return null;
|
|
120
|
+
const headers = {};
|
|
121
|
+
for (const line of header_lines) {
|
|
122
|
+
const colon = line.indexOf(':');
|
|
123
|
+
if (colon <= 0)
|
|
124
|
+
continue;
|
|
125
|
+
headers[line.slice(0, colon).trim().toLowerCase()] = line.slice(colon + 1).trim();
|
|
126
|
+
}
|
|
127
|
+
const status = Number(status_match[1]);
|
|
128
|
+
const rest = raw.slice(head_end + 4);
|
|
129
|
+
if (headers['transfer-encoding']?.toLowerCase().includes('chunked')) {
|
|
130
|
+
const body = decode_chunked(rest);
|
|
131
|
+
return body === null ? null : { status, headers, body };
|
|
132
|
+
}
|
|
133
|
+
const content_length = headers['content-length'];
|
|
134
|
+
if (content_length !== undefined) {
|
|
135
|
+
const length = Number(content_length);
|
|
136
|
+
if (!Number.isSafeInteger(length) || length < 0 || rest.length < length)
|
|
137
|
+
return null;
|
|
138
|
+
return { status, headers, body: rest.slice(0, length) };
|
|
139
|
+
}
|
|
140
|
+
return { status, headers, body: rest };
|
|
141
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.137.0",
|
|
4
4
|
"description": "fullstack app library",
|
|
5
5
|
"glyph": "🗝",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"@electric-sql/pglite": ">=0.4",
|
|
35
35
|
"@fuzdev/blake3-wasm": ">=0.2.0",
|
|
36
36
|
"@fuzdev/fuz_css": ">=0.65.0",
|
|
37
|
-
"@fuzdev/
|
|
37
|
+
"@fuzdev/fuz_schema": "^0.2.0",
|
|
38
|
+
"@fuzdev/fuz_util": ">=0.73.0",
|
|
38
39
|
"@fuzdev/pglet-wasm": ">=0.1.1",
|
|
39
40
|
"@hono/bun": ">=1",
|
|
40
41
|
"@hono/deno": ">=1",
|
|
@@ -92,9 +93,10 @@
|
|
|
92
93
|
"@fuzdev/blake3-wasm": "^0.2.0",
|
|
93
94
|
"@fuzdev/fuz_code": "^0.52.1",
|
|
94
95
|
"@fuzdev/fuz_css": "^0.66.1",
|
|
96
|
+
"@fuzdev/fuz_schema": "^0.2.0",
|
|
95
97
|
"@fuzdev/fuz_ui": "^0.213.0",
|
|
96
|
-
"@fuzdev/fuz_util": "^0.
|
|
97
|
-
"@fuzdev/gro": "^0.
|
|
98
|
+
"@fuzdev/fuz_util": "^0.73.0",
|
|
99
|
+
"@fuzdev/gro": "^0.217.0",
|
|
98
100
|
"@fuzdev/mdz": "^0.4.0",
|
|
99
101
|
"@fuzdev/pglet-wasm": "^0.1.1",
|
|
100
102
|
"@hono/bun": "^1.0.1",
|
|
@@ -130,7 +132,7 @@
|
|
|
130
132
|
"vitest": "^5.0.0",
|
|
131
133
|
"ws": "^8.20.1",
|
|
132
134
|
"zimmerframe": "^1.1.4",
|
|
133
|
-
"zod": "^4.
|
|
135
|
+
"zod": "^4.6.7"
|
|
134
136
|
},
|
|
135
137
|
"sideEffects": [
|
|
136
138
|
"**/*.css"
|
package/dist/env/resolve.d.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment variable `$$VAR$$` resolution suite.
|
|
3
|
-
*
|
|
4
|
-
* Resolves `$$VAR$$` references in strings and object trees,
|
|
5
|
-
* scans configs for references, and validates/formats missing vars.
|
|
6
|
-
*
|
|
7
|
-
* The double-dollar bookending syntax is:
|
|
8
|
-
* - Visually distinct from shell `$VAR` syntax
|
|
9
|
-
* - Unambiguous about variable boundaries
|
|
10
|
-
* - Easy to grep: `grep '\$\$'`
|
|
11
|
-
* - Fails loud if accidentally shell-processed (`$$`=PID in shell)
|
|
12
|
-
*
|
|
13
|
-
* # Syntax
|
|
14
|
-
*
|
|
15
|
-
* - `$$VAR$$` — required reference. Missing or empty fails validation
|
|
16
|
-
* (see `validate_env_vars`).
|
|
17
|
-
* - `$$?VAR$$` — optional reference. Missing or empty resolves to the
|
|
18
|
-
* empty string and passes validation. Use for vars that exist in the
|
|
19
|
-
* contract but may be intentionally blank (e.g. `SMTP_PASSWORD=` on
|
|
20
|
-
* a deployment that hasn't configured SMTP yet).
|
|
21
|
-
* - `\$$VAR$$` — escape. The leading backslash is dropped at resolution
|
|
22
|
-
* time and the rest is emitted literally. Use this when documenting
|
|
23
|
-
* the syntax inside a string literal (comments in env-file templates,
|
|
24
|
-
* for example) where a regex scan would otherwise treat the mention
|
|
25
|
-
* as a real reference. Combines with `?` as `\$$?VAR$$`.
|
|
26
|
-
*
|
|
27
|
-
* @module
|
|
28
|
-
*/
|
|
29
|
-
import type { EnvDeps } from '../runtime/deps.js';
|
|
30
|
-
/**
|
|
31
|
-
* Resolve environment variable references in a string.
|
|
32
|
-
*
|
|
33
|
-
* - `$$VAR$$` resolves from the runtime env; missing values are left as-is
|
|
34
|
-
* for the validation phase to report.
|
|
35
|
-
* - `$$?VAR$$` is the optional form — missing or empty resolves to the empty
|
|
36
|
-
* string. Required validation skips refs marked optional.
|
|
37
|
-
* - `\$$VAR$$` / `\$$?VAR$$` are escapes — the leading backslash is dropped
|
|
38
|
-
* and the body is emitted literally (no resolution attempted).
|
|
39
|
-
*
|
|
40
|
-
* @param runtime - runtime with `env_get` capability
|
|
41
|
-
* @param value - string that may contain `$$VAR$$` references
|
|
42
|
-
* @returns string with env vars resolved
|
|
43
|
-
*/
|
|
44
|
-
export declare const resolve_env_vars: (runtime: Pick<EnvDeps, "env_get">, value: string) => string;
|
|
45
|
-
/**
|
|
46
|
-
* Check if a string contains unresolved env var references.
|
|
47
|
-
*
|
|
48
|
-
* Escaped references (`\$$VAR$$`) do not count — they're literal text once
|
|
49
|
-
* resolved.
|
|
50
|
-
*
|
|
51
|
-
* @param value - string to check
|
|
52
|
-
* @returns `true` if string contains unescaped `$$VAR$$` patterns
|
|
53
|
-
*/
|
|
54
|
-
export declare const has_env_vars: (value: string) => boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Get list of env var names referenced in a string.
|
|
57
|
-
*
|
|
58
|
-
* Escaped references are skipped; optional and required references are both
|
|
59
|
-
* included (callers that care about the distinction should use `scan_env_vars`
|
|
60
|
-
* which preserves the `optional` flag per ref).
|
|
61
|
-
*
|
|
62
|
-
* @param value - string to scan
|
|
63
|
-
* @returns array of variable names (without `$$` delimiters)
|
|
64
|
-
*/
|
|
65
|
-
export declare const get_env_var_names: (value: string) => Array<string>;
|
|
66
|
-
/**
|
|
67
|
-
* Resolve env vars in an object's string values (shallow).
|
|
68
|
-
*
|
|
69
|
-
* @param runtime - runtime with `env_get` capability
|
|
70
|
-
* @param obj - object with string values
|
|
71
|
-
* @returns new object with env vars resolved
|
|
72
|
-
*/
|
|
73
|
-
export declare const resolve_env_vars_in_object: <T extends Record<string, unknown>>(runtime: Pick<EnvDeps, "env_get">, obj: T) => T;
|
|
74
|
-
/**
|
|
75
|
-
* Resolve env vars and throw if any are missing/empty.
|
|
76
|
-
*
|
|
77
|
-
* Use this for values that must be present. `$$?VAR$$` (optional) refs
|
|
78
|
-
* resolve to the empty string on miss without contributing to the error.
|
|
79
|
-
* Escaped references (`\$$VAR$$`) emit literally and never check the env.
|
|
80
|
-
*
|
|
81
|
-
* @param runtime - runtime with `env_get` capability
|
|
82
|
-
* @param value - string with `$$VAR$$` references
|
|
83
|
-
* @param context - description for error message (e.g., `"target.host"`)
|
|
84
|
-
* @returns resolved string
|
|
85
|
-
* @throws Error if any referenced env var is missing or empty
|
|
86
|
-
*/
|
|
87
|
-
export declare const resolve_env_vars_required: (runtime: Pick<EnvDeps, "env_get">, value: string, context: string) => string;
|
|
88
|
-
/**
|
|
89
|
-
* An env var reference found in a config.
|
|
90
|
-
*/
|
|
91
|
-
export interface EnvVarRef {
|
|
92
|
-
/** Variable name (without `$$` delimiters). */
|
|
93
|
-
name: string;
|
|
94
|
-
/** Path where the reference was found (e.g., `"target.host"`, `"resources[3].path"`). */
|
|
95
|
-
path: string;
|
|
96
|
-
/**
|
|
97
|
-
* Whether the reference is optional (`$$?VAR$$`). Optional refs resolve
|
|
98
|
-
* to the empty string when unset and are skipped by `validate_env_vars` —
|
|
99
|
-
* a var that's intentionally blank doesn't count as missing.
|
|
100
|
-
*/
|
|
101
|
-
optional: boolean;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Recursively scan an object for `$$VAR$$` env var references.
|
|
105
|
-
*
|
|
106
|
-
* Walks all string values in the object tree and extracts env var names
|
|
107
|
-
* with their path context for error reporting. Escaped references
|
|
108
|
-
* (`\$$VAR$$`) are skipped — they're literal text, not references.
|
|
109
|
-
* The `optional` flag on each ref distinguishes `$$VAR$$` (required) from
|
|
110
|
-
* `$$?VAR$$` (optional) for downstream validation.
|
|
111
|
-
*
|
|
112
|
-
* @param obj - object to scan (typically a config)
|
|
113
|
-
* @returns array of env var references with paths and optional flags
|
|
114
|
-
*/
|
|
115
|
-
export declare const scan_env_vars: (obj: unknown) => Array<EnvVarRef>;
|
|
116
|
-
/**
|
|
117
|
-
* Result of env var validation.
|
|
118
|
-
*
|
|
119
|
-
* Uses discriminated union for better type narrowing:
|
|
120
|
-
* - `ok: true, missing: null` — all vars present
|
|
121
|
-
* - `ok: false, missing: EnvVarRef[]` — some vars missing
|
|
122
|
-
*/
|
|
123
|
-
export type EnvValidationResult = {
|
|
124
|
-
ok: true;
|
|
125
|
-
missing: null;
|
|
126
|
-
} | {
|
|
127
|
-
ok: false;
|
|
128
|
-
missing: Array<EnvVarRef>;
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Validate that all referenced env vars exist in the environment.
|
|
132
|
-
*
|
|
133
|
-
* Returns all missing refs (including duplicates by name). Grouping
|
|
134
|
-
* and deduplication is handled by `format_missing_env_vars` at display time.
|
|
135
|
-
* Refs marked `optional: true` (from `$$?VAR$$` syntax) are skipped — a
|
|
136
|
-
* deliberately-blank var is contract, not a missing dependency.
|
|
137
|
-
*
|
|
138
|
-
* @param runtime - runtime with `env_get` capability
|
|
139
|
-
* @param refs - env var references from `scan_env_vars`
|
|
140
|
-
* @returns validation result with any missing vars
|
|
141
|
-
*/
|
|
142
|
-
export declare const validate_env_vars: (runtime: Pick<EnvDeps, "env_get">, refs: Array<EnvVarRef>) => EnvValidationResult;
|
|
143
|
-
/**
|
|
144
|
-
* Options for `format_missing_env_vars`.
|
|
145
|
-
*/
|
|
146
|
-
export interface FormatMissingEnvVarsOptions {
|
|
147
|
-
/** Path to env file if one was loaded. */
|
|
148
|
-
env_file?: string;
|
|
149
|
-
/** Hint text for how to set up the environment. */
|
|
150
|
-
setup_hint?: string;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Format missing env vars error message.
|
|
154
|
-
*
|
|
155
|
-
* Groups refs by variable name so each missing var is shown once
|
|
156
|
-
* with all paths where it's referenced.
|
|
157
|
-
*
|
|
158
|
-
* @param missing - missing env var references (may contain duplicate names)
|
|
159
|
-
* @param options - formatting options
|
|
160
|
-
* @returns formatted error message for display
|
|
161
|
-
*/
|
|
162
|
-
export declare const format_missing_env_vars: (missing: Array<EnvVarRef>, options?: FormatMissingEnvVarsOptions) => string;
|
|
163
|
-
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/env/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAiBlD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,MAAM,KAAG,MAYnF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,OAQ5C,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,KAAK,CAAC,MAAM,CAS7D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3E,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,KAAK,CAAC,KACJ,CAQF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACrC,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,OAAO,MAAM,EACb,SAAS,MAAM,KACb,MAyBF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,KAAG,KAAK,CAAC,SAAS,CAI3D,CAAC;AA2BF;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;CAAE,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAC7B,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,MAAM,KAAK,CAAC,SAAS,CAAC,KACpB,mBAYF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,KAAK,CAAC,SAAS,CAAC,EACzB,UAAU,2BAA2B,KACnC,MA+BF,CAAC"}
|