stitchkit 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/browser/client.d.ts.map +1 -1
  2. package/dist/browser/socket-io.d.ts +11 -2
  3. package/dist/browser/socket-io.d.ts.map +1 -1
  4. package/dist/cli.js +3 -7
  5. package/dist/contract/define.d.ts +44 -2
  6. package/dist/contract/define.d.ts.map +1 -1
  7. package/dist/contract/index.d.ts +1 -1
  8. package/dist/contract/index.d.ts.map +1 -1
  9. package/dist/contract/index.js +0 -1
  10. package/dist/{index-za2p453b.js → index-031q8xmx.js} +1 -1
  11. package/dist/{index-tr7r1530.js → index-13psnhhe.js} +5 -9
  12. package/dist/{index-5qe31283.js → index-9zrq8x5z.js} +9 -6
  13. package/dist/index-jgpsd7dy.js +105 -0
  14. package/dist/{index-mwmpw6j1.js → index-p9m9c0jw.js} +2 -4
  15. package/dist/index-tm7dqzxc.js +20 -0
  16. package/dist/{index-5789sbt8.js → index-zshrc6kx.js} +9 -16
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +480 -13
  20. package/dist/node.js +4 -8
  21. package/dist/observability/index.js +4 -7
  22. package/dist/react.js +0 -2
  23. package/dist/retained.d.ts +30 -0
  24. package/dist/retained.d.ts.map +1 -0
  25. package/dist/server/implement.d.ts.map +1 -1
  26. package/dist/server/index.js +89 -34
  27. package/dist/server/types.d.ts +7 -0
  28. package/dist/server/types.d.ts.map +1 -1
  29. package/dist/tools/dispatch.d.ts +65 -0
  30. package/dist/tools/dispatch.d.ts.map +1 -0
  31. package/dist/tools/mcp-app.d.ts.map +1 -1
  32. package/dist/tools/remote.d.ts.map +1 -1
  33. package/dist/tools.d.ts +1 -0
  34. package/dist/tools.d.ts.map +1 -1
  35. package/dist/tools.js +290 -21
  36. package/llms-full.txt +111 -3
  37. package/package.json +5 -3
  38. package/dist/index-37x76zdn.js +0 -4
  39. package/dist/index-48ffdxgk.js +0 -6
  40. package/dist/index-809wc1tt.js +0 -18
  41. package/dist/index-a1702zj4.js +0 -72
  42. package/dist/index-kdkvp26v.js +0 -380
  43. package/dist/index-kzfs85xp.js +0 -9
  44. package/dist/index-wjwj5bz2.js +0 -37
  45. package/dist/index-x3fcszf8.js +0 -8
package/dist/index.js CHANGED
@@ -1,14 +1,3 @@
1
- import {
2
- ApiError,
3
- createClient,
4
- createClients,
5
- createHttpClient
6
- } from "./index-kdkvp26v.js";
7
- import {
8
- parseSSE
9
- } from "./index-a1702zj4.js";
10
- import"./index-48ffdxgk.js";
11
- import"./index-wjwj5bz2.js";
12
1
  import {
13
2
  ALL_TRANSPORTS,
14
3
  AppError,
@@ -26,10 +15,438 @@ import {
26
15
  rateLimited,
27
16
  unauthorized
28
17
  } from "./index-eq29zkrx.js";
29
- import"./index-809wc1tt.js";
30
- import"./index-37x76zdn.js";
18
+
19
+ // src/internal/http-input.ts
20
+ function inputIsQuery(method) {
21
+ return method === "GET" || method === "DELETE";
22
+ }
23
+
24
+ // src/internal/typed.ts
25
+ function typedEntries(value) {
26
+ return Object.entries(value);
27
+ }
28
+ function isRecord(value) {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+ function mapObject(source, mapper) {
32
+ const result = {};
33
+ for (const [key, value] of typedEntries(source)) {
34
+ const mapped = mapper(key, value);
35
+ if (mapped !== undefined)
36
+ result[key] = mapped;
37
+ }
38
+ return result;
39
+ }
40
+
41
+ // src/browser/http.ts
42
+ import ky, { isHTTPError } from "ky";
43
+ class ApiError extends Error {
44
+ code;
45
+ status;
46
+ details;
47
+ hint;
48
+ constructor(code, status = 0, details, message, hint) {
49
+ super(message ?? `API Error: ${code}`);
50
+ this.code = code;
51
+ this.status = status;
52
+ this.details = details;
53
+ this.hint = hint;
54
+ this.name = "ApiError";
55
+ }
56
+ static is(error) {
57
+ return error instanceof ApiError;
58
+ }
59
+ }
60
+ function parseApiErrorBody(body) {
61
+ if (!isRecord(body) || !isRecord(body.error))
62
+ return null;
63
+ const error = body.error;
64
+ if (typeof error.code !== "string")
65
+ return null;
66
+ return {
67
+ code: error.code,
68
+ message: typeof error.message === "string" ? error.message : undefined,
69
+ details: error.details,
70
+ hint: typeof error.hint === "string" ? error.hint : undefined
71
+ };
72
+ }
73
+ function createHttpClient(config) {
74
+ let ssrCookies = null;
75
+ let isLoggedOut = false;
76
+ const listeners = new Set;
77
+ const authEndpoints = config.authEndpoints ?? ["/auth/"];
78
+ const parseError = config.parseError ?? parseApiErrorBody;
79
+ function emit(event) {
80
+ for (const fn of listeners) {
81
+ try {
82
+ fn(event);
83
+ } catch {}
84
+ }
85
+ }
86
+ const client = ky.create({
87
+ prefix: config.baseUrl,
88
+ credentials: config.credentials ?? "include",
89
+ timeout: config.timeout ?? 30000,
90
+ retry: {
91
+ limit: config.retry?.limit ?? 2,
92
+ methods: config.retry?.methods ?? ["get"],
93
+ statusCodes: config.retry?.statusCodes ?? []
94
+ },
95
+ hooks: {
96
+ beforeRequest: [
97
+ ({ request: request2 }) => {
98
+ if (ssrCookies) {
99
+ request2.headers.set("Cookie", ssrCookies);
100
+ }
101
+ const extra = typeof config.headers === "function" ? config.headers() : config.headers;
102
+ if (extra) {
103
+ for (const [key, value] of Object.entries(extra)) {
104
+ request2.headers.set(key, value);
105
+ }
106
+ }
107
+ }
108
+ ],
109
+ afterResponse: [
110
+ async ({ request: request2, response }) => {
111
+ if (response.status === 401) {
112
+ const url = new URL(request2.url).pathname;
113
+ if (!isLoggedOut && !authEndpoints.some((path) => url.startsWith(path))) {
114
+ isLoggedOut = true;
115
+ emit({ type: "unauthorized" });
116
+ }
117
+ }
118
+ if (!response.ok) {
119
+ const body = await response.clone().json().catch(() => null);
120
+ if (body) {
121
+ const parsed = parseError(body);
122
+ if (parsed) {
123
+ throw new ApiError(parsed.code, response.status, parsed.details, parsed.message, parsed.hint);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ ]
129
+ }
130
+ });
131
+ async function request(method, url, data, options = {}) {
132
+ const kyOptions = {
133
+ timeout: options.timeout
134
+ };
135
+ if (options.params) {
136
+ const searchParams = new URLSearchParams;
137
+ for (const [key, value] of Object.entries(options.params)) {
138
+ if (value === undefined)
139
+ continue;
140
+ if (Array.isArray(value)) {
141
+ for (const item of value)
142
+ searchParams.append(key, String(item));
143
+ } else {
144
+ searchParams.set(key, String(value));
145
+ }
146
+ }
147
+ if (searchParams.size > 0) {
148
+ kyOptions.searchParams = searchParams;
149
+ }
150
+ }
151
+ if (data instanceof FormData) {
152
+ kyOptions.body = data;
153
+ } else if (data !== undefined) {
154
+ kyOptions.json = data;
155
+ }
156
+ try {
157
+ if (options.responseType === "blob") {
158
+ return client[method](url, kyOptions).blob();
159
+ }
160
+ const response = await client[method](url, kyOptions);
161
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
162
+ return;
163
+ }
164
+ return response.json();
165
+ } catch (error) {
166
+ if (ApiError.is(error))
167
+ throw error;
168
+ const isAbort = error instanceof Error && error.name === "AbortError";
169
+ if (!isAbort) {
170
+ emit({ type: "network_error" });
171
+ }
172
+ const status = isAbort ? 0 : isHTTPError(error) ? error.response.status : 0;
173
+ const msg = error instanceof Error ? error.message : undefined;
174
+ throw new ApiError("UNKNOWN_ERROR", status, msg ? { message: msg } : undefined);
175
+ }
176
+ }
177
+ return {
178
+ get: (url, options) => request("get", url, undefined, options),
179
+ post: (url, data, options) => request("post", url, data, options),
180
+ put: (url, data, options) => request("put", url, data, options),
181
+ patch: (url, data, options) => request("patch", url, data, options),
182
+ delete: (url, options) => request("delete", url, undefined, options),
183
+ setServerContext(cookies) {
184
+ ssrCookies = cookies;
185
+ },
186
+ subscribe(listener) {
187
+ listeners.add(listener);
188
+ return () => listeners.delete(listener);
189
+ },
190
+ logout() {
191
+ isLoggedOut = true;
192
+ emit({ type: "logout" });
193
+ },
194
+ resetLogoutState() {
195
+ isLoggedOut = false;
196
+ }
197
+ };
198
+ }
199
+
200
+ // src/browser/client.ts
201
+ function withTimeout(options, timeout) {
202
+ if (timeout === undefined)
203
+ return options;
204
+ return { ...options, timeout };
205
+ }
206
+ function isParamArray(value) {
207
+ return Array.isArray(value) && value.every((v) => typeof v === "string" || typeof v === "number");
208
+ }
209
+ function collectQueryParams(args, skipKeys) {
210
+ const params = {};
211
+ let hasParams = false;
212
+ for (const [key, value] of Object.entries(args)) {
213
+ if (skipKeys.has(key) || value === undefined || value === null)
214
+ continue;
215
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || isParamArray(value)) {
216
+ params[key] = value;
217
+ hasParams = true;
218
+ }
219
+ }
220
+ return hasParams ? params : undefined;
221
+ }
222
+ function createClient(contract, configOrClient, contractConfig) {
223
+ const client = {};
224
+ const makeMethod = isHttpAdapter(configOrClient) ? (endpoint) => createHttpMethod(endpoint, contract.meta.prefix, configOrClient, contractConfig) : (endpoint) => createFetchMethod(endpoint, contract.meta.prefix, configOrClient, contractConfig);
225
+ for (const [key, endpoint] of typedEntries(contract.endpoints)) {
226
+ if (endpoint.expose && !endpoint.expose.includes("HTTP"))
227
+ continue;
228
+ setClientMethod(client, key, makeMethod(endpoint));
229
+ }
230
+ return client;
231
+ }
232
+ function createClients(contracts, http) {
233
+ return mapObject(contracts, (_key, contract) => createClient(contract, http));
234
+ }
235
+ function isHttpAdapter(value) {
236
+ return typeof value === "object" && "get" in value && typeof value.get === "function";
237
+ }
238
+ function setClientMethod(target, key, method) {
239
+ target[key] = method;
240
+ }
241
+ function createHttpMethod(endpoint, prefix, client, config) {
242
+ const httpMethod = endpoint.method.toLowerCase();
243
+ const isGet = httpMethod === "get";
244
+ const paramNames = extractParamNames(endpoint.path);
245
+ const prefixKeys = new Set([...config?.stripPrefixKeys ?? [], ...paramNames]);
246
+ return (...args) => {
247
+ const firstArg = args[0] ?? {};
248
+ let pathPrefixStr = "";
249
+ if (config?.pathPrefix) {
250
+ pathPrefixStr = typeof config.pathPrefix === "function" ? config.pathPrefix(firstArg) : config.pathPrefix;
251
+ if (pathPrefixStr && !pathPrefixStr.endsWith("/"))
252
+ pathPrefixStr += "/";
253
+ }
254
+ let url = `${pathPrefixStr}${prefix}${endpoint.path}`;
255
+ for (const name of paramNames) {
256
+ const value = firstArg[name];
257
+ if (value === undefined || value === null) {
258
+ throw new Error(`Missing path param: ${name}`);
259
+ }
260
+ url = url.replace(`:${name}`, encodeURIComponent(String(value)));
261
+ }
262
+ if (url.endsWith("/"))
263
+ url = url.slice(0, -1);
264
+ if (endpoint.multipart) {
265
+ const file = firstArg[endpoint.multipart];
266
+ if (!isMultipartFile(file)) {
267
+ throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
268
+ }
269
+ const formData = new FormData;
270
+ appendMultipartFile(formData, endpoint.multipart, file);
271
+ appendFormFields(formData, firstArg, new Set([...prefixKeys, endpoint.multipart]));
272
+ return client.post(url, formData, withTimeout(undefined, endpoint.timeout));
273
+ }
274
+ if (isGet) {
275
+ const params = collectQueryParams(firstArg, prefixKeys);
276
+ return client.get(url, withTimeout(params ? { params } : undefined, endpoint.timeout));
277
+ }
278
+ if (httpMethod === "delete") {
279
+ const params = collectQueryParams(firstArg, prefixKeys);
280
+ return client.delete(url, withTimeout(params ? { params } : undefined, endpoint.timeout));
281
+ }
282
+ const payload = {};
283
+ for (const [key, value] of Object.entries(firstArg)) {
284
+ if (!prefixKeys.has(key) && value !== undefined) {
285
+ payload[key] = value;
286
+ }
287
+ }
288
+ return client[httpMethod](url, Object.keys(payload).length > 0 ? payload : undefined, withTimeout(undefined, endpoint.timeout));
289
+ };
290
+ }
291
+ function createFetchMethod(endpoint, prefix, config, contractConfig) {
292
+ const prefixKeys = new Set(contractConfig?.stripPrefixKeys ?? []);
293
+ return async (args) => {
294
+ let pathPrefixStr = "";
295
+ if (contractConfig?.pathPrefix) {
296
+ pathPrefixStr = typeof contractConfig.pathPrefix === "function" ? contractConfig.pathPrefix(args ?? {}) : contractConfig.pathPrefix;
297
+ if (pathPrefixStr && !pathPrefixStr.endsWith("/"))
298
+ pathPrefixStr += "/";
299
+ }
300
+ let url = buildFetchUrl(config.baseUrl, prefix, endpoint.path, args, pathPrefixStr);
301
+ const headers = {
302
+ Accept: "application/json",
303
+ ...typeof config.headers === "function" ? config.headers() : config.headers
304
+ };
305
+ const isQuery = inputIsQuery(endpoint.method);
306
+ const hasBody = !isQuery && !endpoint.multipart && endpoint.input && args;
307
+ if (isQuery && args) {
308
+ const remaining = stripParams(args, endpoint.path, prefixKeys);
309
+ const searchParams = new URLSearchParams;
310
+ for (const [k, v] of Object.entries(remaining)) {
311
+ if (v === undefined || v === null)
312
+ continue;
313
+ if (isParamArray(v)) {
314
+ for (const item of v)
315
+ searchParams.append(k, String(item));
316
+ } else if (typeof v !== "object") {
317
+ searchParams.set(k, String(v));
318
+ }
319
+ }
320
+ if (searchParams.size > 0)
321
+ url += `?${searchParams}`;
322
+ }
323
+ if (hasBody)
324
+ headers["Content-Type"] = "application/json";
325
+ if (endpoint.multipart && args) {
326
+ const file = args[endpoint.multipart];
327
+ if (!isMultipartFile(file)) {
328
+ throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
329
+ }
330
+ const formData = new FormData;
331
+ appendMultipartFile(formData, endpoint.multipart, file);
332
+ appendFormFields(formData, stripParams(args, endpoint.path, prefixKeys), new Set([endpoint.multipart]));
333
+ const res2 = await fetch(url, {
334
+ method: endpoint.method,
335
+ headers,
336
+ credentials: config.credentials,
337
+ body: formData
338
+ });
339
+ if (!res2.ok) {
340
+ await throwForErrorResponse(res2, config, null);
341
+ }
342
+ if (res2.status === 204)
343
+ return;
344
+ const json2 = await res2.json();
345
+ return endpoint.output ? endpoint.output.parse(json2) : json2;
346
+ }
347
+ const res = await fetch(url, {
348
+ method: endpoint.method,
349
+ headers,
350
+ credentials: config.credentials,
351
+ ...hasBody && {
352
+ body: JSON.stringify(stripParams(hasBody, endpoint.path, prefixKeys))
353
+ }
354
+ });
355
+ if (!res.ok) {
356
+ await throwForErrorResponse(res, config, { error: res.statusText });
357
+ }
358
+ if (res.status === 204)
359
+ return;
360
+ const json = await res.json();
361
+ return endpoint.output ? endpoint.output.parse(json) : json;
362
+ };
363
+ }
364
+ function isFileDescriptor(value) {
365
+ return typeof value === "object" && value !== null && !(value instanceof Blob) && "uri" in value && typeof value.uri === "string" && "name" in value && typeof value.name === "string" && "type" in value && typeof value.type === "string";
366
+ }
367
+ function isMultipartFile(value) {
368
+ return value instanceof Blob || isFileDescriptor(value);
369
+ }
370
+ function appendMultipartFile(form, field, file) {
371
+ const sink = form;
372
+ sink.append(field, file);
373
+ }
374
+ function appendFormFields(formData, values, skipKeys) {
375
+ for (const [key, value] of Object.entries(values)) {
376
+ if (skipKeys.has(key) || value === undefined || value === null)
377
+ continue;
378
+ formData.append(key, typeof value === "string" ? value : JSON.stringify(value));
379
+ }
380
+ }
381
+ async function throwForErrorResponse(res, config, fallbackBody) {
382
+ const body = await res.json().catch(() => fallbackBody);
383
+ config.onError?.(res.status, body);
384
+ const parsed = parseApiErrorBody(body);
385
+ if (parsed) {
386
+ throw new ApiError(parsed.code, res.status, parsed.details, parsed.message, parsed.hint);
387
+ }
388
+ throw new ApiError("HTTP_ERROR", res.status, { body });
389
+ }
390
+ function extractParamNames(path) {
391
+ const matches = path.match(/:(\w+)/g);
392
+ return matches ? matches.map((m) => m.slice(1)) : [];
393
+ }
394
+ function buildFetchUrl(baseUrl, prefix, path, args, pathPrefix = "") {
395
+ let fullPath = `/${pathPrefix}${prefix}${path === "/" ? "" : path}`;
396
+ if (args) {
397
+ fullPath = fullPath.replace(/:(\w+)/g, (_, key) => {
398
+ const val = args[key];
399
+ if (val === undefined || val === null) {
400
+ throw new Error(`Missing path param: ${key}`);
401
+ }
402
+ return encodeURIComponent(String(val));
403
+ });
404
+ }
405
+ return `${baseUrl}${fullPath}`;
406
+ }
407
+ function stripParams(args, path, extra) {
408
+ const skip = new Set(extra);
409
+ for (const match of path.matchAll(/:(\w+)/g)) {
410
+ if (match[1])
411
+ skip.add(match[1]);
412
+ }
413
+ const result = {};
414
+ for (const [k, v] of Object.entries(args)) {
415
+ if (!skip.has(k))
416
+ result[k] = v;
417
+ }
418
+ return result;
419
+ }
31
420
  // src/browser/socket-io.ts
32
421
  import { io } from "socket.io-client";
422
+
423
+ // src/retained.ts
424
+ function createRetainedTopics() {
425
+ const last = {};
426
+ return {
427
+ record(topic, payload) {
428
+ last[topic] = payload;
429
+ },
430
+ replay(topic, handler) {
431
+ const value = last[topic];
432
+ if (value !== undefined)
433
+ handler(value);
434
+ },
435
+ get(topic) {
436
+ return last[topic];
437
+ },
438
+ clear(topic) {
439
+ if (topic !== undefined) {
440
+ delete last[topic];
441
+ return;
442
+ }
443
+ for (const key of Object.keys(last))
444
+ Reflect.deleteProperty(last, key);
445
+ }
446
+ };
447
+ }
448
+
449
+ // src/browser/socket-io.ts
33
450
  function toIoAuth(auth) {
34
451
  if (typeof auth !== "function")
35
452
  return auth;
@@ -41,6 +458,9 @@ function createSocketIOClient(config) {
41
458
  let socket = null;
42
459
  const connectionListeners = new Set;
43
460
  const subscriptions = new Set;
461
+ const retainNames = config.retain ? config.retain.map(String) : [];
462
+ const retainSet = new Set(retainNames);
463
+ const retained = retainNames.length > 0 ? createRetainedTopics() : null;
44
464
  function notifyConnection(connected) {
45
465
  for (const listener of connectionListeners)
46
466
  listener(connected);
@@ -68,6 +488,11 @@ function createSocketIOClient(config) {
68
488
  });
69
489
  socket.on("connect", () => notifyConnection(true));
70
490
  socket.on("disconnect", () => notifyConnection(false));
491
+ if (retained) {
492
+ for (const name of retainNames) {
493
+ socket.on(name, (payload) => retained.record(name, payload));
494
+ }
495
+ }
71
496
  for (const attach of subscriptions)
72
497
  attach(socket);
73
498
  socket.connect();
@@ -90,6 +515,10 @@ function createSocketIOClient(config) {
90
515
  subscriptions.add(attach);
91
516
  if (socket)
92
517
  attach(socket);
518
+ if (retained && retainSet.has(name)) {
519
+ const fn = handler;
520
+ retained.replay(name, (payload) => fn(payload));
521
+ }
93
522
  return () => {
94
523
  subscriptions.delete(attach);
95
524
  socket?.off(name, handler);
@@ -107,6 +536,43 @@ function createSocketIOClient(config) {
107
536
  }
108
537
  };
109
538
  }
539
+ // src/internal/errors.ts
540
+ import { z } from "zod";
541
+
542
+ // src/server/stream.ts
543
+ async function* parseSSE(response, options) {
544
+ const reader = response.body?.getReader();
545
+ if (!reader)
546
+ return;
547
+ const decoder = new TextDecoder;
548
+ let buffer = "";
549
+ try {
550
+ while (true) {
551
+ const { done, value } = await reader.read();
552
+ if (done)
553
+ break;
554
+ buffer += decoder.decode(value, { stream: true });
555
+ const lines = buffer.split(`
556
+ `);
557
+ buffer = lines.pop() ?? "";
558
+ for (const rawLine of lines) {
559
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
560
+ if (!line.startsWith("data:"))
561
+ continue;
562
+ const data = line.slice(5).replace(/^ /, "");
563
+ if (data === "[DONE]")
564
+ return;
565
+ try {
566
+ yield JSON.parse(data);
567
+ } catch (err) {
568
+ options?.onParseError?.(data, err instanceof Error ? err : new Error(String(err)));
569
+ }
570
+ }
571
+ }
572
+ } finally {
573
+ reader.releaseLock();
574
+ }
575
+ }
110
576
  export {
111
577
  unauthorized,
112
578
  rateLimited,
@@ -119,6 +585,7 @@ export {
119
585
  defineContract,
120
586
  decodeCursor,
121
587
  createSocketIOClient,
588
+ createRetainedTopics,
122
589
  createHttpClient,
123
590
  createClients,
124
591
  createClient,
package/dist/node.js CHANGED
@@ -3,9 +3,7 @@ import {
3
3
  createImplement,
4
4
  createSocketIOServer,
5
5
  implement
6
- } from "./index-5789sbt8.js";
7
- import"./index-x3fcszf8.js";
8
- import"./index-wjwj5bz2.js";
6
+ } from "./index-zshrc6kx.js";
9
7
  import {
10
8
  AppError,
11
9
  appError,
@@ -15,11 +13,9 @@ import {
15
13
  notFound,
16
14
  rateLimited,
17
15
  unauthorized
18
- } from "./index-eq29zkrx.js";
19
- import"./index-mwmpw6j1.js";
20
- import"./index-kzfs85xp.js";
21
- import"./index-809wc1tt.js";
22
- import"./index-37x76zdn.js";
16
+ } from "./index-jgpsd7dy.js";
17
+ import"./index-p9m9c0jw.js";
18
+ import"./index-tm7dqzxc.js";
23
19
  // src/server/node.ts
24
20
  import { serve } from "srvx";
25
21
  async function serveNode(config) {
@@ -11,15 +11,12 @@ import {
11
11
  setRequestError,
12
12
  setRequestUser,
13
13
  wrapInRequestContext
14
- } from "../index-za2p453b.js";
15
- import"../index-mwmpw6j1.js";
14
+ } from "../index-031q8xmx.js";
15
+ import"../index-p9m9c0jw.js";
16
16
  import {
17
+ isRecord,
17
18
  isUnsafeKey
18
- } from "../index-kzfs85xp.js";
19
- import {
20
- isRecord
21
- } from "../index-809wc1tt.js";
22
- import"../index-37x76zdn.js";
19
+ } from "../index-tm7dqzxc.js";
23
20
 
24
21
  // src/observability/sanitize.ts
25
22
  var DEFAULT_SENSITIVE_KEYS = /(password|passwd|pwd|secret|token|apikey|api[-_ ]?key|auth|authorization|bearer|session|cookie|init[-_ ]?data|credential|private[-_ ]?key)/i;
package/dist/react.js CHANGED
@@ -1,5 +1,3 @@
1
- import"./index-37x76zdn.js";
2
-
3
1
  // src/react/cache-bridge.ts
4
2
  function createCacheBridge(config) {
5
3
  const freshWindow = config.freshWindow ?? 500;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Retained-last-value memory for pub/sub topics — "sticky events".
3
+ *
4
+ * A subscriber that connects (or a component that mounts / re-renders) *after* an
5
+ * event was published would otherwise miss it and show stale state until the next
6
+ * publish. Record each topic's last payload and replay it to a fresh subscriber,
7
+ * so a late one catches up at once — the pub/sub analogue of MQTT's "retained"
8
+ * message or an RxJS `BehaviorSubject`.
9
+ *
10
+ * Transport-agnostic: wrap it around any pub/sub. `createSocketIOClient`'s
11
+ * `retain` option uses it internally, and a bring-your-own-transport lane (a raw
12
+ * WebSocket driving a contract through `createContractDispatcher`) can use it
13
+ * directly for its own event channel. Browser-safe — no Node built-ins.
14
+ */
15
+ export interface RetainedTopics<Events extends Record<string, unknown>> {
16
+ /** Record a topic's latest payload — call on every publish / receive. */
17
+ record<K extends keyof Events & string>(topic: K, payload: Events[K]): void;
18
+ /** Replay the retained payload, if any, to a just-subscribed handler. */
19
+ replay<K extends keyof Events & string>(topic: K, handler: (payload: Events[K]) => void): void;
20
+ /** The retained payload for a topic, or `undefined` if none recorded yet. */
21
+ get<K extends keyof Events & string>(topic: K): Events[K] | undefined;
22
+ /** Forget a topic's retained value, or every topic when `topic` is omitted. */
23
+ clear(topic?: keyof Events & string): void;
24
+ }
25
+ /**
26
+ * Create a {@link RetainedTopics} store. `Events` maps each topic name to its
27
+ * payload type, so `record` / `replay` / `get` are typed per topic.
28
+ */
29
+ export declare function createRetainedTopics<Events extends Record<string, unknown>>(): RetainedTopics<Events>;
30
+ //# sourceMappingURL=retained.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retained.d.ts","sourceRoot":"","sources":["../src/retained.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,yEAAyE;IACzE,MAAM,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC5E,yEAAyE;IACzE,MAAM,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EACpC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GACpC,IAAI,CAAC;IACR,6EAA6E;IAC7E,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE,+EAA+E;IAC/E,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACnC,cAAc,CAAC,MAAM,CAAC,CAqB1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/server/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAa,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,EAC5C,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CA6C3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,MACjD,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC3C,UAAU,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,UAAU,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,KAC1B,UAAU,CACd"}
1
+ {"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../../src/server/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,KAAK,EAAE,QAAQ,EAAa,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrC,IAAI,SAAS,cAAc,GAAG,cAAc,EAC5C,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CA+C3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,MACjD,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EAC3C,UAAU,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,UAAU,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,KAC1B,UAAU,CACd"}