@craftainer/crud-sdk 0.2.0 → 0.4.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 +28 -2
- package/dist/cache/index.d.mts +7 -4
- package/dist/cache/index.mjs +24 -9
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{client-B8fSsx4L.d.mts → client-B--FzoDe.d.mts} +27 -6
- package/dist/{client-Bv0yCuwC.mjs → client-DkSP1q63.mjs} +68 -18
- package/dist/client-DkSP1q63.mjs.map +1 -0
- package/dist/codegen/cli.mjs +37 -4
- package/dist/codegen/cli.mjs.map +1 -1
- package/dist/{events-DpSWzSN9.d.mts → events-Bp__8_4V.d.mts} +17 -6
- package/dist/{events-Cqj9Rbon.mjs → events-DD5dodtF.mjs} +54 -9
- package/dist/events-DD5dodtF.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/tanstack/index.d.mts +6 -4
- package/dist/tanstack/index.mjs +9 -3
- package/dist/tanstack/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/client-Bv0yCuwC.mjs.map +0 -1
- package/dist/events-Cqj9Rbon.mjs.map +0 -1
|
@@ -45,16 +45,27 @@ interface EventsConfig {
|
|
|
45
45
|
readonly replay?: "persistent" | "none";
|
|
46
46
|
readonly keepalive_seconds?: number;
|
|
47
47
|
}
|
|
48
|
+
/** Opaque identity of one connection of a hub; a new one per connect. */
|
|
49
|
+
type EventConnection = object;
|
|
48
50
|
/**
|
|
49
51
|
* What a subscriber wants to hear. A handler that throws must not kill the
|
|
50
52
|
* stream, so errors are swallowed; handlers run one after another, in order.
|
|
51
53
|
*/
|
|
52
54
|
interface EventHandlers {
|
|
53
55
|
onEvent(event: CrudEvent): MaybePromise<void>;
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
/**
|
|
57
|
+
* After every connect (the first included), and for an event with no usable
|
|
58
|
+
* record id: clear the resource. `connection` is set for the connect reset
|
|
59
|
+
* only, and is the same object `onStatus(true)` then receives.
|
|
60
|
+
*/
|
|
61
|
+
onReset?(connection?: EventConnection): MaybePromise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* `true` when the stream is connected, `false` on any loss, exit or
|
|
64
|
+
* unsubscribe. `connection` identifies the connection when `connected`.
|
|
65
|
+
* A subscriber that joined after the connect reset gets `true` without
|
|
66
|
+
* having been sent `onReset` for that connection.
|
|
67
|
+
*/
|
|
68
|
+
onStatus?(connected: boolean, connection?: EventConnection): MaybePromise<void>;
|
|
58
69
|
}
|
|
59
70
|
/** A shared event stream: `subscribe` returns the (idempotent) unsubscribe. */
|
|
60
71
|
interface EventHub {
|
|
@@ -84,5 +95,5 @@ declare function getEventHub(options: EventHubOptions): EventHub;
|
|
|
84
95
|
*/
|
|
85
96
|
declare function subscribeEvents(options: SubscribeEventsOptions): () => void;
|
|
86
97
|
//#endregion
|
|
87
|
-
export {
|
|
88
|
-
//# sourceMappingURL=events-
|
|
98
|
+
export { EventsConfig as a, subscribeEvents as c, cachePolicyFromManifest as d, tanstackCachePolicy as f, EventHubOptions as i, CachePolicy as l, EventHandlers as n, SubscribeEventsOptions as o, EventHub as r, getEventHub as s, EventConnection as t, ManifestCacheHints as u };
|
|
99
|
+
//# sourceMappingURL=events-Bp__8_4V.d.mts.map
|
|
@@ -25,6 +25,37 @@ function tanstackCachePolicy(resource, overrides = {}) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
//#endregion
|
|
28
|
+
//#region src/reset-once.ts
|
|
29
|
+
const ledger = /* @__PURE__ */ new WeakMap();
|
|
30
|
+
/**
|
|
31
|
+
* Runs `reset` unless `owner`/`key` was already reset on `connection`; resolves
|
|
32
|
+
* when that reset is done, whoever started it. A failed reset is forgotten so
|
|
33
|
+
* a later subscriber can retry.
|
|
34
|
+
*/
|
|
35
|
+
function resetOnce(connection, owner, key, reset) {
|
|
36
|
+
let owners = ledger.get(connection);
|
|
37
|
+
if (owners === void 0) {
|
|
38
|
+
owners = /* @__PURE__ */ new WeakMap();
|
|
39
|
+
ledger.set(connection, owners);
|
|
40
|
+
}
|
|
41
|
+
let keys = owners.get(owner);
|
|
42
|
+
if (keys === void 0) {
|
|
43
|
+
keys = /* @__PURE__ */ new Map();
|
|
44
|
+
owners.set(owner, keys);
|
|
45
|
+
}
|
|
46
|
+
const done = keys.get(key);
|
|
47
|
+
if (done !== void 0) return done;
|
|
48
|
+
const started = (async () => {
|
|
49
|
+
await reset();
|
|
50
|
+
})();
|
|
51
|
+
keys.set(key, started);
|
|
52
|
+
const forget = keys;
|
|
53
|
+
started.catch(() => {
|
|
54
|
+
forget.delete(key);
|
|
55
|
+
});
|
|
56
|
+
return started;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
28
59
|
//#region node_modules/.pnpm/eventsource-parser@4.1.2/node_modules/eventsource-parser/dist/errors.js
|
|
29
60
|
/**
|
|
30
61
|
* Error thrown when encountering an issue during parsing.
|
|
@@ -403,6 +434,13 @@ function isPotentialField(line, field) {
|
|
|
403
434
|
* covered even without replay. A stream silent for three keep-alive
|
|
404
435
|
* intervals is dead and is reconnected with exponential backoff and jitter.
|
|
405
436
|
* Events invalidate; they never apply snapshots.
|
|
437
|
+
*
|
|
438
|
+
* Every connection has an identity (an opaque {@link EventConnection}) that
|
|
439
|
+
* the hub passes to `onReset` (for the connect reset) and to `onStatus(true)`.
|
|
440
|
+
* A subscriber that joins an already connected hub, or during the handshake's
|
|
441
|
+
* reset fan-out, gets no connect reset; it sees `onStatus(true)` with a
|
|
442
|
+
* connection it was never reset for and resets itself (see `invalidateOnEvents`
|
|
443
|
+
* and `bridgeEvents`).
|
|
406
444
|
*/
|
|
407
445
|
const BACKOFF_START_MS = 1e3;
|
|
408
446
|
const BACKOFF_MAX_MS = 3e4;
|
|
@@ -436,6 +474,7 @@ var Hub = class {
|
|
|
436
474
|
onEmpty;
|
|
437
475
|
controller;
|
|
438
476
|
connected = false;
|
|
477
|
+
connection;
|
|
439
478
|
subscriberId;
|
|
440
479
|
attempt = 0;
|
|
441
480
|
constructor(options, onEmpty) {
|
|
@@ -445,14 +484,14 @@ var Hub = class {
|
|
|
445
484
|
subscribe(handlers) {
|
|
446
485
|
const entry = {
|
|
447
486
|
onEvent: (event) => handlers.onEvent(event),
|
|
448
|
-
onReset: () => handlers.onReset?.(),
|
|
449
|
-
onStatus: (connected) => handlers.onStatus?.(connected)
|
|
487
|
+
onReset: (connection) => handlers.onReset?.(connection),
|
|
488
|
+
onStatus: (connected, connection) => handlers.onStatus?.(connected, connection)
|
|
450
489
|
};
|
|
451
490
|
this.handlers.add(entry);
|
|
452
491
|
if (this.controller === void 0) {
|
|
453
492
|
this.controller = new AbortController();
|
|
454
493
|
this.run(this.controller.signal);
|
|
455
|
-
} else if (this.connected) this.guard(() => entry.onStatus?.(true));
|
|
494
|
+
} else if (this.connected) this.guard(() => entry.onStatus?.(true, this.connection));
|
|
456
495
|
return () => {
|
|
457
496
|
if (!this.handlers.delete(entry)) return;
|
|
458
497
|
this.guard(() => entry.onStatus?.(false));
|
|
@@ -471,10 +510,11 @@ var Hub = class {
|
|
|
471
510
|
async fan(call) {
|
|
472
511
|
for (const handlers of Array.from(this.handlers)) await this.guard(() => call(handlers));
|
|
473
512
|
}
|
|
474
|
-
async setConnected(connected) {
|
|
513
|
+
async setConnected(connected, connection) {
|
|
475
514
|
if (this.connected === connected) return;
|
|
476
515
|
this.connected = connected;
|
|
477
|
-
|
|
516
|
+
this.connection = connected ? connection : void 0;
|
|
517
|
+
await this.fan((handlers) => handlers.onStatus?.(connected, connection));
|
|
478
518
|
}
|
|
479
519
|
backoff() {
|
|
480
520
|
const base = Math.min(BACKOFF_START_MS * 2 ** this.attempt, BACKOFF_MAX_MS);
|
|
@@ -552,8 +592,13 @@ var Hub = class {
|
|
|
552
592
|
if ("subscriber_id" in record) {
|
|
553
593
|
this.subscriberId = String(record["subscriber_id"]);
|
|
554
594
|
this.attempt = 0;
|
|
555
|
-
|
|
556
|
-
|
|
595
|
+
const connection = {};
|
|
596
|
+
const present = new Set(this.handlers);
|
|
597
|
+
await this.fan((handlers) => handlers.onReset?.(connection));
|
|
598
|
+
if (this.connected) {
|
|
599
|
+
this.connection = connection;
|
|
600
|
+
for (const handlers of Array.from(this.handlers)) if (!present.has(handlers)) await this.guard(() => handlers.onStatus?.(true, connection));
|
|
601
|
+
} else await this.setConnected(true, connection);
|
|
557
602
|
return;
|
|
558
603
|
}
|
|
559
604
|
if (record["resource"] !== this.options.events.resource) return;
|
|
@@ -611,6 +656,6 @@ function subscribeEvents(options) {
|
|
|
611
656
|
};
|
|
612
657
|
}
|
|
613
658
|
//#endregion
|
|
614
|
-
export { tanstackCachePolicy as i, subscribeEvents as n,
|
|
659
|
+
export { tanstackCachePolicy as a, cachePolicyFromManifest as i, subscribeEvents as n, resetOnce as r, getEventHub as t };
|
|
615
660
|
|
|
616
|
-
//# sourceMappingURL=events-
|
|
661
|
+
//# sourceMappingURL=events-DD5dodtF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-DD5dodtF.mjs","names":[],"sources":["../src/cache/policy.ts","../src/reset-once.ts","../node_modules/.pnpm/eventsource-parser@4.1.2/node_modules/eventsource-parser/dist/errors.js","../node_modules/.pnpm/eventsource-parser@4.1.2/node_modules/eventsource-parser/dist/parse.js","../src/events.ts"],"sourcesContent":["/** Per-resource cache policy, defaulted from the manifest `cache` hints. */\n\n/**\n * How one resource is cached. Times are seconds. `maxAge` and\n * `staleWhileRevalidate` only take effect while the resource's SSE stream is\n * running; without it every read revalidates. `statsTtl` bounds the staleness\n * of `stats`/`predict` on its own, as the server caches those bodies for the\n * same time. `revalidateOnly` means no fresh window and no\n * stale-while-revalidate.\n */\nexport interface CachePolicy {\n readonly enabled: boolean;\n readonly maxAge: number;\n readonly statsTtl: number;\n readonly staleWhileRevalidate: number;\n readonly etag: boolean;\n readonly revalidateOnly: boolean;\n}\n\n/** The manifest resource members a policy reads (I1); everything else is ignored. */\nexport interface ManifestCacheHints {\n readonly cache?: {\n readonly max_age?: unknown;\n readonly stats_ttl?: unknown;\n readonly stale_while_revalidate?: unknown;\n readonly etag?: unknown;\n readonly revalidate_only?: unknown;\n } | null;\n}\n\nfunction seconds(value: unknown): number {\n return typeof value === \"number\" && Number.isFinite(value)\n ? Math.max(value, 0)\n : 0;\n}\n\n/** Build a policy from a manifest resource object; `overrides` win over the hints. */\nexport function cachePolicyFromManifest(\n resource: ManifestCacheHints,\n overrides: Partial<CachePolicy> = {},\n): CachePolicy {\n const hints = resource.cache ?? {};\n return {\n enabled: overrides.enabled ?? true,\n maxAge: overrides.maxAge ?? seconds(hints.max_age),\n statsTtl: overrides.statsTtl ?? seconds(hints.stats_ttl),\n staleWhileRevalidate:\n overrides.staleWhileRevalidate ?? seconds(hints.stale_while_revalidate),\n etag: overrides.etag ?? hints.etag !== false,\n revalidateOnly: overrides.revalidateOnly ?? hints.revalidate_only === true,\n };\n}\n\n/**\n * The policy to use under TanStack Query: its `staleTime` decides freshness,\n * so the SDK cache only revalidates (ETag/304) unless told otherwise.\n */\nexport function tanstackCachePolicy(\n resource: ManifestCacheHints,\n overrides: Partial<CachePolicy> = {},\n): CachePolicy {\n return cachePolicyFromManifest(resource, {\n revalidateOnly: true,\n ...overrides,\n });\n}\n","/**\n * \"Reset once per target per connection\" (ADR 0002). A subscriber that is told\n * about a connection runs its reset through here; a second subscriber with the\n * same target (the same cache, or the same QueryClient, and resource) on the\n * same connection joins the first one's reset instead of running another.\n */\nimport type { EventConnection } from \"./events.ts\";\n\nconst ledger = new WeakMap<\n EventConnection,\n WeakMap<object, Map<string, Promise<void>>>\n>();\n\n/**\n * Runs `reset` unless `owner`/`key` was already reset on `connection`; resolves\n * when that reset is done, whoever started it. A failed reset is forgotten so\n * a later subscriber can retry.\n */\nexport function resetOnce(\n connection: EventConnection,\n owner: object,\n key: string,\n reset: () => Promise<void> | void,\n): Promise<void> {\n let owners = ledger.get(connection);\n if (owners === undefined) {\n owners = new WeakMap();\n ledger.set(connection, owners);\n }\n let keys = owners.get(owner);\n if (keys === undefined) {\n keys = new Map();\n owners.set(owner, keys);\n }\n const done = keys.get(key);\n if (done !== undefined) {\n return done;\n }\n const started = (async (): Promise<void> => {\n await reset();\n })();\n keys.set(key, started);\n const forget = keys;\n started.catch(() => {\n forget.delete(key);\n });\n return started;\n}\n","/**\n * Error thrown when encountering an issue during parsing.\n *\n * @public\n */\nexport class ParseError extends Error {\n constructor(message, options) {\n super(message);\n this.name = 'ParseError';\n this.type = options.type;\n this.field = options.field;\n this.value = options.value;\n this.line = options.line;\n }\n}\n//# sourceMappingURL=errors.js.map","/**\n * EventSource/Server-Sent Events parser\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nimport { ParseError } from './errors.js';\n// ASCII codes used in the hot parsing paths.\nconst LF = 10;\nconst CR = 13;\nconst SPACE = 32;\nconst MAX_FIELD_PREFIX_LENGTH = 6;\n/**\n * Creates a new EventSource parser.\n *\n * @param config - Parser configuration. Accepts callbacks (see {@link ParserCallbacks})\n * and options like `maxBufferSize` (see {@link ParserConfig}).\n *\n * @returns A new EventSource parser, with `feed` and `reset` methods.\n * @public\n */\nexport function createParser(config) {\n if (typeof config === 'function') {\n throw new TypeError('`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?');\n }\n const { maxBufferSize, onComment, onError, onEvent, onId, onRetry } = config;\n // Trailing bytes from prior `feed()` calls that did not yet form a complete line.\n // Stored as an array of fragments and only joined when a line terminator arrives.\n // Concatenating per-feed (`prefix + chunk`) is O(N²) when a single SSE line spans\n // many chunks (e.g. a large `data:` payload streamed in tiny slices, or an MCP-style\n // server that emits one giant content block). Buffering as fragments + joining once\n // makes the same workload linear.\n const pendingFragments = [];\n // Running total of `pendingFragments` lengths, kept in sync with the array so the\n // `maxBufferSize` check doesn't have to walk the fragment list on every feed.\n let pendingFragmentsLength = 0;\n // Empty or partial leading BOM; undefined once the BOM check is complete.\n let bomPrefix = '';\n let id;\n let data = '';\n let dataLines = 0;\n let eventType;\n // Set after a `maxBufferSize` overflow. Once tripped, `feed()` throws until\n // `reset()` is called — see the comment on `maxBufferSize` in `ParserConfig`.\n let terminated = false;\n let skippingLine = false;\n // Set when a line (parsed or discarded) was terminated by a trailing `\\r` at the very\n // end of a chunk. That `\\r` is ambiguous: it may be a bare-CR terminator or the first\n // half of a `\\r\\n` whose `\\n` lands in the next chunk. The line itself is complete\n // either way, but we must remember to swallow a single leading `\\n` from the next\n // chunk so the pair is treated as one terminator rather than a blank line (which\n // would dispatch an event prematurely).\n let skipNextLineFeed = false;\n /**\n * Feeds a chunk of the SSE stream to the parser. Any trailing bytes that do\n * not yet form a complete line are held back and prepended to the next chunk,\n * so callers can pass arbitrary slices of the stream without worrying about\n * line boundaries.\n *\n * Per the SSE spec, one leading UTF-8 BOM is stripped before parsing,\n * even when split across chunks. This handles both the raw 3-byte form (0xEF 0xBB\n * 0xBF) and a single decoded U+FEFF, so a leading BOM is ignored regardless of\n * how the caller decoded the bytes.\n *\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream\n */\n function feed(chunk) {\n if (terminated) {\n throw new Error('Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.');\n }\n if (bomPrefix !== undefined) {\n chunk = bomPrefix + chunk;\n // Wait only while the input could still be a raw BOM. Empty chunks must\n // not finish the check, and a mismatched prefix must remain ordinary input.\n if (chunk === '' || chunk === '\\xEF' || chunk === '\\xEF\\xBB') {\n bomPrefix = chunk;\n return;\n }\n bomPrefix = undefined;\n // Strip exactly one leading BOM, in decoded or raw byte-valued form.\n chunk = chunk.replace(/^(?:\\uFEFF|\\xEF\\xBB\\xBF)/, '');\n }\n // Rare resume states from a prior chunk boundary: a pending `\\r\\n` split or a\n // line being discarded. Single combined check keeps the hot path to one branch;\n // the actual handling lives out-of-line in `resumeAfterSkip`.\n if (skippingLine || skipNextLineFeed) {\n chunk = resumeAfterSkip(chunk);\n if (!chunk) {\n return;\n }\n }\n // Hot path: no buffered prefix from a prior partial line. Hand the chunk\n // straight to `processLines`, exactly like the original implementation.\n // Zero new work in the common case (every chunk ends with `\\n\\n`).\n if (!pendingFragments.length) {\n const trailing = processLines(chunk);\n if (trailing !== '') {\n storeTrailing(trailing);\n }\n checkForBufferOverflow();\n return;\n }\n // We have a buffered prefix. If this chunk also has no terminator, append\n // to the buffer without concatenating — that's the O(N²) trap we're\n // avoiding (large single `data:` payload split across many tiny chunks).\n if (chunk.indexOf('\\n') === -1 && chunk.indexOf('\\r') === -1) {\n if (pendingFragmentsLength < MAX_FIELD_PREFIX_LENGTH) {\n const head = pendingFragments.join('') +\n chunk.slice(0, MAX_FIELD_PREFIX_LENGTH - pendingFragmentsLength);\n if (!shouldBufferTrailing(head)) {\n pendingFragments.length = 0;\n pendingFragmentsLength = 0;\n skippingLine = true;\n return;\n }\n }\n pendingFragments.push(chunk);\n pendingFragmentsLength += chunk.length;\n checkForBufferOverflow();\n return;\n }\n // Terminator arrived. Join the accumulated fragments + this chunk once,\n // process, and buffer any new trailing partial line.\n pendingFragments.push(chunk);\n const input = pendingFragments.join('');\n pendingFragments.length = 0;\n pendingFragmentsLength = 0;\n storeTrailing(processLines(input));\n checkForBufferOverflow();\n }\n // Out-of-line handler for the rare post-boundary states. `skipNextLineFeed` means a\n // line (parsed or discarded) ended with a trailing `\\r` at the previous chunk boundary,\n // so a single leading `\\n` must be swallowed to consume a split `\\r\\n` as one terminator.\n // `skippingLine` means we are discarding an invalid line until its terminator arrives.\n // The two states are mutually exclusive. Returns the remaining chunk to parse ('' if\n // this chunk was fully consumed).\n function resumeAfterSkip(chunk) {\n if (chunk.length === 0) {\n return chunk;\n }\n if (skipNextLineFeed) {\n skipNextLineFeed = false;\n return chunk.charCodeAt(0) === LF ? chunk.slice(1) : chunk;\n }\n const crIndex = chunk.indexOf('\\r');\n const lfIndex = chunk.indexOf('\\n');\n const lineEnd = crIndex === -1 ? lfIndex : lfIndex === -1 ? crIndex : crIndex < lfIndex ? crIndex : lfIndex;\n if (lineEnd === -1) {\n return '';\n }\n // Trailing `\\r` at the very end of the chunk: defer the CRLF/CR decision to the\n // next chunk (see `skipNextLineFeed`) rather than greedily consuming just the `\\r`.\n if (lineEnd === chunk.length - 1 && chunk.charCodeAt(lineEnd) === CR) {\n skippingLine = false;\n skipNextLineFeed = true;\n return '';\n }\n skippingLine = false;\n return chunk.slice(lineEnd + (chunk.charCodeAt(lineEnd) === CR && chunk.charCodeAt(lineEnd + 1) === LF ? 2 : 1));\n }\n function storeTrailing(trailing) {\n if (!trailing)\n return;\n // A trailing that ends with `\\r` is not a partial line: the `\\r` terminates it,\n // and only the CRLF-vs-CR ambiguity remains (the matching `\\n` may start the next\n // chunk). Parse the completed line now — buffering it could grow unbounded through\n // the no-terminator append path, and discarding it would eat the terminator and\n // make skip mode swallow the next line.\n if (trailing.charCodeAt(trailing.length - 1) === CR) {\n parseLine(trailing, 0, trailing.length - 1);\n if (checkForBufferOverflow())\n return;\n skipNextLineFeed = true;\n return;\n }\n if (shouldBufferTrailing(trailing)) {\n pendingFragments.push(trailing);\n pendingFragmentsLength = trailing.length;\n return;\n }\n skippingLine = true;\n }\n function shouldBufferTrailing(trailing) {\n const firstCharCode = trailing.charCodeAt(0);\n return ((firstCharCode === 58 && !!onComment) ||\n (firstCharCode === 100 && isPotentialField(trailing, 'data')) ||\n (firstCharCode === 101 && isPotentialField(trailing, 'event')) ||\n (firstCharCode === 105 && isPotentialField(trailing, 'id')) ||\n (firstCharCode === 114 && isPotentialField(trailing, 'retry')));\n }\n // Returns true when parsing must stop for the current chunk (the parser was already\n // terminated or has just overflowed), and false when processing can continue.\n function checkForBufferOverflow() {\n if (terminated)\n return true;\n if (maxBufferSize === undefined)\n return false;\n if (pendingFragmentsLength + data.length <= maxBufferSize)\n return false;\n terminated = true;\n pendingFragments.length = 0;\n pendingFragmentsLength = 0;\n id = undefined;\n data = '';\n dataLines = 0;\n eventType = undefined;\n skippingLine = false;\n skipNextLineFeed = false;\n onError === null || onError === void 0 ? void 0 : onError(new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: 'max-buffer-size-exceeded',\n }));\n return true;\n }\n /**\n * Splits `chunk` into SSE lines and dispatches each to the appropriate handler.\n * Returns any trailing bytes that did not terminate with a line break, so the\n * caller can prepend them to the next chunk.\n *\n * The SSE spec permits three line terminators: `\\n`, `\\r`, and `\\r\\n`. Real-world\n * streams almost always use plain `\\n`, so we take a fast path when no `\\r` is\n * present in the chunk. The slow path is spec-correct but does more work per line.\n */\n function processLines(chunk) {\n let searchIndex = 0;\n // Fast path: LF-only chunk (the common case for typical SSE servers).\n // We can scan forward with a single `indexOf('\\n')` per line and inline\n // the hot-path branches for `data:` and `event:` without the CR bookkeeping\n // the slow path needs.\n if (chunk.indexOf('\\r') === -1) {\n let lfIndex = chunk.indexOf('\\n', searchIndex);\n while (lfIndex !== -1) {\n // Blank line: end-of-event marker. Dispatch the accumulated event (if any)\n // and reset the buffered fields. This is hoisted out of `parseLine` because\n // it's the single most common line shape after `data:` lines.\n if (searchIndex === lfIndex) {\n if (id !== undefined) {\n onId === null || onId === void 0 ? void 0 : onId(id);\n }\n if (dataLines > 0) {\n onEvent === null || onEvent === void 0 ? void 0 : onEvent({ id, event: eventType, data });\n }\n id = undefined;\n data = '';\n dataLines = 0;\n eventType = undefined;\n searchIndex = lfIndex + 1;\n lfIndex = chunk.indexOf('\\n', searchIndex);\n continue;\n }\n const firstCharCode = chunk.charCodeAt(searchIndex);\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n // `data:` line — append the value to the event's data buffer.\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5;\n const value = chunk.slice(valueStart, lfIndex);\n // Fast path within a fast path: if this is the first data line AND the\n // next char is another LF (i.e. `data:foo\\n\\n`), dispatch immediately\n // without ever writing to the `data` buffer. This is the shape of a\n // typical single-line SSE event (ChatGPT-style streams, etc.) and is\n // hot enough to be worth the duplication.\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n if (id !== undefined) {\n onId === null || onId === void 0 ? void 0 : onId(id);\n }\n onEvent === null || onEvent === void 0 ? void 0 : onEvent({ id, event: eventType, data: value });\n id = undefined;\n data = '';\n eventType = undefined;\n searchIndex = lfIndex + 2;\n lfIndex = chunk.indexOf('\\n', searchIndex);\n continue;\n }\n // Multi-line data: concatenate with newline separator per spec.\n data = dataLines === 0 ? value : `${data}\\n${value}`;\n dataLines++;\n }\n else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {\n // `event:` line — set the event type for the next dispatch. Per spec,\n // an empty value resets `event type` to its default (undefined here).\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6, lfIndex) || undefined;\n }\n else {\n // Everything else: `id:`, `retry:`, comment lines (`:` prefix), unknown\n // fields, or malformed lines. These are rarer and go through the full\n // per-line parser, which handles the SSE field grammar in detail.\n parseLine(chunk, searchIndex, lfIndex);\n }\n if (checkForBufferOverflow())\n return '';\n searchIndex = lfIndex + 1;\n lfIndex = chunk.indexOf('\\n', searchIndex);\n }\n return chunk.slice(searchIndex);\n }\n // Slow path: the chunk contains at least one `\\r`, so lines may be terminated\n // by `\\r`, `\\n`, or `\\r\\n`. We locate the next terminator by looking at both\n // the nearest `\\r` and `\\n` and picking whichever comes first.\n while (searchIndex < chunk.length) {\n const crIndex = chunk.indexOf('\\r', searchIndex);\n const lfIndex = chunk.indexOf('\\n', searchIndex);\n let lineEnd = -1;\n if (crIndex !== -1 && lfIndex !== -1) {\n lineEnd = crIndex < lfIndex ? crIndex : lfIndex;\n }\n else if (crIndex !== -1) {\n // A trailing `\\r` at the very end of the chunk is ambiguous: it could be\n // a bare-CR terminator, or the first half of a `\\r\\n` whose `\\n` arrives\n // in the next chunk. Defer until we see more input.\n if (crIndex === chunk.length - 1) {\n lineEnd = -1;\n }\n else {\n lineEnd = crIndex;\n }\n }\n else if (lfIndex !== -1) {\n lineEnd = lfIndex;\n }\n if (lineEnd === -1) {\n break;\n }\n parseLine(chunk, searchIndex, lineEnd);\n if (checkForBufferOverflow())\n return '';\n searchIndex = lineEnd + 1;\n // If we just consumed a `\\r` and the next char is `\\n`, skip it so the\n // pair is treated as a single terminator rather than an empty line.\n if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {\n searchIndex++;\n }\n }\n return chunk.slice(searchIndex);\n }\n function parseLine(chunk, start, end) {\n if (start === end) {\n dispatchEvent();\n return;\n }\n const firstCharCode = chunk.charCodeAt(start);\n if (isDataPrefix(chunk, start, firstCharCode)) {\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5;\n const value = chunk.slice(valueStart, end);\n data = dataLines === 0 ? value : `${data}\\n${value}`;\n dataLines++;\n return;\n }\n if (isEventPrefix(chunk, start, firstCharCode)) {\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || undefined;\n return;\n }\n // Fast path for \"id:\" — 'i' = 105, 'd' = 100, ':' = 58\n if (firstCharCode === 105 &&\n chunk.charCodeAt(start + 1) === 100 &&\n chunk.charCodeAt(start + 2) === 58) {\n // 'id:'.length === 3, 'id: '.length === 4\n const value = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);\n // If the field value does not contain U+0000 NULL, then set the `ID` buffer to\n // the field value. Otherwise, ignore the field.\n if (!value.includes('\\0'))\n id = value;\n return;\n }\n // Comment line — ':' = 58\n if (firstCharCode === 58) {\n if (onComment) {\n const line = chunk.slice(start, end);\n // skip ':' (+1), or ': ' (+2) when a space follows\n onComment(line.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));\n }\n return;\n }\n const line = chunk.slice(start, end);\n const fieldSeparatorIndex = line.indexOf(':');\n if (fieldSeparatorIndex === -1) {\n processField(line, '', line);\n return;\n }\n const field = line.slice(0, fieldSeparatorIndex);\n // skip ':' (+1), or ': ' (+2) when a space follows\n const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1;\n const value = line.slice(fieldSeparatorIndex + offset);\n processField(field, value, line);\n }\n function processField(field, value, line) {\n // Field names must be compared literally, with no case folding performed.\n switch (field) {\n case 'event':\n // Set the `event type` buffer to field value\n eventType = value || undefined;\n break;\n case 'data':\n data = dataLines === 0 ? value : `${data}\\n${value}`;\n dataLines++;\n break;\n case 'id':\n // If the field value does not contain U+0000 NULL, then set the `ID` buffer to\n // the field value. Otherwise, ignore the field.\n if (!value.includes('\\0'))\n id = value;\n break;\n case 'retry':\n // If the field value consists of only ASCII digits, then interpret the field value as an\n // integer in base ten, and set the event stream's reconnection time to that integer.\n // Otherwise, ignore the field.\n if (/^\\d+$/.test(value)) {\n onRetry === null || onRetry === void 0 ? void 0 : onRetry(parseInt(value, 10));\n }\n else {\n onError === null || onError === void 0 ? void 0 : onError(new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: 'invalid-retry',\n value,\n line,\n }));\n }\n break;\n default:\n // Otherwise, the field is ignored.\n onError === null || onError === void 0 ? void 0 : onError(new ParseError(`Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}…` : field}\"`, { type: 'unknown-field', field, value, line }));\n break;\n }\n }\n function dispatchEvent() {\n if (id !== undefined) {\n onId === null || onId === void 0 ? void 0 : onId(id);\n }\n if (dataLines > 0) {\n onEvent === null || onEvent === void 0 ? void 0 : onEvent({\n id,\n event: eventType,\n data,\n });\n }\n id = undefined;\n data = '';\n dataLines = 0;\n eventType = undefined;\n }\n function reset(options = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join('');\n parseLine(incompleteLine, 0, incompleteLine.length);\n }\n bomPrefix = '';\n id = undefined;\n data = '';\n dataLines = 0;\n eventType = undefined;\n pendingFragments.length = 0;\n pendingFragmentsLength = 0;\n terminated = false;\n skippingLine = false;\n skipNextLineFeed = false;\n }\n return { feed, reset };\n}\n/**\n * Checks if `chunk` starts with the literal `data:` at index `i`.\n *\n * Equivalent to `chunk.startsWith('data:', i)`, but benchmarks show this\n * hand-unrolled char-code comparison is ~20% faster on common event types.\n * The caller passes `firstCharCode` (the code at `i`) so it can be reused\n * across prefix checks.\n *\n * ASCII: 'd' = 100, 'a' = 97, 't' = 116, 'a' = 97, ':' = 58\n */\nfunction isDataPrefix(chunk, i, firstCharCode) {\n return (firstCharCode === 100 &&\n chunk.charCodeAt(i + 1) === 97 &&\n chunk.charCodeAt(i + 2) === 116 &&\n chunk.charCodeAt(i + 3) === 97 &&\n chunk.charCodeAt(i + 4) === 58);\n}\n/**\n * Checks if `chunk` starts with the literal `event:` at index `i`.\n *\n * See {@link isDataPrefix} for why this is hand-unrolled rather than using\n * `String.prototype.startsWith`.\n *\n * ASCII: 'e' = 101, 'v' = 118, 'e' = 101, 'n' = 110, 't' = 116, ':' = 58\n */\nfunction isEventPrefix(chunk, i, firstCharCode) {\n return (firstCharCode === 101 &&\n chunk.charCodeAt(i + 1) === 118 &&\n chunk.charCodeAt(i + 2) === 101 &&\n chunk.charCodeAt(i + 3) === 110 &&\n chunk.charCodeAt(i + 4) === 116 &&\n chunk.charCodeAt(i + 5) === 58);\n}\nfunction isPotentialField(line, field) {\n let i = 1;\n while (i < line.length && i < field.length) {\n if (line.charCodeAt(i) !== field.charCodeAt(i)) {\n return false;\n }\n i++;\n }\n return line.length <= field.length || line.charCodeAt(field.length) === 58;\n}\n//# sourceMappingURL=parse.js.map","/**\n * SSE subscriber that turns `GET {base_path}/events` into callbacks.\n *\n * One connection per (fetch, url), reference-counted and fanned out to every\n * subscriber. The caller passes an already authenticated `fetch`: this is\n * fetch streaming, not `EventSource`, because `EventSource` cannot send an\n * Authorization header. Frames carry no `event:` name: the first has\n * `id: 0` and `{\"subscriber_id\": ...}`; later ones are record events. A\n * reconnect passes `?{resume_param}=<subscriber_id>` explicitly (never\n * `Last-Event-ID`, which the server would read as a subscriber id) when the\n * stream replays, and always tells subscribers to reset, so the gap is\n * covered even without replay. A stream silent for three keep-alive\n * intervals is dead and is reconnected with exponential backoff and jitter.\n * Events invalidate; they never apply snapshots.\n *\n * Every connection has an identity (an opaque {@link EventConnection}) that\n * the hub passes to `onReset` (for the connect reset) and to `onStatus(true)`.\n * A subscriber that joins an already connected hub, or during the handshake's\n * reset fan-out, gets no connect reset; it sees `onStatus(true)` with a\n * connection it was never reset for and resets itself (see `invalidateOnEvents`\n * and `bridgeEvents`).\n */\nimport { createParser } from \"eventsource-parser\";\nimport type { EventSourceMessage } from \"eventsource-parser\";\nimport type { CrudEvent, MaybePromise } from \"./interfaces.ts\";\n\n/** The manifest's `capabilities.events` object (I1), plain. */\nexport interface EventsConfig {\n /** The server's event label, not the manifest name. */\n readonly resource: string;\n readonly path?: string;\n readonly resume_param?: string;\n readonly replay?: \"persistent\" | \"none\";\n readonly keepalive_seconds?: number;\n}\n\n/** Opaque identity of one connection of a hub; a new one per connect. */\nexport type EventConnection = object;\n\n/**\n * What a subscriber wants to hear. A handler that throws must not kill the\n * stream, so errors are swallowed; handlers run one after another, in order.\n */\nexport interface EventHandlers {\n onEvent(event: CrudEvent): MaybePromise<void>;\n /**\n * After every connect (the first included), and for an event with no usable\n * record id: clear the resource. `connection` is set for the connect reset\n * only, and is the same object `onStatus(true)` then receives.\n */\n onReset?(connection?: EventConnection): MaybePromise<void>;\n /**\n * `true` when the stream is connected, `false` on any loss, exit or\n * unsubscribe. `connection` identifies the connection when `connected`.\n * A subscriber that joined after the connect reset gets `true` without\n * having been sent `onReset` for that connection.\n */\n onStatus?(\n connected: boolean,\n connection?: EventConnection,\n ): MaybePromise<void>;\n}\n\n/** A shared event stream: `subscribe` returns the (idempotent) unsubscribe. */\nexport interface EventHub {\n subscribe(handlers: EventHandlers): () => void;\n}\n\nexport interface EventHubOptions {\n /** Authenticated; shared hubs are keyed by this function and `url`. */\n readonly fetch: typeof fetch;\n /** Absolute URL of the events endpoint. */\n readonly url: string;\n readonly events: EventsConfig;\n /** Jitter source in `[0, 1)`; defaults to `Math.random`. */\n readonly random?: () => number;\n}\n\nexport interface SubscribeEventsOptions extends EventHubOptions, EventHandlers {\n /** Aborting unsubscribes. */\n readonly signal?: AbortSignal;\n}\n\nconst BACKOFF_START_MS = 1000;\nconst BACKOFF_MAX_MS = 30_000;\nconst DEFAULT_KEEPALIVE_S = 15;\nconst DEAD_AFTER_KEEPALIVES = 3;\nconst ACTIONS: ReadonlySet<unknown> = new Set([\n \"create\",\n \"update\",\n \"delete\",\n \"restore\",\n]);\n\nconst registry = new WeakMap<typeof fetch, Map<string, Hub>>();\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = (): void => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, ms);\n signal.addEventListener(\"abort\", done, { once: true });\n });\n}\n\nclass Hub {\n private readonly handlers = new Set<EventHandlers>();\n private readonly options: EventHubOptions;\n private readonly onEmpty: () => void;\n private controller: AbortController | undefined;\n private connected = false;\n private connection: EventConnection | undefined;\n private subscriberId: string | undefined;\n private attempt = 0;\n\n constructor(options: EventHubOptions, onEmpty: () => void) {\n this.options = options;\n this.onEmpty = onEmpty;\n }\n\n subscribe(handlers: EventHandlers): () => void {\n // The same object may subscribe twice; give each subscription its own identity.\n const entry: EventHandlers = {\n onEvent: (event) => handlers.onEvent(event),\n onReset: (connection) => handlers.onReset?.(connection),\n onStatus: (connected, connection) =>\n handlers.onStatus?.(connected, connection),\n };\n this.handlers.add(entry);\n if (this.controller === undefined) {\n this.controller = new AbortController();\n void this.run(this.controller.signal);\n } else if (this.connected) {\n void this.guard(() => entry.onStatus?.(true, this.connection));\n }\n return () => {\n if (!this.handlers.delete(entry)) {\n return;\n }\n void this.guard(() => entry.onStatus?.(false));\n if (this.handlers.size === 0) {\n // Abort the live stream instead of waiting for the next frame.\n this.controller?.abort();\n this.controller = undefined;\n this.onEmpty();\n }\n };\n }\n\n private async guard(\n call: () => MaybePromise<void> | undefined,\n ): Promise<void> {\n try {\n await call();\n } catch {\n // A failing subscriber must not take the stream down.\n }\n }\n\n private async fan(\n call: (handlers: EventHandlers) => MaybePromise<void> | undefined,\n ): Promise<void> {\n for (const handlers of Array.from(this.handlers)) {\n await this.guard(() => call(handlers));\n }\n }\n\n private async setConnected(\n connected: boolean,\n connection?: EventConnection,\n ): Promise<void> {\n if (this.connected === connected) {\n return;\n }\n this.connected = connected;\n this.connection = connected ? connection : undefined;\n await this.fan((handlers) => handlers.onStatus?.(connected, connection));\n }\n\n private backoff(): number {\n const base = Math.min(BACKOFF_START_MS * 2 ** this.attempt, BACKOFF_MAX_MS);\n this.attempt += 1;\n const jitter = (this.options.random ?? Math.random)();\n return Math.min(base * (1 + 0.5 * jitter), BACKOFF_MAX_MS);\n }\n\n /** Connect, read and reconnect until aborted. */\n private async run(signal: AbortSignal): Promise<void> {\n try {\n while (!signal.aborted) {\n try {\n await this.readOnce(signal);\n } catch {\n // Lost, refused or dead: reconnect below.\n }\n await this.setConnected(false);\n await sleep(this.backoff(), signal);\n }\n } finally {\n // On any exit, tell the subscribers so the freshness gate closes.\n await this.setConnected(false);\n }\n }\n\n private resumeUrl(): string {\n const { url, events } = this.options;\n if (events.replay !== \"persistent\" || this.subscriberId === undefined) {\n return url;\n }\n const param = events.resume_param ?? \"subscriber_id\";\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}${encodeURIComponent(param)}=${encodeURIComponent(this.subscriberId)}`;\n }\n\n private async readOnce(signal: AbortSignal): Promise<void> {\n const keepalive = this.options.events.keepalive_seconds;\n const deadMs =\n (keepalive ?? DEFAULT_KEEPALIVE_S) * DEAD_AFTER_KEEPALIVES * 1000;\n const conn = new AbortController();\n const abortConn = (): void => {\n conn.abort();\n };\n signal.addEventListener(\"abort\", abortConn, { once: true });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const arm = (): void => {\n clearTimeout(timer);\n timer = setTimeout(abortConn, deadMs);\n };\n try {\n arm();\n const response = await this.options.fetch(this.resumeUrl(), {\n headers: { accept: \"text/event-stream\" },\n signal: conn.signal,\n });\n if (!response.ok || response.body === null) {\n throw new Error(`event stream refused: HTTP ${response.status}`);\n }\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n const pending: EventSourceMessage[] = [];\n const parser = createParser({\n onEvent: (message) => {\n pending.push(message);\n },\n });\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n return;\n }\n arm(); // any byte, keep-alive comments included, proves the stream is alive\n parser.feed(decoder.decode(value, { stream: true }));\n for (const message of pending.splice(0)) {\n await this.handle(message);\n }\n }\n } finally {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", abortConn);\n conn.abort(); // release the connection on every way out\n }\n }\n\n private async handle(message: EventSourceMessage): Promise<void> {\n let data: unknown;\n try {\n data = JSON.parse(message.data);\n } catch {\n return;\n }\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return;\n }\n const record = data as Record<string, unknown>;\n if (\"subscriber_id\" in record) {\n this.subscriberId = String(record[\"subscriber_id\"]);\n this.attempt = 0;\n // Every connect, the first included: entries stored before this stream\n // (an earlier page session, a persisted store) may predate server changes.\n // A subscriber that joins while this fans out is not in it; it gets\n // `onStatus(true)` with this connection and resets itself.\n const connection: EventConnection = {};\n const present = new Set(this.handlers);\n await this.fan((handlers) => handlers.onReset?.(connection));\n if (this.connected) {\n // A repeated handshake on a live stream: whoever joined during the\n // fan-out was told about the old connection only.\n this.connection = connection;\n for (const handlers of Array.from(this.handlers)) {\n if (!present.has(handlers)) {\n await this.guard(() => handlers.onStatus?.(true, connection));\n }\n }\n } else {\n await this.setConnected(true, connection);\n }\n return;\n }\n if (record[\"resource\"] !== this.options.events.resource) {\n return;\n }\n const action = record[\"action\"];\n const recordId = record[\"record_id\"];\n if (!ACTIONS.has(action) || !Number.isInteger(recordId)) {\n await this.fan((handlers) => handlers.onReset?.());\n return;\n }\n const snapshot = record[\"snapshot\"];\n const event: CrudEvent = {\n resource: String(record[\"resource\"]),\n recordId: recordId as number,\n action: action as CrudEvent[\"action\"],\n snapshot:\n typeof snapshot === \"object\" && snapshot !== null\n ? (snapshot as Record<string, unknown>)\n : {},\n timestamp: String(record[\"timestamp\"] ?? \"\"),\n };\n await this.fan((handlers) => handlers.onEvent(event));\n }\n}\n\n/**\n * The shared hub for `(fetch, url)`: the first subscriber opens the stream,\n * the last one aborts it. A later subscribe after that opens a fresh one.\n */\nexport function getEventHub(options: EventHubOptions): EventHub {\n return {\n subscribe(handlers) {\n let hubs = registry.get(options.fetch);\n if (hubs === undefined) {\n hubs = new Map();\n registry.set(options.fetch, hubs);\n }\n const byUrl = hubs;\n let hub = byUrl.get(options.url);\n if (hub === undefined) {\n hub = new Hub(options, () => {\n byUrl.delete(options.url);\n });\n byUrl.set(options.url, hub);\n }\n return hub.subscribe(handlers);\n },\n };\n}\n\n/**\n * Subscribe to one resource's event stream. Returns the unsubscribe; the\n * last unsubscribe aborts the stream. `signal` aborting unsubscribes too.\n */\nexport function subscribeEvents(options: SubscribeEventsOptions): () => void {\n const { signal } = options;\n if (signal?.aborted === true) {\n return () => undefined;\n }\n const unsubscribe = getEventHub(options).subscribe(options);\n signal?.addEventListener(\"abort\", unsubscribe, { once: true });\n return () => {\n signal?.removeEventListener(\"abort\", unsubscribe);\n unsubscribe();\n };\n}\n"],"ignoreList":[2,3],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as Middleware, c as RequestContext, i as MaybePromise, l as TokenProvider, n as CacheStore, o as Next, r as CrudEvent, s as Op, t as CacheEntry, u as composeMiddleware } from "./interfaces-s73cVfIF.mjs";
|
|
2
|
-
import { A as Pagination, C as Capabilities, D as JsonObject, E as GetJson, F as manifestUrlFromCatalog, I as parseManifest, M as Schemas, N as findResource, O as Manifest, P as loadManifest, S as CacheHints, T as FieldSpec, _ as encodeParams, a as PredictOptions, b as AuthSpec, c as RestoreOptions, d as createClient, f as Filters, g as buildQuery, h as Sort, i as ListOptions, j as Resource, k as PROFILE, l as StatsOptions, m as QueryParams, n as ClientOptions, o as RequestOptions, p as QueryField, r as CrudClient, s as ResourceClient, t as BulkResult, u as WriteOptions, v as formatScalar, w as EventsSpec, x as CATALOG_PATH, y as quote } from "./client-
|
|
2
|
+
import { A as Pagination, C as Capabilities, D as JsonObject, E as GetJson, F as manifestUrlFromCatalog, I as parseManifest, M as Schemas, N as findResource, O as Manifest, P as loadManifest, S as CacheHints, T as FieldSpec, _ as encodeParams, a as PredictOptions, b as AuthSpec, c as RestoreOptions, d as createClient, f as Filters, g as buildQuery, h as Sort, i as ListOptions, j as Resource, k as PROFILE, l as StatsOptions, m as QueryParams, n as ClientOptions, o as RequestOptions, p as QueryField, r as CrudClient, s as ResourceClient, t as BulkResult, u as WriteOptions, v as formatScalar, w as EventsSpec, x as CATALOG_PATH, y as quote } from "./client-B--FzoDe.mjs";
|
|
3
3
|
//#region src/auth.d.ts
|
|
4
4
|
/** The scope of an anonymous caller. */
|
|
5
5
|
export declare const ANON = "anon";
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as UnknownResourceError, C as NotFound, D as ResponseError, E as RateLimited, M as problemFromResponse, N as composeMiddleware, O as ServerError, S as ManifestError, T as ProblemError, _ as ConnectionFailed, a as encodeParams, b as InvalidQueryError, c as CATALOG_PATH, d as loadManifest, f as manifestUrlFromCatalog, g as ClientError, h as CapabilityError, i as buildQuery, j as ValidationFailed, k as Unauthorized, l as PROFILE, m as BadRequest, n as ResourceClient, o as formatScalar, p as parseManifest, r as createClient, s as quote, t as CrudClient, u as findResource, v as CrudError, w as PreconditionFailed, x as Locked, y as Forbidden } from "./client-
|
|
1
|
+
import { A as UnknownResourceError, C as NotFound, D as ResponseError, E as RateLimited, M as problemFromResponse, N as composeMiddleware, O as ServerError, S as ManifestError, T as ProblemError, _ as ConnectionFailed, a as encodeParams, b as InvalidQueryError, c as CATALOG_PATH, d as loadManifest, f as manifestUrlFromCatalog, g as ClientError, h as CapabilityError, i as buildQuery, j as ValidationFailed, k as Unauthorized, l as PROFILE, m as BadRequest, n as ResourceClient, o as formatScalar, p as parseManifest, r as createClient, s as quote, t as CrudClient, u as findResource, v as CrudError, w as PreconditionFailed, x as Locked, y as Forbidden } from "./client-DkSP1q63.mjs";
|
|
2
2
|
import { n as cacheScope, r as staticToken, t as ANON } from "./auth-DwtNEKrw.mjs";
|
|
3
3
|
export { ANON, BadRequest, CATALOG_PATH, CapabilityError, ClientError, ConnectionFailed, CrudClient, CrudError, Forbidden, InvalidQueryError, Locked, ManifestError, NotFound, PROFILE, PreconditionFailed, ProblemError, RateLimited, ResourceClient, ResponseError, ServerError, Unauthorized, UnknownResourceError, ValidationFailed, buildQuery, cacheScope, composeMiddleware, createClient, encodeParams, findResource, formatScalar, loadManifest, manifestUrlFromCatalog, parseManifest, problemFromResponse, quote, staticToken };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as CrudEvent } from "../interfaces-s73cVfIF.mjs";
|
|
2
|
-
import { a as
|
|
3
|
-
import { D as JsonObject, O as Manifest, r as CrudClient, s as ResourceClient } from "../client-
|
|
2
|
+
import { a as EventsConfig, c as subscribeEvents, f as tanstackCachePolicy, i as EventHubOptions, l as CachePolicy, n as EventHandlers, o as SubscribeEventsOptions, r as EventHub, s as getEventHub, t as EventConnection, u as ManifestCacheHints } from "../events-Bp__8_4V.mjs";
|
|
3
|
+
import { D as JsonObject, O as Manifest, r as CrudClient, s as ResourceClient } from "../client-B--FzoDe.mjs";
|
|
4
4
|
import { DataTag, QueryClient } from "@tanstack/query-core";
|
|
5
5
|
//#region src/tanstack/adapter.d.ts
|
|
6
6
|
type Init = {
|
|
@@ -237,9 +237,11 @@ export declare function deleteMutationOptions(queryClient: Invalidator, ns: Crud
|
|
|
237
237
|
* invalidate and never apply snapshots: `create` invalidates lists, stats and
|
|
238
238
|
* predict; `update`/`delete`/`restore` also the record and its revisions; a
|
|
239
239
|
* reconnect (or an event with no usable record id) invalidates the resource.
|
|
240
|
-
*
|
|
240
|
+
* A bridge that joins an already connected hub (or during the handshake)
|
|
241
|
+
* invalidates the resource once on subscribing, unless this `queryClient` was
|
|
242
|
+
* already reset for the resource on that connection. Returns the unsubscribe.
|
|
241
243
|
*/
|
|
242
244
|
export declare function bridgeEvents(queryClient: Pick<QueryClient, "invalidateQueries">, hub: EventHub, target: CrudTarget): () => void;
|
|
243
245
|
//#endregion
|
|
244
|
-
export { type CachePolicy, type CrudEvent, type EventHandlers, type EventHub, type EventHubOptions, type EventsConfig, type ManifestCacheHints, type SubscribeEventsOptions, type TanstackApi, getEventHub, subscribeEvents, tanstackCachePolicy };
|
|
246
|
+
export { type CachePolicy, type CrudEvent, type EventConnection, type EventHandlers, type EventHub, type EventHubOptions, type EventsConfig, type ManifestCacheHints, type SubscribeEventsOptions, type TanstackApi, getEventHub, subscribeEvents, tanstackCachePolicy };
|
|
245
247
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/tanstack/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as tanstackCachePolicy, n as subscribeEvents, r as resetOnce, t as getEventHub } from "../events-DD5dodtF.mjs";
|
|
2
2
|
//#region src/tanstack/adapter.ts
|
|
3
3
|
/**
|
|
4
4
|
* A query is the client's `ListOptions`, `StatsOptions` or `PredictOptions`
|
|
@@ -297,16 +297,22 @@ function deleteMutationOptions(queryClient, ns, resource, api) {
|
|
|
297
297
|
* invalidate and never apply snapshots: `create` invalidates lists, stats and
|
|
298
298
|
* predict; `update`/`delete`/`restore` also the record and its revisions; a
|
|
299
299
|
* reconnect (or an event with no usable record id) invalidates the resource.
|
|
300
|
-
*
|
|
300
|
+
* A bridge that joins an already connected hub (or during the handshake)
|
|
301
|
+
* invalidates the resource once on subscribing, unless this `queryClient` was
|
|
302
|
+
* already reset for the resource on that connection. Returns the unsubscribe.
|
|
301
303
|
*/
|
|
302
304
|
function bridgeEvents(queryClient, hub, target) {
|
|
305
|
+
const reset = (connection) => resetOnce(connection, queryClient, JSON.stringify([target.client, target.resource]), () => invalidateResource(queryClient, target));
|
|
303
306
|
return hub.subscribe({
|
|
304
307
|
onEvent: async (event) => {
|
|
305
308
|
const pending = [invalidateCollections(queryClient, target)];
|
|
306
309
|
if (event.action !== "create") pending.push(invalidateRecord(queryClient, target, event.recordId));
|
|
307
310
|
await Promise.all(pending);
|
|
308
311
|
},
|
|
309
|
-
onReset: () => invalidateResource(queryClient, target)
|
|
312
|
+
onReset: (connection) => connection === void 0 ? invalidateResource(queryClient, target) : reset(connection),
|
|
313
|
+
onStatus: async (up, connection) => {
|
|
314
|
+
if (up && connection !== void 0) await reset(connection);
|
|
315
|
+
}
|
|
310
316
|
});
|
|
311
317
|
}
|
|
312
318
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/tanstack/adapter.ts","../../src/tanstack/index.ts"],"sourcesContent":["// Adapts TS1's `ResourceClient` (options-object methods) to the structural\n// slices the `./tanstack` factories take (`list(query, { signal })`, ...).\nimport type { CrudClient, ResourceClient } from \"../client.ts\";\nimport type { JsonObject, Manifest } from \"../manifest.ts\";\nimport type { CrudQuery } from \"./index.ts\";\n\ntype Init = { signal?: AbortSignal };\n\n/** What `tanstackApi` returns: every slice the factories use, bound to one resource. */\nexport interface TanstackApi<Read, Create, Update> {\n list(query: CrudQuery | undefined, init?: Init): Promise<Read[]>;\n get(id: number, init?: Init): Promise<Read>;\n stats(query: CrudQuery | undefined, init?: Init): Promise<JsonObject>;\n predict(query: CrudQuery | undefined, init?: Init): Promise<JsonObject>;\n revisions(id: number, init?: Init): Promise<JsonObject[]>;\n /** The manifest the client already loaded (the client's loader takes no signal). */\n manifest(init?: Init): Promise<Manifest>;\n create(body: Create): Promise<Read>;\n update(id: number, patch: Update, init?: { ifMatch?: string }): Promise<Read>;\n delete(id: number): Promise<void>;\n}\n\n/**\n * A query is the client's `ListOptions`, `StatsOptions` or `PredictOptions`\n * without `signal` (`{ filters, sort, skip, limit, ... }`); TanStack's signal\n * is added per call.\n */\nexport function tanstackApi<\n Read extends object,\n Create extends object,\n Update extends object,\n>(\n client: CrudClient,\n resource: ResourceClient<Read, Create, Update>,\n): TanstackApi<Read, Create, Update> {\n return {\n list: (query, init) => resource.list({ ...query, ...init }),\n get: (id, init) => resource.get(id, init),\n stats: (query, init) => resource.stats({ ...query, ...init }),\n predict: (query, init) => resource.predict({ ...query, ...init }),\n revisions: (id, init) => resource.revisions(id, init),\n manifest: () => client.manifest(),\n create: (body) => resource.create(body),\n update: (id, patch, init) => resource.update(id, patch, init),\n delete: (id) => resource.delete(id),\n };\n}\n","// Entry point `./tanstack`: `queryOptions` factories, key factory and the\n// invalidation bridge (TS2). Needs the optional peer `@tanstack/query-core`\n// (types only: nothing here imports it at runtime). query-core has no\n// `queryOptions`, so the factories return plain `{ queryKey, queryFn,\n// staleTime }` objects with `queryKey` tagged through `DataTag`; the React\n// wrappers live in lib-crud-views-ts.\nimport type { DataTag, InfiniteData, QueryClient } from \"@tanstack/query-core\";\nimport type { EventHub } from \"../events.ts\";\n\nexport { tanstackApi } from \"./adapter.ts\";\nexport type { TanstackApi } from \"./adapter.ts\";\nexport { tanstackCachePolicy } from \"../cache/policy.ts\";\nexport type { CachePolicy, ManifestCacheHints } from \"../cache/policy.ts\";\nexport { getEventHub, subscribeEvents } from \"../events.ts\";\nexport type {\n EventHandlers,\n EventHub,\n EventHubOptions,\n EventsConfig,\n SubscribeEventsOptions,\n} from \"../events.ts\";\nexport type { CrudEvent } from \"../interfaces.ts\";\n\n/** A query object as the client's query builder takes it. */\nexport type CrudQuery = Readonly<Record<string, unknown>>;\n\ntype Key = readonly unknown[];\n\n/**\n * Who a query belongs to: the client's stable identity and the caller scope\n * (`await client.namespace()`). Two clients, or two callers, that share one\n * `QueryClient` never see each other's cached data.\n */\nexport interface CrudNamespace {\n readonly client: string;\n readonly scope: string;\n}\n\n/** The kinds that sit at `key[4]`. */\nexport type CrudKind = \"list\" | \"record\" | \"stats\" | \"predict\" | \"revisions\";\n\n/** Which queries a {@link CrudKeys.filter} matches; every member narrows. */\nexport interface CrudKeyFilter {\n readonly client: string;\n /** Omit for every resource of the client (including manifests). */\n readonly resource?: string;\n /** Omit for every caller scope. */\n readonly scope?: string;\n /** Skips this caller scope (every other scope matches). */\n readonly exceptScope?: string;\n /** Omit for every kind. */\n readonly kinds?: readonly CrudKind[];\n /** A record id; matches only the `record` and `revisions` keys of that record. */\n readonly id?: number;\n}\n\n/** Spread into `invalidateQueries`, `removeQueries` or `cancelQueries`. */\nexport type CrudKeyMatcher =\n | { readonly queryKey: readonly unknown[] }\n | {\n readonly predicate: (query: {\n readonly queryKey: readonly unknown[];\n }) => boolean;\n };\n\n/** The key factory type. */\nexport interface CrudKeys {\n readonly all: readonly [\"crud\"];\n /** Everything of one client. */\n client(client: string): readonly [\"crud\", string];\n /** Everything of one resource on one client, every scope and kind. */\n resource(client: string, resource: string): readonly [\"crud\", string, string];\n /** All lists of one resource and scope, plain and infinite. */\n lists(\n ns: CrudNamespace,\n resource: string,\n ): readonly [\"crud\", string, string, string, \"list\"];\n list(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n infiniteList(\n ns: CrudNamespace,\n resource: string,\n query: CrudQuery,\n limit: number,\n ): Key;\n /** All record queries of one resource and scope. */\n records(\n ns: CrudNamespace,\n resource: string,\n ): readonly [\"crud\", string, string, string, \"record\"];\n record(\n ns: CrudNamespace,\n resource: string,\n id: number,\n ): readonly [\"crud\", string, string, string, \"record\", number];\n stats(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n predict(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n revisions(\n ns: CrudNamespace,\n resource: string,\n id: number,\n ): readonly [\"crud\", string, string, string, \"revisions\", number];\n /** The manifest of one caller scope. */\n manifest(ns: CrudNamespace): readonly [\"crud\", string, \"_manifest\", string];\n /**\n * A matcher for `invalidateQueries({ ...crudKeys.filter(f) })`. A client\n * or resource filter is one key prefix; narrowing by scope, kind or id\n * (which sit after the resource) uses a predicate.\n */\n filter(filter: CrudKeyFilter): CrudKeyMatcher;\n}\n\n/**\n * The key factory. Keys are\n * `[\"crud\", client, resource, scope, kind, ...]` (the manifest is\n * `[\"crud\", client, \"_manifest\", scope]`), so a prefix invalidates by client\n * or by resource on one client; `resource` is the manifest name, or\n * `name@apiVersion` for a pinned version (a different string is a different\n * resource: pass the same one to the invalidate helpers). `_manifest` is\n * reserved; a scope-only filter also matches the scope's manifest.\n */\nexport const crudKeys: CrudKeys = {\n all: [\"crud\"],\n client: (client) => [\"crud\", client],\n resource: (client, resource) => [\"crud\", client, resource],\n lists: (ns, resource) => [\"crud\", ns.client, resource, ns.scope, \"list\"],\n list: (ns, resource, query) =>\n query === undefined\n ? crudKeys.lists(ns, resource)\n : [...crudKeys.lists(ns, resource), query],\n infiniteList: (ns, resource, query, limit) => [\n ...crudKeys.lists(ns, resource),\n \"infinite\",\n query,\n limit,\n ],\n records: (ns, resource) => [\"crud\", ns.client, resource, ns.scope, \"record\"],\n record: (ns, resource, id) => [...crudKeys.records(ns, resource), id],\n stats: (ns, resource, query) =>\n query === undefined\n ? [\"crud\", ns.client, resource, ns.scope, \"stats\"]\n : [\"crud\", ns.client, resource, ns.scope, \"stats\", query],\n predict: (ns, resource, query) =>\n query === undefined\n ? [\"crud\", ns.client, resource, ns.scope, \"predict\"]\n : [\"crud\", ns.client, resource, ns.scope, \"predict\", query],\n revisions: (ns, resource, id) => [\n \"crud\",\n ns.client,\n resource,\n ns.scope,\n \"revisions\",\n id,\n ],\n manifest: (ns) => [\"crud\", ns.client, \"_manifest\", ns.scope],\n filter: (filter) => {\n const { client, resource, scope, exceptScope, kinds, id } = filter;\n if (\n scope === undefined &&\n exceptScope === undefined &&\n kinds === undefined &&\n id === undefined\n ) {\n return {\n queryKey:\n resource === undefined\n ? crudKeys.client(client)\n : crudKeys.resource(client, resource),\n };\n }\n return {\n predicate: ({ queryKey: key }) =>\n key[0] === \"crud\" &&\n key[1] === client &&\n (resource === undefined || key[2] === resource) &&\n (scope === undefined || key[3] === scope) &&\n (exceptScope === undefined || key[3] !== exceptScope) &&\n (kinds === undefined || kinds.includes(key[4] as CrudKind)) &&\n (id === undefined ||\n ((key[4] === \"record\" || key[4] === \"revisions\") && key[5] === id)),\n };\n },\n};\n\n/** What a query function receives from TanStack Query (a subset). */\nexport interface QueryContext {\n readonly signal: AbortSignal;\n}\n\nexport interface QueryOptionsInit {\n /** TanStack's `staleTime` in milliseconds. Default 0 (the SDK cache revalidates for free). */\n readonly staleTime?: number;\n}\n\n/** Plain query options with a typed key; spread into `useQuery`/`fetchQuery`. */\nexport interface CrudQueryOptions<T> {\n readonly queryKey: DataTag<Key, T, Error>;\n readonly queryFn: (context: QueryContext) => Promise<T>;\n readonly staleTime: number;\n}\n\nexport interface CrudInfiniteQueryOptions<T> {\n readonly queryKey: DataTag<\n Key,\n { pages: T[][]; pageParams: number[] },\n Error\n >;\n readonly queryFn: (\n context: QueryContext & { pageParam: number },\n ) => Promise<T[]>;\n readonly initialPageParam: number;\n /** `skip + limit` unless the page was short. */\n readonly getNextPageParam: (\n lastPage: T[],\n allPages: T[][],\n lastPageParam: number,\n ) => number | undefined;\n readonly staleTime: number;\n}\n\n/** The slices of a resource API the factories call (structural: TS1's resource client fits). */\nexport interface ListApi<T> {\n list(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<T[]>;\n}\nexport interface RecordApi<T> {\n get(id: number, init?: { signal?: AbortSignal }): Promise<T>;\n}\nexport interface StatsApi<S> {\n stats(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<S>;\n}\nexport interface PredictApi<P> {\n predict(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<P>;\n}\nexport interface RevisionsApi<R> {\n revisions(id: number, init?: { signal?: AbortSignal }): Promise<R[]>;\n}\nexport interface ManifestApi<M> {\n manifest(init?: { signal?: AbortSignal }): Promise<M>;\n}\nexport interface CreateApi<T, C> {\n create(body: C): Promise<T>;\n}\nexport interface UpdateApi<T, P> {\n update(id: number, patch: P, init?: { ifMatch?: string }): Promise<T>;\n}\nexport interface DeleteApi {\n delete(id: number): Promise<unknown>;\n}\n\nfunction tag<T>(key: Key): DataTag<Key, T, Error> {\n return key as DataTag<Key, T, Error>;\n}\n\nexport function listOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: ListApi<T>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<T[]> {\n return {\n queryKey: tag(crudKeys.list(ns, resource, query)),\n queryFn: ({ signal }) => api.list(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function recordOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: RecordApi<T>,\n id: number,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<T> {\n return {\n queryKey: tag(crudKeys.record(ns, resource, id)),\n queryFn: ({ signal }) => api.get(id, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function statsOptions<S>(\n ns: CrudNamespace,\n resource: string,\n api: StatsApi<S>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<S> {\n return {\n queryKey: tag(crudKeys.stats(ns, resource, query)),\n queryFn: ({ signal }) => api.stats(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function predictOptions<P>(\n ns: CrudNamespace,\n resource: string,\n api: PredictApi<P>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<P> {\n return {\n queryKey: tag(crudKeys.predict(ns, resource, query)),\n queryFn: ({ signal }) => api.predict(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function revisionsOptions<R>(\n ns: CrudNamespace,\n resource: string,\n api: RevisionsApi<R>,\n id: number,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<R[]> {\n return {\n queryKey: tag(crudKeys.revisions(ns, resource, id)),\n queryFn: ({ signal }) => api.revisions(id, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function manifestOptions<M>(\n ns: CrudNamespace,\n api: ManifestApi<M>,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<M> {\n return {\n queryKey: tag(crudKeys.manifest(ns)),\n queryFn: ({ signal }) => api.manifest({ signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport interface InfiniteListInit extends QueryOptionsInit {\n /** Page size; default 100 (the server's default). */\n readonly limit?: number;\n /**\n * Keys of the paging parameters in the query object handed to `api.list`.\n * Default `skip`/`limit`, which is what `tanstackApi` (the client's\n * `ListOptions`) reads; change them only for a hand-written `ListApi`. The\n * client maps them to the manifest's `skip_param`/`limit_param` itself.\n */\n readonly skipKey?: string;\n readonly limitKey?: string;\n}\n\n/** Offset paging: the next page starts at `skip + limit`, unless the last page was short. */\nexport function infiniteListOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: ListApi<T>,\n query: CrudQuery = {},\n init: InfiniteListInit = {},\n): CrudInfiniteQueryOptions<T> {\n const limit = init.limit ?? 100;\n const skipKey = init.skipKey ?? \"skip\";\n const limitKey = init.limitKey ?? \"limit\";\n return {\n queryKey: tag<InfiniteData<T[], number>>(\n crudKeys.infiniteList(ns, resource, query, limit),\n ),\n queryFn: ({ signal, pageParam }) =>\n api.list(\n { ...query, [skipKey]: pageParam, [limitKey]: limit },\n { signal },\n ),\n initialPageParam: 0,\n getNextPageParam: (lastPage, _all, lastPageParam) =>\n lastPage.length < limit ? undefined : lastPageParam + limit,\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport type Invalidator = Pick<\n QueryClient,\n \"invalidateQueries\" | \"removeQueries\" | \"setQueryData\"\n>;\n\nconst COLLECTION_KINDS = [\"list\", \"stats\", \"predict\"] as const;\n\n/** One resource of one client; `resource` is the string used in its keys. */\nexport interface CrudTarget {\n readonly client: string;\n readonly resource: string;\n}\n\n/**\n * Invalidates the list, stats and predict queries of `resource` on `client`,\n * for every caller scope (a write changes what every caller sees).\n */\nexport function invalidateCollections(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n): Promise<void> {\n return queryClient.invalidateQueries(\n crudKeys.filter({ client, resource, kinds: COLLECTION_KINDS }),\n );\n}\n\n/** Invalidates everything cached for `resource` on `client`: one key prefix. */\nexport function invalidateResource(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n): Promise<void> {\n return queryClient.invalidateQueries(crudKeys.filter({ client, resource }));\n}\n\n/** Invalidates one record's `record` and `revisions` queries, for every scope. */\nexport function invalidateRecord(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n id: number,\n): Promise<void> {\n return queryClient.invalidateQueries(\n crudKeys.filter({\n client,\n resource,\n kinds: [\"record\", \"revisions\"],\n id,\n }),\n );\n}\n\nexport interface CreateMutationOptions<T, C> {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"create\"];\n readonly mutationFn: (body: C) => Promise<T>;\n readonly onSuccess: () => Promise<void>;\n}\n\nexport interface UpdateMutationOptions<T, P> {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"update\"];\n readonly mutationFn: (variables: UpdateVariables<P>) => Promise<T>;\n readonly onSuccess: (\n record: T,\n variables: UpdateVariables<P>,\n ) => Promise<void>;\n}\n\nexport interface DeleteMutationOptions {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"delete\"];\n readonly mutationFn: (id: number) => Promise<unknown>;\n readonly onSuccess: (result: unknown, id: number) => Promise<void>;\n}\n\n/** Create: invalidate lists, stats and predict. */\nexport function createMutationOptions<T, C>(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: CreateApi<T, C>,\n): CreateMutationOptions<T, C> {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"create\"],\n mutationFn: (body: C) => api.create(body),\n onSuccess: () =>\n invalidateCollections(queryClient, { client: ns.client, resource }),\n };\n}\n\nexport interface UpdateVariables<P> {\n readonly id: number;\n readonly patch: P;\n /** Sent as `If-Match`; a stale one fails with 412. */\n readonly ifMatch?: string;\n}\n\n/** Update: `setQueryData` from the PATCH response, invalidate lists, stats, predict and revisions. */\nexport function updateMutationOptions<T, P>(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: UpdateApi<T, P>,\n): UpdateMutationOptions<T, P> {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"update\"],\n mutationFn: ({ id, patch, ifMatch }: UpdateVariables<P>) =>\n api.update(id, patch, ifMatch === undefined ? {} : { ifMatch }),\n onSuccess: async (record: T, { id }: UpdateVariables<P>) => {\n queryClient.setQueryData(crudKeys.record(ns, resource, id), record);\n await Promise.all([\n invalidateCollections(queryClient, { client: ns.client, resource }),\n // The caller's own record was just set; other scopes' are stale.\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n exceptScope: ns.scope,\n kinds: [\"record\"],\n id,\n }),\n ),\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"revisions\"],\n id,\n }),\n ),\n ]);\n },\n };\n}\n\n/** Delete: drop the record query, invalidate lists, stats, predict and revisions. */\nexport function deleteMutationOptions(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: DeleteApi,\n): DeleteMutationOptions {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"delete\"],\n mutationFn: (id: number) => api.delete(id),\n onSuccess: async (_result: unknown, id: number) => {\n queryClient.removeQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"record\"],\n id,\n }),\n );\n await Promise.all([\n invalidateCollections(queryClient, { client: ns.client, resource }),\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"revisions\"],\n id,\n }),\n ),\n ]);\n },\n };\n}\n\n/**\n * Invalidate TanStack queries from the resource's event stream. Events\n * invalidate and never apply snapshots: `create` invalidates lists, stats and\n * predict; `update`/`delete`/`restore` also the record and its revisions; a\n * reconnect (or an event with no usable record id) invalidates the resource.\n * Returns the unsubscribe.\n */\nexport function bridgeEvents(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n hub: EventHub,\n target: CrudTarget,\n): () => void {\n return hub.subscribe({\n onEvent: async (event) => {\n const pending = [invalidateCollections(queryClient, target)];\n if (event.action !== \"create\") {\n pending.push(invalidateRecord(queryClient, target, event.recordId));\n }\n await Promise.all(pending);\n },\n onReset: () => invalidateResource(queryClient, target),\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/tanstack/adapter.ts","../../src/tanstack/index.ts"],"sourcesContent":["// Adapts TS1's `ResourceClient` (options-object methods) to the structural\n// slices the `./tanstack` factories take (`list(query, { signal })`, ...).\nimport type { CrudClient, ResourceClient } from \"../client.ts\";\nimport type { JsonObject, Manifest } from \"../manifest.ts\";\nimport type { CrudQuery } from \"./index.ts\";\n\ntype Init = { signal?: AbortSignal };\n\n/** What `tanstackApi` returns: every slice the factories use, bound to one resource. */\nexport interface TanstackApi<Read, Create, Update> {\n list(query: CrudQuery | undefined, init?: Init): Promise<Read[]>;\n get(id: number, init?: Init): Promise<Read>;\n stats(query: CrudQuery | undefined, init?: Init): Promise<JsonObject>;\n predict(query: CrudQuery | undefined, init?: Init): Promise<JsonObject>;\n revisions(id: number, init?: Init): Promise<JsonObject[]>;\n /** The manifest the client already loaded (the client's loader takes no signal). */\n manifest(init?: Init): Promise<Manifest>;\n create(body: Create): Promise<Read>;\n update(id: number, patch: Update, init?: { ifMatch?: string }): Promise<Read>;\n delete(id: number): Promise<void>;\n}\n\n/**\n * A query is the client's `ListOptions`, `StatsOptions` or `PredictOptions`\n * without `signal` (`{ filters, sort, skip, limit, ... }`); TanStack's signal\n * is added per call.\n */\nexport function tanstackApi<\n Read extends object,\n Create extends object,\n Update extends object,\n>(\n client: CrudClient,\n resource: ResourceClient<Read, Create, Update>,\n): TanstackApi<Read, Create, Update> {\n return {\n list: (query, init) => resource.list({ ...query, ...init }),\n get: (id, init) => resource.get(id, init),\n stats: (query, init) => resource.stats({ ...query, ...init }),\n predict: (query, init) => resource.predict({ ...query, ...init }),\n revisions: (id, init) => resource.revisions(id, init),\n manifest: () => client.manifest(),\n create: (body) => resource.create(body),\n update: (id, patch, init) => resource.update(id, patch, init),\n delete: (id) => resource.delete(id),\n };\n}\n","// Entry point `./tanstack`: `queryOptions` factories, key factory and the\n// invalidation bridge (TS2). Needs the optional peer `@tanstack/query-core`\n// (types only: nothing here imports it at runtime). query-core has no\n// `queryOptions`, so the factories return plain `{ queryKey, queryFn,\n// staleTime }` objects with `queryKey` tagged through `DataTag`; the React\n// wrappers live in lib-crud-views-ts.\nimport type { DataTag, InfiniteData, QueryClient } from \"@tanstack/query-core\";\nimport type { EventConnection, EventHub } from \"../events.ts\";\nimport { resetOnce } from \"../reset-once.ts\";\n\nexport { tanstackApi } from \"./adapter.ts\";\nexport type { TanstackApi } from \"./adapter.ts\";\nexport { tanstackCachePolicy } from \"../cache/policy.ts\";\nexport type { CachePolicy, ManifestCacheHints } from \"../cache/policy.ts\";\nexport { getEventHub, subscribeEvents } from \"../events.ts\";\nexport type {\n EventConnection,\n EventHandlers,\n EventHub,\n EventHubOptions,\n EventsConfig,\n SubscribeEventsOptions,\n} from \"../events.ts\";\nexport type { CrudEvent } from \"../interfaces.ts\";\n\n/** A query object as the client's query builder takes it. */\nexport type CrudQuery = Readonly<Record<string, unknown>>;\n\ntype Key = readonly unknown[];\n\n/**\n * Who a query belongs to: the client's stable identity and the caller scope\n * (`await client.namespace()`). Two clients, or two callers, that share one\n * `QueryClient` never see each other's cached data.\n */\nexport interface CrudNamespace {\n readonly client: string;\n readonly scope: string;\n}\n\n/** The kinds that sit at `key[4]`. */\nexport type CrudKind = \"list\" | \"record\" | \"stats\" | \"predict\" | \"revisions\";\n\n/** Which queries a {@link CrudKeys.filter} matches; every member narrows. */\nexport interface CrudKeyFilter {\n readonly client: string;\n /** Omit for every resource of the client (including manifests). */\n readonly resource?: string;\n /** Omit for every caller scope. */\n readonly scope?: string;\n /** Skips this caller scope (every other scope matches). */\n readonly exceptScope?: string;\n /** Omit for every kind. */\n readonly kinds?: readonly CrudKind[];\n /** A record id; matches only the `record` and `revisions` keys of that record. */\n readonly id?: number;\n}\n\n/** Spread into `invalidateQueries`, `removeQueries` or `cancelQueries`. */\nexport type CrudKeyMatcher =\n | { readonly queryKey: readonly unknown[] }\n | {\n readonly predicate: (query: {\n readonly queryKey: readonly unknown[];\n }) => boolean;\n };\n\n/** The key factory type. */\nexport interface CrudKeys {\n readonly all: readonly [\"crud\"];\n /** Everything of one client. */\n client(client: string): readonly [\"crud\", string];\n /** Everything of one resource on one client, every scope and kind. */\n resource(client: string, resource: string): readonly [\"crud\", string, string];\n /** All lists of one resource and scope, plain and infinite. */\n lists(\n ns: CrudNamespace,\n resource: string,\n ): readonly [\"crud\", string, string, string, \"list\"];\n list(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n infiniteList(\n ns: CrudNamespace,\n resource: string,\n query: CrudQuery,\n limit: number,\n ): Key;\n /** All record queries of one resource and scope. */\n records(\n ns: CrudNamespace,\n resource: string,\n ): readonly [\"crud\", string, string, string, \"record\"];\n record(\n ns: CrudNamespace,\n resource: string,\n id: number,\n ): readonly [\"crud\", string, string, string, \"record\", number];\n stats(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n predict(ns: CrudNamespace, resource: string, query?: CrudQuery): Key;\n revisions(\n ns: CrudNamespace,\n resource: string,\n id: number,\n ): readonly [\"crud\", string, string, string, \"revisions\", number];\n /** The manifest of one caller scope. */\n manifest(ns: CrudNamespace): readonly [\"crud\", string, \"_manifest\", string];\n /**\n * A matcher for `invalidateQueries({ ...crudKeys.filter(f) })`. A client\n * or resource filter is one key prefix; narrowing by scope, kind or id\n * (which sit after the resource) uses a predicate.\n */\n filter(filter: CrudKeyFilter): CrudKeyMatcher;\n}\n\n/**\n * The key factory. Keys are\n * `[\"crud\", client, resource, scope, kind, ...]` (the manifest is\n * `[\"crud\", client, \"_manifest\", scope]`), so a prefix invalidates by client\n * or by resource on one client; `resource` is the manifest name, or\n * `name@apiVersion` for a pinned version (a different string is a different\n * resource: pass the same one to the invalidate helpers). `_manifest` is\n * reserved; a scope-only filter also matches the scope's manifest.\n */\nexport const crudKeys: CrudKeys = {\n all: [\"crud\"],\n client: (client) => [\"crud\", client],\n resource: (client, resource) => [\"crud\", client, resource],\n lists: (ns, resource) => [\"crud\", ns.client, resource, ns.scope, \"list\"],\n list: (ns, resource, query) =>\n query === undefined\n ? crudKeys.lists(ns, resource)\n : [...crudKeys.lists(ns, resource), query],\n infiniteList: (ns, resource, query, limit) => [\n ...crudKeys.lists(ns, resource),\n \"infinite\",\n query,\n limit,\n ],\n records: (ns, resource) => [\"crud\", ns.client, resource, ns.scope, \"record\"],\n record: (ns, resource, id) => [...crudKeys.records(ns, resource), id],\n stats: (ns, resource, query) =>\n query === undefined\n ? [\"crud\", ns.client, resource, ns.scope, \"stats\"]\n : [\"crud\", ns.client, resource, ns.scope, \"stats\", query],\n predict: (ns, resource, query) =>\n query === undefined\n ? [\"crud\", ns.client, resource, ns.scope, \"predict\"]\n : [\"crud\", ns.client, resource, ns.scope, \"predict\", query],\n revisions: (ns, resource, id) => [\n \"crud\",\n ns.client,\n resource,\n ns.scope,\n \"revisions\",\n id,\n ],\n manifest: (ns) => [\"crud\", ns.client, \"_manifest\", ns.scope],\n filter: (filter) => {\n const { client, resource, scope, exceptScope, kinds, id } = filter;\n if (\n scope === undefined &&\n exceptScope === undefined &&\n kinds === undefined &&\n id === undefined\n ) {\n return {\n queryKey:\n resource === undefined\n ? crudKeys.client(client)\n : crudKeys.resource(client, resource),\n };\n }\n return {\n predicate: ({ queryKey: key }) =>\n key[0] === \"crud\" &&\n key[1] === client &&\n (resource === undefined || key[2] === resource) &&\n (scope === undefined || key[3] === scope) &&\n (exceptScope === undefined || key[3] !== exceptScope) &&\n (kinds === undefined || kinds.includes(key[4] as CrudKind)) &&\n (id === undefined ||\n ((key[4] === \"record\" || key[4] === \"revisions\") && key[5] === id)),\n };\n },\n};\n\n/** What a query function receives from TanStack Query (a subset). */\nexport interface QueryContext {\n readonly signal: AbortSignal;\n}\n\nexport interface QueryOptionsInit {\n /** TanStack's `staleTime` in milliseconds. Default 0 (the SDK cache revalidates for free). */\n readonly staleTime?: number;\n}\n\n/** Plain query options with a typed key; spread into `useQuery`/`fetchQuery`. */\nexport interface CrudQueryOptions<T> {\n readonly queryKey: DataTag<Key, T, Error>;\n readonly queryFn: (context: QueryContext) => Promise<T>;\n readonly staleTime: number;\n}\n\nexport interface CrudInfiniteQueryOptions<T> {\n readonly queryKey: DataTag<\n Key,\n { pages: T[][]; pageParams: number[] },\n Error\n >;\n readonly queryFn: (\n context: QueryContext & { pageParam: number },\n ) => Promise<T[]>;\n readonly initialPageParam: number;\n /** `skip + limit` unless the page was short. */\n readonly getNextPageParam: (\n lastPage: T[],\n allPages: T[][],\n lastPageParam: number,\n ) => number | undefined;\n readonly staleTime: number;\n}\n\n/** The slices of a resource API the factories call (structural: TS1's resource client fits). */\nexport interface ListApi<T> {\n list(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<T[]>;\n}\nexport interface RecordApi<T> {\n get(id: number, init?: { signal?: AbortSignal }): Promise<T>;\n}\nexport interface StatsApi<S> {\n stats(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<S>;\n}\nexport interface PredictApi<P> {\n predict(\n query: CrudQuery | undefined,\n init?: { signal?: AbortSignal },\n ): Promise<P>;\n}\nexport interface RevisionsApi<R> {\n revisions(id: number, init?: { signal?: AbortSignal }): Promise<R[]>;\n}\nexport interface ManifestApi<M> {\n manifest(init?: { signal?: AbortSignal }): Promise<M>;\n}\nexport interface CreateApi<T, C> {\n create(body: C): Promise<T>;\n}\nexport interface UpdateApi<T, P> {\n update(id: number, patch: P, init?: { ifMatch?: string }): Promise<T>;\n}\nexport interface DeleteApi {\n delete(id: number): Promise<unknown>;\n}\n\nfunction tag<T>(key: Key): DataTag<Key, T, Error> {\n return key as DataTag<Key, T, Error>;\n}\n\nexport function listOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: ListApi<T>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<T[]> {\n return {\n queryKey: tag(crudKeys.list(ns, resource, query)),\n queryFn: ({ signal }) => api.list(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function recordOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: RecordApi<T>,\n id: number,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<T> {\n return {\n queryKey: tag(crudKeys.record(ns, resource, id)),\n queryFn: ({ signal }) => api.get(id, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function statsOptions<S>(\n ns: CrudNamespace,\n resource: string,\n api: StatsApi<S>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<S> {\n return {\n queryKey: tag(crudKeys.stats(ns, resource, query)),\n queryFn: ({ signal }) => api.stats(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function predictOptions<P>(\n ns: CrudNamespace,\n resource: string,\n api: PredictApi<P>,\n query?: CrudQuery,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<P> {\n return {\n queryKey: tag(crudKeys.predict(ns, resource, query)),\n queryFn: ({ signal }) => api.predict(query, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function revisionsOptions<R>(\n ns: CrudNamespace,\n resource: string,\n api: RevisionsApi<R>,\n id: number,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<R[]> {\n return {\n queryKey: tag(crudKeys.revisions(ns, resource, id)),\n queryFn: ({ signal }) => api.revisions(id, { signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport function manifestOptions<M>(\n ns: CrudNamespace,\n api: ManifestApi<M>,\n init: QueryOptionsInit = {},\n): CrudQueryOptions<M> {\n return {\n queryKey: tag(crudKeys.manifest(ns)),\n queryFn: ({ signal }) => api.manifest({ signal }),\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport interface InfiniteListInit extends QueryOptionsInit {\n /** Page size; default 100 (the server's default). */\n readonly limit?: number;\n /**\n * Keys of the paging parameters in the query object handed to `api.list`.\n * Default `skip`/`limit`, which is what `tanstackApi` (the client's\n * `ListOptions`) reads; change them only for a hand-written `ListApi`. The\n * client maps them to the manifest's `skip_param`/`limit_param` itself.\n */\n readonly skipKey?: string;\n readonly limitKey?: string;\n}\n\n/** Offset paging: the next page starts at `skip + limit`, unless the last page was short. */\nexport function infiniteListOptions<T>(\n ns: CrudNamespace,\n resource: string,\n api: ListApi<T>,\n query: CrudQuery = {},\n init: InfiniteListInit = {},\n): CrudInfiniteQueryOptions<T> {\n const limit = init.limit ?? 100;\n const skipKey = init.skipKey ?? \"skip\";\n const limitKey = init.limitKey ?? \"limit\";\n return {\n queryKey: tag<InfiniteData<T[], number>>(\n crudKeys.infiniteList(ns, resource, query, limit),\n ),\n queryFn: ({ signal, pageParam }) =>\n api.list(\n { ...query, [skipKey]: pageParam, [limitKey]: limit },\n { signal },\n ),\n initialPageParam: 0,\n getNextPageParam: (lastPage, _all, lastPageParam) =>\n lastPage.length < limit ? undefined : lastPageParam + limit,\n staleTime: init.staleTime ?? 0,\n };\n}\n\nexport type Invalidator = Pick<\n QueryClient,\n \"invalidateQueries\" | \"removeQueries\" | \"setQueryData\"\n>;\n\nconst COLLECTION_KINDS = [\"list\", \"stats\", \"predict\"] as const;\n\n/** One resource of one client; `resource` is the string used in its keys. */\nexport interface CrudTarget {\n readonly client: string;\n readonly resource: string;\n}\n\n/**\n * Invalidates the list, stats and predict queries of `resource` on `client`,\n * for every caller scope (a write changes what every caller sees).\n */\nexport function invalidateCollections(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n): Promise<void> {\n return queryClient.invalidateQueries(\n crudKeys.filter({ client, resource, kinds: COLLECTION_KINDS }),\n );\n}\n\n/** Invalidates everything cached for `resource` on `client`: one key prefix. */\nexport function invalidateResource(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n): Promise<void> {\n return queryClient.invalidateQueries(crudKeys.filter({ client, resource }));\n}\n\n/** Invalidates one record's `record` and `revisions` queries, for every scope. */\nexport function invalidateRecord(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n { client, resource }: CrudTarget,\n id: number,\n): Promise<void> {\n return queryClient.invalidateQueries(\n crudKeys.filter({\n client,\n resource,\n kinds: [\"record\", \"revisions\"],\n id,\n }),\n );\n}\n\nexport interface CreateMutationOptions<T, C> {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"create\"];\n readonly mutationFn: (body: C) => Promise<T>;\n readonly onSuccess: () => Promise<void>;\n}\n\nexport interface UpdateMutationOptions<T, P> {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"update\"];\n readonly mutationFn: (variables: UpdateVariables<P>) => Promise<T>;\n readonly onSuccess: (\n record: T,\n variables: UpdateVariables<P>,\n ) => Promise<void>;\n}\n\nexport interface DeleteMutationOptions {\n readonly mutationKey: readonly [\"crud\", string, string, string, \"delete\"];\n readonly mutationFn: (id: number) => Promise<unknown>;\n readonly onSuccess: (result: unknown, id: number) => Promise<void>;\n}\n\n/** Create: invalidate lists, stats and predict. */\nexport function createMutationOptions<T, C>(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: CreateApi<T, C>,\n): CreateMutationOptions<T, C> {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"create\"],\n mutationFn: (body: C) => api.create(body),\n onSuccess: () =>\n invalidateCollections(queryClient, { client: ns.client, resource }),\n };\n}\n\nexport interface UpdateVariables<P> {\n readonly id: number;\n readonly patch: P;\n /** Sent as `If-Match`; a stale one fails with 412. */\n readonly ifMatch?: string;\n}\n\n/** Update: `setQueryData` from the PATCH response, invalidate lists, stats, predict and revisions. */\nexport function updateMutationOptions<T, P>(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: UpdateApi<T, P>,\n): UpdateMutationOptions<T, P> {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"update\"],\n mutationFn: ({ id, patch, ifMatch }: UpdateVariables<P>) =>\n api.update(id, patch, ifMatch === undefined ? {} : { ifMatch }),\n onSuccess: async (record: T, { id }: UpdateVariables<P>) => {\n queryClient.setQueryData(crudKeys.record(ns, resource, id), record);\n await Promise.all([\n invalidateCollections(queryClient, { client: ns.client, resource }),\n // The caller's own record was just set; other scopes' are stale.\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n exceptScope: ns.scope,\n kinds: [\"record\"],\n id,\n }),\n ),\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"revisions\"],\n id,\n }),\n ),\n ]);\n },\n };\n}\n\n/** Delete: drop the record query, invalidate lists, stats, predict and revisions. */\nexport function deleteMutationOptions(\n queryClient: Invalidator,\n ns: CrudNamespace,\n resource: string,\n api: DeleteApi,\n): DeleteMutationOptions {\n return {\n mutationKey: [\"crud\", ns.client, resource, ns.scope, \"delete\"],\n mutationFn: (id: number) => api.delete(id),\n onSuccess: async (_result: unknown, id: number) => {\n queryClient.removeQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"record\"],\n id,\n }),\n );\n await Promise.all([\n invalidateCollections(queryClient, { client: ns.client, resource }),\n queryClient.invalidateQueries(\n crudKeys.filter({\n client: ns.client,\n resource,\n kinds: [\"revisions\"],\n id,\n }),\n ),\n ]);\n },\n };\n}\n\n/**\n * Invalidate TanStack queries from the resource's event stream. Events\n * invalidate and never apply snapshots: `create` invalidates lists, stats and\n * predict; `update`/`delete`/`restore` also the record and its revisions; a\n * reconnect (or an event with no usable record id) invalidates the resource.\n * A bridge that joins an already connected hub (or during the handshake)\n * invalidates the resource once on subscribing, unless this `queryClient` was\n * already reset for the resource on that connection. Returns the unsubscribe.\n */\nexport function bridgeEvents(\n queryClient: Pick<QueryClient, \"invalidateQueries\">,\n hub: EventHub,\n target: CrudTarget,\n): () => void {\n const reset = (connection: EventConnection): Promise<void> =>\n resetOnce(\n connection,\n queryClient,\n JSON.stringify([target.client, target.resource]),\n () => invalidateResource(queryClient, target),\n );\n return hub.subscribe({\n onEvent: async (event) => {\n const pending = [invalidateCollections(queryClient, target)];\n if (event.action !== \"create\") {\n pending.push(invalidateRecord(queryClient, target, event.recordId));\n }\n await Promise.all(pending);\n },\n onReset: (connection) =>\n connection === undefined\n ? invalidateResource(queryClient, target)\n : reset(connection),\n onStatus: async (up, connection) => {\n if (up && connection !== undefined) {\n await reset(connection); // a late joiner is reset like every connect\n }\n },\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@craftainer/crud-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Typed fetch client for craftainer CRUD APIs: manifest-driven resources, query builder, problem-details errors, opt-in cache with SSE invalidation and TanStack Query helpers.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|