@haverstack/core 0.22.0 → 0.24.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.
@@ -1 +1 @@
1
- {"version":3,"file":"wire-entry.js","sourceRoot":"","sources":["../src/wire-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,eAAe,GAChB,MAAM,WAAW,CAAC;AAGnB,0EAA0E;AAC1E,0EAA0E;AAC1E,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,6DAA6D;AAC7D,gEAAgE;AAChE,oDAAoD;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,oCAAoC,EACpC,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAGlC,kEAAkE;AAClE,yEAAyE;AACzE,wEAAwE;AACxE,6EAA6E;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"wire-entry.js","sourceRoot":"","sources":["../src/wire-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,eAAe,GAChB,MAAM,WAAW,CAAC;AAGnB,0EAA0E;AAC1E,0EAA0E;AAC1E,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,6DAA6D;AAC7D,gEAAgE;AAChE,oDAAoD;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,oCAAoC,EACpC,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAGlC,kEAAkE;AAClE,yEAAyE;AACzE,wEAAwE;AACxE,6EAA6E;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAMnE,wEAAwE;AACxE,oEAAoE;AACpE,wEAAwE;AACxE,sEAAsE;AACtE,gEAAgE;AAChE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,wEAAwE;AACxE,sCAAsC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,4EAA4E;AAC5E,oEAAoE;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,uEAAuE;AACvE,yEAAyE;AACzE,0DAA0D;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAG/D,4EAA4E;AAC5E,wEAAwE;AACxE,6CAA6C;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Stack — Wire Record Bodies
3
+ * -------------------------------------------------------
4
+ * `POST /records` is the one endpoint where a client sends a whole record
5
+ * and the server may trust only part of it. This module turns that body
6
+ * into the arguments `ScopedStack.create()` takes, so the disposition of
7
+ * each field — stamped, conditionally dropped, forwarded — is structural
8
+ * rather than a property of how carefully a route was written. Which field
9
+ * gets which is not derivable from its name, and the rules are normative
10
+ * for servers in other languages, so they live in the spec rather than
11
+ * here: docs/spec/wire-format.md § Records.
12
+ */
13
+ import type { BackdatableCreateRecordOptions } from './stack.js';
14
+ import type { EntityId, TokenSession, TypeId } from './types.js';
15
+ /**
16
+ * A `POST /records` body as the three arguments `ScopedStack.create()`
17
+ * takes. Not spread-ready, so a call site reads as three deliberate things.
18
+ */
19
+ export type WireCreateRequest = {
20
+ typeId: TypeId;
21
+ content: Record<string, unknown>;
22
+ /** Omitted, not optional: a caller cannot forward what the type lacks. */
23
+ options: Omit<BackdatableCreateRecordOptions, 'entityId' | 'principalId'>;
24
+ };
25
+ /**
26
+ * Read a `POST /records` body as a create. Takes the session rather than a
27
+ * pre-computed boolean so the owner-acting-alone determination stays with
28
+ * the rule that needs it.
29
+ *
30
+ * Beyond shape, values are `Stack`'s to judge — an id's legality, a
31
+ * permission's contents, the content's schema — since judging them twice
32
+ * would let the two answers drift.
33
+ */
34
+ export declare function createOptionsFromWireRecord(body: unknown, session: TokenSession, ownerEntityId: EntityId): WireCreateRequest;
35
+ //# sourceMappingURL=wire-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire-record.d.ts","sourceRoot":"","sources":["../src/wire-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAe,QAAQ,EAAc,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1F;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,0EAA0E;IAC1E,OAAO,EAAE,IAAI,CAAC,8BAA8B,EAAE,UAAU,GAAG,aAAa,CAAC,CAAC;CAC3E,CAAC;AA8CF;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,YAAY,EACrB,aAAa,EAAE,QAAQ,GACtB,iBAAiB,CAoCnB"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Stack — Wire Record Bodies
3
+ * -------------------------------------------------------
4
+ * `POST /records` is the one endpoint where a client sends a whole record
5
+ * and the server may trust only part of it. This module turns that body
6
+ * into the arguments `ScopedStack.create()` takes, so the disposition of
7
+ * each field — stamped, conditionally dropped, forwarded — is structural
8
+ * rather than a property of how carefully a route was written. Which field
9
+ * gets which is not derivable from its name, and the rules are normative
10
+ * for servers in other languages, so they live in the spec rather than
11
+ * here: docs/spec/wire-format.md § Records.
12
+ */
13
+ import { isOwnerActingAlone } from './access.js';
14
+ import { StackQueryError, StackValidationError } from './stack.js';
15
+ function requireBody(body) {
16
+ if (typeof body !== 'object' || body === null || Array.isArray(body))
17
+ throw new StackQueryError('Invalid record body: expected an object');
18
+ return body;
19
+ }
20
+ /**
21
+ * The refusal a present field earns when its value is the wrong shape.
22
+ * `StackValidationError` because the failure names a field of the record
23
+ * being written, and only that class carries the path.
24
+ */
25
+ function fieldError(path, message) {
26
+ throw new StackValidationError([{ path, message }]);
27
+ }
28
+ function optionalString(body, key) {
29
+ const value = body[key];
30
+ if (value === undefined)
31
+ return undefined;
32
+ if (typeof value !== 'string')
33
+ fieldError(key, `${key} must be a string`);
34
+ return value;
35
+ }
36
+ function optionalArray(body, key) {
37
+ const value = body[key];
38
+ if (value === undefined)
39
+ return undefined;
40
+ if (!Array.isArray(value))
41
+ fieldError(key, `${key} must be an array`);
42
+ return value;
43
+ }
44
+ /**
45
+ * A wire date for one of the owner-only clock fields. An `Invalid Date`
46
+ * must not reach `create()`: its `NaN` timestamp switches the id-skew
47
+ * check off instead of failing it.
48
+ */
49
+ function ownerDate(body, key) {
50
+ const value = body[key];
51
+ if (value === undefined)
52
+ return undefined;
53
+ if (typeof value !== 'string' && typeof value !== 'number')
54
+ fieldError(key, `${key} must be a date string`);
55
+ const date = new Date(value);
56
+ if (isNaN(date.getTime()))
57
+ fieldError(key, `Invalid ${key}: ${JSON.stringify(value)}`);
58
+ return date;
59
+ }
60
+ /**
61
+ * Read a `POST /records` body as a create. Takes the session rather than a
62
+ * pre-computed boolean so the owner-acting-alone determination stays with
63
+ * the rule that needs it.
64
+ *
65
+ * Beyond shape, values are `Stack`'s to judge — an id's legality, a
66
+ * permission's contents, the content's schema — since judging them twice
67
+ * would let the two answers drift.
68
+ */
69
+ export function createOptionsFromWireRecord(body, session, ownerEntityId) {
70
+ const record = requireBody(body);
71
+ const typeId = record.typeId;
72
+ if (typeof typeId !== 'string' || typeId === '')
73
+ throw new StackQueryError('Invalid record body: typeId is required');
74
+ const content = record.content;
75
+ if (typeof content !== 'object' || content === null || Array.isArray(content))
76
+ throw new StackQueryError('Invalid record body: content is required');
77
+ const options = {};
78
+ const id = optionalString(record, 'id');
79
+ if (id !== undefined)
80
+ options.id = id;
81
+ const parentId = optionalString(record, 'parentId');
82
+ if (parentId !== undefined)
83
+ options.parentId = parentId;
84
+ const appId = optionalString(record, 'appId');
85
+ if (appId !== undefined)
86
+ options.appId = appId;
87
+ const permissions = optionalArray(record, 'permissions');
88
+ if (permissions !== undefined)
89
+ options.permissions = permissions;
90
+ const associations = optionalArray(record, 'associations');
91
+ if (associations !== undefined)
92
+ options.associations = associations;
93
+ // Presence is the whole signal: an unlisted record is unlisted from
94
+ // birth, so the timestamp a client sends has nothing to say.
95
+ if (optionalString(record, 'unlistedAt') !== undefined)
96
+ options.unlisted = true;
97
+ if (isOwnerActingAlone(session, ownerEntityId)) {
98
+ const createdAt = ownerDate(record, 'createdAt');
99
+ if (createdAt !== undefined)
100
+ options.createdAt = createdAt;
101
+ const updatedAt = ownerDate(record, 'updatedAt');
102
+ if (updatedAt !== undefined)
103
+ options.updatedAt = updatedAt;
104
+ }
105
+ return { typeId, content: content, options };
106
+ }
107
+ //# sourceMappingURL=wire-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire-record.js","sourceRoot":"","sources":["../src/wire-record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Stack — Wire Request Parsing
3
+ * -------------------------------------------------------
4
+ * The parse half of the request encoding `adapter-api` builds: URL search
5
+ * params and JSON bodies in, the core types `Stack` takes out. Core has no
6
+ * HTTP server of its own — this module exists so every server
7
+ * implementation shares one decoding of the query grammar rather than
8
+ * transcribing docs/spec/wire-format.md § Records' parameter table and
9
+ * re-deriving its rules, which is where a miss produces a query that
10
+ * silently widens rather than one that fails.
11
+ *
12
+ * Three things this deliberately does not do:
13
+ *
14
+ * - **No clamping.** A `limit` ceiling is deployment policy, not wire
15
+ * contract, so the requested limit is reported as asked and the server
16
+ * applies its own cap afterwards.
17
+ * - **No capability gating.** `assertQueryCapabilities()` in
18
+ * `@haverstack/core/adapter` stays a separate, deliberate call.
19
+ * - **No value validation beyond shape.** An ID's or a cursor's legality is
20
+ * `Stack`'s to judge, and judging it twice would let the two answers
21
+ * drift.
22
+ *
23
+ * Everything here throws `StackQueryError` on malformed input, which
24
+ * servers already map to 400 — see docs/spec/wire-format.md § Error
25
+ * responses.
26
+ */
27
+ import type { ChangeFilter, StackQuery } from './types.js';
28
+ /** Parse an ISO date string from a wire body; undefined if absent or invalid. */
29
+ export declare function parseDate(val: unknown): Date | undefined;
30
+ /**
31
+ * Strict positive-integer parse for a URL param — rejects "1abc", "2.7",
32
+ * "-5". Also serves the `:version` path params, which share this
33
+ * "malformed, don't silently coerce" requirement.
34
+ */
35
+ export declare function parsePositiveInt(raw: string, label: string): number;
36
+ /**
37
+ * Build a `StackQuery` from `GET /records` search params — the inverse of
38
+ * what `APIAdapter` encodes for a server without `contentFieldQuery`.
39
+ * `parentId=null` is the literal string, the sentinel for "top level":
40
+ * Crockford base-32 excludes `u` and `l`, so no record ID can collide
41
+ * with it. See docs/spec/wire-format.md § Records.
42
+ */
43
+ export declare function parseQueryParams(url: URL): StackQuery;
44
+ /**
45
+ * Build a `StackQuery` from a `POST /records/query` JSON body — the
46
+ * superset form, which additionally carries `filter.content`. Dates arrive
47
+ * as the ISO strings `JSON.stringify` made of them and are decoded back to
48
+ * `Date`. See docs/spec/wire-format.md § Records.
49
+ */
50
+ export declare function parseQueryBody(raw: unknown): StackQuery;
51
+ /**
52
+ * What `GET /changes`' query params carry. One object rather than three
53
+ * exports because `includeUnlisted` sits on `SubscribeOptions` rather than
54
+ * `ChangeFilter`, and a server needs it before the stream opens to answer
55
+ * the owner-only 403. See docs/spec/change-feed.md.
56
+ */
57
+ export type ParsedChangeParams = {
58
+ filter: ChangeFilter;
59
+ includeRecords: boolean;
60
+ includeUnlisted: boolean;
61
+ };
62
+ /**
63
+ * Parse `GET /changes`' query params. The resume cursor is deliberately
64
+ * absent: `Last-Event-ID` outranks `?since=`, and reconciling the two is
65
+ * the server's own resumption machinery rather than request encoding.
66
+ */
67
+ export declare function parseChangeParams(url: URL): ParsedChangeParams;
68
+ /**
69
+ * Parse an `If-Match: "5"` header into the version for `ifVersion`.
70
+ * A value that is not a bare, optionally quoted version is refused rather
71
+ * than read as absent: the header exists to fence a write, so degrading a
72
+ * malformed one to an unconditional last-writer-wins mutation defeats the
73
+ * only thing it was sent to do. Weak comparators (`W/"5"`) are refused for
74
+ * the same reason — a version match is exact or it is nothing.
75
+ * See docs/spec/wire-format.md § Records.
76
+ */
77
+ export declare function parseIfMatch(header: string | undefined): number | undefined;
78
+ /**
79
+ * Read an upload's filename from its `Content-Disposition`. Prefers the
80
+ * RFC 5987 extended form (`filename*=UTF-8''name.txt`), which is what a
81
+ * client carrying a non-ASCII name sends, and falls back to the plain
82
+ * quoted form. See docs/spec/wire-format.md § Upload.
83
+ */
84
+ export declare function parseUploadFilename(header: string | undefined): string | undefined;
85
+ //# sourceMappingURL=wire-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire-request.d.ts","sourceRoot":"","sources":["../src/wire-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EACV,YAAY,EAMZ,UAAU,EACX,MAAM,YAAY,CAAC;AAMpB,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAIxD;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnE;AAwKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU,CA2ErD;AAaD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CA4DvD;AAMD;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,kBAAkB,CA8B9D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAK3E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAYlF"}