bunderstack 0.19.2 → 0.21.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 (149) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/dist/api/crud-router.d.ts +23 -28
  3. package/dist/api/crud-router.d.ts.map +1 -1
  4. package/dist/api/crud-router.js +39 -18
  5. package/dist/api/crud-router.js.map +1 -1
  6. package/dist/api/live-window.d.ts.map +1 -1
  7. package/dist/api/live-window.js +13 -3
  8. package/dist/api/live-window.js.map +1 -1
  9. package/dist/api/realtime-router.d.ts +3 -1
  10. package/dist/api/realtime-router.d.ts.map +1 -1
  11. package/dist/api/realtime-router.js +1 -0
  12. package/dist/api/realtime-router.js.map +1 -1
  13. package/dist/api/types.d.ts +2 -2
  14. package/dist/api/types.d.ts.map +1 -1
  15. package/dist/api/types.js.map +1 -1
  16. package/dist/client/client.d.ts +53 -0
  17. package/dist/client/client.d.ts.map +1 -0
  18. package/dist/client/client.js +111 -0
  19. package/dist/client/client.js.map +1 -0
  20. package/dist/client/index.d.ts +4 -0
  21. package/dist/client/index.d.ts.map +1 -0
  22. package/dist/client/index.js +4 -0
  23. package/dist/client/index.js.map +1 -0
  24. package/dist/client/live-view.d.ts +49 -0
  25. package/dist/client/live-view.d.ts.map +1 -0
  26. package/dist/client/live-view.js +184 -0
  27. package/dist/client/live-view.js.map +1 -0
  28. package/dist/client/react.d.ts +4 -0
  29. package/dist/client/react.d.ts.map +1 -0
  30. package/dist/client/react.js +6 -0
  31. package/dist/client/react.js.map +1 -0
  32. package/dist/client/realtime.d.ts +54 -0
  33. package/dist/client/realtime.d.ts.map +1 -0
  34. package/dist/client/realtime.js +105 -0
  35. package/dist/client/realtime.js.map +1 -0
  36. package/dist/client/rest.d.ts +2 -0
  37. package/dist/client/rest.d.ts.map +1 -0
  38. package/dist/client/rest.js +2 -0
  39. package/dist/client/rest.js.map +1 -0
  40. package/dist/client/rpc-client.d.ts +38 -0
  41. package/dist/client/rpc-client.d.ts.map +1 -0
  42. package/dist/client/rpc-client.js +59 -0
  43. package/dist/client/rpc-client.js.map +1 -0
  44. package/dist/client/solid.d.ts +7 -0
  45. package/dist/client/solid.d.ts.map +1 -0
  46. package/dist/client/solid.js +21 -0
  47. package/dist/client/solid.js.map +1 -0
  48. package/dist/client/svelte.d.ts +6 -0
  49. package/dist/client/svelte.d.ts.map +1 -0
  50. package/dist/client/svelte.js +9 -0
  51. package/dist/client/svelte.js.map +1 -0
  52. package/dist/client/vue.d.ts +5 -0
  53. package/dist/client/vue.d.ts.map +1 -0
  54. package/dist/client/vue.js +11 -0
  55. package/dist/client/vue.js.map +1 -0
  56. package/dist/codegen.d.ts +36 -0
  57. package/dist/codegen.d.ts.map +1 -0
  58. package/dist/codegen.js +235 -0
  59. package/dist/codegen.js.map +1 -0
  60. package/dist/crud-operations.d.ts.map +1 -1
  61. package/dist/crud-operations.js +7 -3
  62. package/dist/crud-operations.js.map +1 -1
  63. package/dist/index.d.ts +9 -4
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js.map +1 -1
  66. package/dist/live/protocol.d.ts +3 -0
  67. package/dist/live/protocol.d.ts.map +1 -1
  68. package/dist/live/protocol.js.map +1 -1
  69. package/dist/query/api.d.ts +10 -0
  70. package/dist/query/api.d.ts.map +1 -0
  71. package/dist/query/api.js +7 -0
  72. package/dist/query/api.js.map +1 -0
  73. package/dist/query/client.d.ts +33 -0
  74. package/dist/query/client.d.ts.map +1 -0
  75. package/dist/query/client.js +98 -0
  76. package/dist/query/client.js.map +1 -0
  77. package/dist/query/fetch.d.ts +3 -0
  78. package/dist/query/fetch.d.ts.map +1 -0
  79. package/dist/query/fetch.js +4 -0
  80. package/dist/query/fetch.js.map +1 -0
  81. package/dist/query/index.d.ts +8 -0
  82. package/dist/query/index.d.ts.map +1 -0
  83. package/dist/query/index.js +4 -0
  84. package/dist/query/index.js.map +1 -0
  85. package/dist/query/infer.d.ts +25 -0
  86. package/dist/query/infer.d.ts.map +1 -0
  87. package/dist/query/infer.js +2 -0
  88. package/dist/query/infer.js.map +1 -0
  89. package/dist/query/react.d.ts +3 -0
  90. package/dist/query/react.d.ts.map +1 -0
  91. package/dist/query/react.js +2 -0
  92. package/dist/query/react.js.map +1 -0
  93. package/dist/query/realtime-flush.d.ts +26 -0
  94. package/dist/query/realtime-flush.d.ts.map +1 -0
  95. package/dist/query/realtime-flush.js +45 -0
  96. package/dist/query/realtime-flush.js.map +1 -0
  97. package/dist/query/realtime-stream.d.ts +3 -0
  98. package/dist/query/realtime-stream.d.ts.map +1 -0
  99. package/dist/query/realtime-stream.js +3 -0
  100. package/dist/query/realtime-stream.js.map +1 -0
  101. package/dist/query/realtime.d.ts +88 -0
  102. package/dist/query/realtime.d.ts.map +1 -0
  103. package/dist/query/realtime.js +255 -0
  104. package/dist/query/realtime.js.map +1 -0
  105. package/dist/realtime/facade.d.ts +3 -1
  106. package/dist/realtime/facade.d.ts.map +1 -1
  107. package/dist/realtime/facade.js +2 -1
  108. package/dist/realtime/facade.js.map +1 -1
  109. package/dist/realtime/filter.d.ts.map +1 -1
  110. package/dist/realtime/filter.js +1 -0
  111. package/dist/realtime/filter.js.map +1 -1
  112. package/dist/realtime/publisher.d.ts +2 -0
  113. package/dist/realtime/publisher.d.ts.map +1 -1
  114. package/dist/realtime/publisher.js.map +1 -1
  115. package/dist/start/auth-client.d.ts +11 -0
  116. package/dist/start/auth-client.d.ts.map +1 -0
  117. package/dist/start/auth-client.js +15 -0
  118. package/dist/start/auth-client.js.map +1 -0
  119. package/dist/start/index.d.ts +66 -0
  120. package/dist/start/index.d.ts.map +1 -0
  121. package/dist/start/index.js +49 -0
  122. package/dist/start/index.js.map +1 -0
  123. package/dist/start/isomorphic-fetch.d.ts +13 -0
  124. package/dist/start/isomorphic-fetch.d.ts.map +1 -0
  125. package/dist/start/isomorphic-fetch.js +33 -0
  126. package/dist/start/isomorphic-fetch.js.map +1 -0
  127. package/dist/sync/collection.d.ts +106 -0
  128. package/dist/sync/collection.d.ts.map +1 -0
  129. package/dist/sync/collection.js +258 -0
  130. package/dist/sync/collection.js.map +1 -0
  131. package/dist/sync/index.d.ts +8 -0
  132. package/dist/sync/index.d.ts.map +1 -0
  133. package/dist/sync/index.js +4 -0
  134. package/dist/sync/index.js.map +1 -0
  135. package/dist/sync/realtime-sync.d.ts +31 -0
  136. package/dist/sync/realtime-sync.d.ts.map +1 -0
  137. package/dist/sync/realtime-sync.js +46 -0
  138. package/dist/sync/realtime-sync.js.map +1 -0
  139. package/dist/sync/sync-client.d.ts +28 -0
  140. package/dist/sync/sync-client.d.ts.map +1 -0
  141. package/dist/sync/sync-client.js +52 -0
  142. package/dist/sync/sync-client.js.map +1 -0
  143. package/dist/sync/update-queue.d.ts +27 -0
  144. package/dist/sync/update-queue.d.ts.map +1 -0
  145. package/dist/sync/update-queue.js +66 -0
  146. package/dist/sync/update-queue.js.map +1 -0
  147. package/package.json +107 -3
  148. package/skills/creating-bunderstack-apps/references/runtime-integrations.md +1 -1
  149. package/skills/migrating-to-bunderstack/references/runtime-replacements.md +2 -2
@@ -41,8 +41,8 @@ type FilterableOf<TAccess, K extends string> = K extends keyof TAccess ? TAccess
41
41
  type SortableOf<TAccess, K extends string> = K extends keyof TAccess ? TAccess[K] extends {
42
42
  sortableColumns: readonly (infer S extends string)[];
43
43
  } ? S : 'id' : 'id';
44
- export type CrudApiRouterFor<TSchema extends Record<string, unknown>, TAccess = undefined> = {
45
- [K in ExposedApiTables<TSchema, TAccess> as TSchema[K] extends Table ? K : never]: TableCrudProcedures<Extract<TSchema[K], Table>, FilterableOf<TAccess, K>, SortableOf<TAccess, K>>;
44
+ export type CrudApiRouterFor<TSchema extends Record<string, unknown>, TAccess = undefined, TLive extends boolean = false> = {
45
+ [K in ExposedApiTables<TSchema, TAccess> as TSchema[K] extends Table ? K : never]: TableCrudProcedures<Extract<TSchema[K], Table>, FilterableOf<TAccess, K>, SortableOf<TAccess, K>, TLive>;
46
46
  };
47
47
  type IsProcedure<T> = T extends {
48
48
  '~orpc': unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AnyRouter } from '@orpc/server'\nimport type { Table } from 'drizzle-orm'\n\nimport type { AccessUser } from '../access'\nimport type { TableCrudProcedures } from './crud-router'\n\nexport interface ProtectedContextAdditions {\n user: AccessUser\n session: {\n activeOrganizationId: string | null\n }\n}\n\ntype AuthTableName = 'user' | 'session' | 'account' | 'verification'\ntype InferSelect<T> = T extends { $inferSelect: infer R } ? R : never\n\ntype DisabledKeys<TAccess> = {\n [K in keyof TAccess & string]: TAccess[K] extends { crud: false } ? K : never\n}[keyof TAccess & string]\n\ntype ExplicitKeys<TSchema, TAccess> = {\n [K in keyof TAccess & keyof TSchema & string]: TAccess[K] extends {\n crud: false\n }\n ? never\n : K extends AuthTableName\n ? TAccess[K] extends { exposeAuthTable: true }\n ? K extends 'user'\n ? K\n : never\n : never\n : K\n}[keyof TAccess & keyof TSchema & string]\n\ntype ConventionKeys<TSchema> = {\n [K in keyof TSchema & string]: K extends AuthTableName\n ? never\n : InferSelect<TSchema[K]> extends { userId: unknown }\n ? K\n : never\n}[keyof TSchema & string]\n\nexport type ExposedApiTables<TSchema, TAccess> = [TAccess] extends [undefined]\n ? ConventionKeys<TSchema>\n :\n | ExplicitKeys<TSchema, TAccess>\n | Exclude<\n ConventionKeys<TSchema>,\n DisabledKeys<TAccess> | (keyof TAccess & string)\n >\n\n/**\n * Column allowlists declared in `access` reach the client as literal unions, so\n * `list` can type `filters` and `sort` per table. Tables that declare nothing\n * get `never` filters and `'id'` sorting, matching the runtime defaults.\n */\ntype FilterableOf<TAccess, K extends string> = K extends keyof TAccess\n ? TAccess[K] extends {\n filterableColumns: readonly (infer F extends string)[]\n }\n ? F\n : never\n : never\n\ntype SortableOf<TAccess, K extends string> = K extends keyof TAccess\n ? TAccess[K] extends { sortableColumns: readonly (infer S extends string)[] }\n ? S\n : 'id'\n : 'id'\n\nexport type CrudApiRouterFor<\n TSchema extends Record<string, unknown>,\n TAccess = undefined,\n> = {\n [K in ExposedApiTables<TSchema, TAccess> as TSchema[K] extends Table\n ? K\n : never]: TableCrudProcedures<\n Extract<TSchema[K], Table>,\n FilterableOf<TAccess, K>,\n SortableOf<TAccess, K>\n >\n}\n\ntype IsProcedure<T> = T extends { '~orpc': unknown } ? true : false\n\nexport type MergeApiRouterTypes<A, B> = {\n [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? IsProcedure<A[K]> extends true\n ? never\n : IsProcedure<B[K]> extends true\n ? never\n : A[K] extends Record<string, unknown>\n ? B[K] extends Record<string, unknown>\n ? MergeApiRouterTypes<A[K], B[K]>\n : never\n : never\n : A[K]\n : K extends keyof B\n ? B[K]\n : never\n}\n\nexport type UnifiedApiRouter<\n TCrud,\n TCustom extends AnyRouter | undefined,\n> = TCustom extends AnyRouter ? MergeApiRouterTypes<TCrud, TCustom> : TCrud\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AnyRouter } from '@orpc/server'\nimport type { Table } from 'drizzle-orm'\n\nimport type { AccessUser } from '../access'\nimport type { TableCrudProcedures } from './crud-router'\n\nexport interface ProtectedContextAdditions {\n user: AccessUser\n session: {\n activeOrganizationId: string | null\n }\n}\n\ntype AuthTableName = 'user' | 'session' | 'account' | 'verification'\ntype InferSelect<T> = T extends { $inferSelect: infer R } ? R : never\n\ntype DisabledKeys<TAccess> = {\n [K in keyof TAccess & string]: TAccess[K] extends { crud: false } ? K : never\n}[keyof TAccess & string]\n\ntype ExplicitKeys<TSchema, TAccess> = {\n [K in keyof TAccess & keyof TSchema & string]: TAccess[K] extends {\n crud: false\n }\n ? never\n : K extends AuthTableName\n ? TAccess[K] extends { exposeAuthTable: true }\n ? K extends 'user'\n ? K\n : never\n : never\n : K\n}[keyof TAccess & keyof TSchema & string]\n\ntype ConventionKeys<TSchema> = {\n [K in keyof TSchema & string]: K extends AuthTableName\n ? never\n : InferSelect<TSchema[K]> extends { userId: unknown }\n ? K\n : never\n}[keyof TSchema & string]\n\nexport type ExposedApiTables<TSchema, TAccess> = [TAccess] extends [undefined]\n ? ConventionKeys<TSchema>\n :\n | ExplicitKeys<TSchema, TAccess>\n | Exclude<\n ConventionKeys<TSchema>,\n DisabledKeys<TAccess> | (keyof TAccess & string)\n >\n\n/**\n * Column allowlists declared in `access` reach the client as literal unions, so\n * `list` can type `filters` and `sort` per table. Tables that declare nothing\n * get `never` filters and `'id'` sorting, matching the runtime defaults.\n */\ntype FilterableOf<TAccess, K extends string> = K extends keyof TAccess\n ? TAccess[K] extends {\n filterableColumns: readonly (infer F extends string)[]\n }\n ? F\n : never\n : never\n\ntype SortableOf<TAccess, K extends string> = K extends keyof TAccess\n ? TAccess[K] extends { sortableColumns: readonly (infer S extends string)[] }\n ? S\n : 'id'\n : 'id'\n\nexport type CrudApiRouterFor<\n TSchema extends Record<string, unknown>,\n TAccess = undefined,\n TLive extends boolean = false,\n> = {\n [K in ExposedApiTables<TSchema, TAccess> as TSchema[K] extends Table\n ? K\n : never]: TableCrudProcedures<\n Extract<TSchema[K], Table>,\n FilterableOf<TAccess, K>,\n SortableOf<TAccess, K>,\n TLive\n >\n}\n\ntype IsProcedure<T> = T extends { '~orpc': unknown } ? true : false\n\nexport type MergeApiRouterTypes<A, B> = {\n [K in keyof A | keyof B]: K extends keyof A\n ? K extends keyof B\n ? IsProcedure<A[K]> extends true\n ? never\n : IsProcedure<B[K]> extends true\n ? never\n : A[K] extends Record<string, unknown>\n ? B[K] extends Record<string, unknown>\n ? MergeApiRouterTypes<A[K], B[K]>\n : never\n : never\n : A[K]\n : K extends keyof B\n ? B[K]\n : never\n}\n\nexport type UnifiedApiRouter<\n TCrud,\n TCustom extends AnyRouter | undefined,\n> = TCustom extends AnyRouter ? MergeApiRouterTypes<TCrud, TCustom> : TCrud\n"]}
@@ -0,0 +1,53 @@
1
+ import { type CallOptions, type Fetch } from './rpc-client.js';
2
+ export type RouteOperation<TMethod extends string, TPath extends string, TQuery = undefined, TBody = undefined, TOutput = undefined, TStream extends boolean = false> = {
3
+ method: TMethod;
4
+ path: TPath;
5
+ query?: TQuery;
6
+ body?: TBody;
7
+ output?: TOutput;
8
+ stream?: TStream;
9
+ };
10
+ type AnyOperation = {
11
+ method: string;
12
+ path: string;
13
+ query?: unknown;
14
+ body?: unknown;
15
+ output?: unknown;
16
+ stream?: boolean;
17
+ };
18
+ type QueryOf<O extends AnyOperation> = NonNullable<O['query']>;
19
+ type BodyOf<O extends AnyOperation> = NonNullable<O['body']>;
20
+ type OutputOf<O extends AnyOperation> = NonNullable<O['output']>;
21
+ export type RouteCallOptions = CallOptions;
22
+ type PathParamNames<TPath extends string> = TPath extends `${string}{${infer TParam}}${infer TRest}` ? TParam extends `+${infer TName}` ? TName | PathParamNames<TRest> : TParam | PathParamNames<TRest> : never;
23
+ type PathArgs<O extends AnyOperation> = [PathParamNames<O['path']>] extends [
24
+ never
25
+ ] ? object : {
26
+ params: Record<PathParamNames<O['path']>, string>;
27
+ };
28
+ type QueryArgs<O extends AnyOperation> = [O['query']] extends [undefined] ? object : {
29
+ query?: QueryOf<O>;
30
+ };
31
+ type BodyArgs<O extends AnyOperation> = [O['body']] extends [undefined] ? object : {
32
+ body: BodyOf<O>;
33
+ };
34
+ type CallArgs<O extends AnyOperation> = PathArgs<O> & QueryArgs<O> & BodyArgs<O>;
35
+ type HasRequiredArgs<O extends AnyOperation> = [
36
+ PathParamNames<O['path']>
37
+ ] extends [never] ? [O['body']] extends [undefined] ? false : true : true;
38
+ export type RouteMethod<O extends AnyOperation> = (...args: true extends HasRequiredArgs<O> ? [args: CallArgs<O>, options?: RouteCallOptions] : [args?: CallArgs<O>, options?: RouteCallOptions]) => true extends O['stream'] ? AsyncIterable<OutputOf<O>> : Promise<OutputOf<O>>;
39
+ export type ClientOf<TRoutes> = {
40
+ [K in keyof TRoutes]: TRoutes[K] extends AnyOperation ? RouteMethod<TRoutes[K]> : never;
41
+ };
42
+ export type RestClientOptions = {
43
+ baseUrl?: string;
44
+ fetch?: Fetch;
45
+ };
46
+ type RouteDescriptor = {
47
+ method: string;
48
+ path: string;
49
+ stream?: boolean;
50
+ };
51
+ export declare function createRestClient<TRoutes>(routes: Record<string, RouteDescriptor>, options?: RestClientOptions): ClientOf<TRoutes>;
52
+ export {};
53
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import { OPERATION_ID_HEADER } from './rpc-client.js';
2
+ function joinUrl(baseUrl, path) {
3
+ if (!baseUrl)
4
+ return path;
5
+ return `${baseUrl.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
6
+ }
7
+ function fillPath(path, params) {
8
+ return path.replace(/\{(\+?)([^}]+)\}/g, (_, catchAll, name) => {
9
+ const value = params?.[name];
10
+ if (value === undefined)
11
+ throw new Error(`Missing path parameter: ${name}`);
12
+ if (catchAll)
13
+ return value.split('/').map(encodeURIComponent).join('/');
14
+ return encodeURIComponent(value);
15
+ });
16
+ }
17
+ function buildUrl(baseUrl, path, query) {
18
+ const url = joinUrl(baseUrl, path);
19
+ if (!query)
20
+ return url;
21
+ const params = new URLSearchParams();
22
+ for (const [key, value] of Object.entries(query)) {
23
+ if (value === undefined)
24
+ continue;
25
+ params.set(key, typeof value === 'string' ? value : JSON.stringify(value));
26
+ }
27
+ return params.size ? `${url}?${params}` : url;
28
+ }
29
+ async function requestJson(fetcher, url, init) {
30
+ const response = await fetcher(url, init);
31
+ if (!response.ok)
32
+ throw new Error(`${response.status} ${await response.text()}`);
33
+ if (response.status === 204)
34
+ return undefined;
35
+ return response.json();
36
+ }
37
+ async function* streamJsonEvents(fetcher, url, init) {
38
+ const response = await fetcher(url, init);
39
+ if (!response.ok || !response.body) {
40
+ throw new Error(`Stream request failed (${response.status})`);
41
+ }
42
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
43
+ let buffer = '';
44
+ let data = [];
45
+ const flush = () => {
46
+ if (!data.length)
47
+ return undefined;
48
+ const value = JSON.parse(data.join('\n'));
49
+ data = [];
50
+ return value;
51
+ };
52
+ try {
53
+ while (true) {
54
+ const { done, value } = await reader.read();
55
+ if (done)
56
+ break;
57
+ buffer += value;
58
+ let newline = buffer.indexOf('\n');
59
+ while (newline !== -1) {
60
+ const line = buffer.slice(0, newline).replace(/\r$/, '');
61
+ buffer = buffer.slice(newline + 1);
62
+ if (line === '') {
63
+ const event = flush();
64
+ if (event !== undefined)
65
+ yield event;
66
+ }
67
+ else if (line.startsWith('data:')) {
68
+ data.push(line.slice(5).replace(/^ /, ''));
69
+ }
70
+ newline = buffer.indexOf('\n');
71
+ }
72
+ }
73
+ const event = flush();
74
+ if (event !== undefined)
75
+ yield event;
76
+ }
77
+ finally {
78
+ await reader.cancel().catch(() => { });
79
+ }
80
+ }
81
+ export function createRestClient(routes, options = {}) {
82
+ const fetcher = options.fetch ?? globalThis.fetch;
83
+ const client = {};
84
+ for (const [name, route] of Object.entries(routes)) {
85
+ client[name] = (args = {}, callOptions = {}) => {
86
+ const path = fillPath(route.path, args.params);
87
+ const url = buildUrl(options.baseUrl, path, args.query);
88
+ const headers = new Headers(callOptions.headers);
89
+ if (callOptions.operationId)
90
+ headers.set(OPERATION_ID_HEADER, callOptions.operationId);
91
+ if (route.stream) {
92
+ headers.set('accept', 'text/event-stream');
93
+ return streamJsonEvents(fetcher, url, {
94
+ method: route.method,
95
+ signal: callOptions.signal,
96
+ headers,
97
+ });
98
+ }
99
+ if (args.body !== undefined)
100
+ headers.set('content-type', 'application/json');
101
+ return requestJson(fetcher, url, {
102
+ method: route.method,
103
+ signal: callOptions.signal,
104
+ headers,
105
+ ...(args.body === undefined ? {} : { body: JSON.stringify(args.body) }),
106
+ });
107
+ };
108
+ }
109
+ return client;
110
+ }
111
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAgC,MAAM,cAAc,CAAA;AAqFhF,SAAS,OAAO,CAAC,OAA2B,EAAE,IAAY;IACxD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IACzB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAA;AACnE,CAAC;AAED,SAAS,QAAQ,CACf,IAAY,EACZ,MAA0C;IAE1C,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAY,EAAE,EAAE;QACrE,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAA;QAC3E,IAAI,QAAQ;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACvE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,QAAQ,CACf,OAA2B,EAC3B,IAAY,EACZ,KAA0C;IAE1C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAA;IACtB,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;AAC/C,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,OAAc,EACd,GAAW,EACX,IAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACzC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAChE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,SAAS,CAAA;IAC7C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAA;AACxB,CAAC;AAED,KAAK,SAAS,CAAC,CAAC,gBAAgB,CAC9B,OAAc,EACd,GAAW,EACX,IAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACzC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAC,SAAS,EAAE,CAAA;IAC7E,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,IAAI,GAAa,EAAE,CAAA;IACvB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAA;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAY,CAAA;QACpD,IAAI,GAAG,EAAE,CAAA;QACT,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IACD,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;YAC3C,IAAI,IAAI;gBAAE,MAAK;YACf,MAAM,IAAI,KAAK,CAAA;YACf,IAAI,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAClC,OAAO,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;gBACxD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;gBAClC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;oBAChB,MAAM,KAAK,GAAG,KAAK,EAAE,CAAA;oBACrB,IAAI,KAAK,KAAK,SAAS;wBAAE,MAAM,KAAK,CAAA;gBACtC,CAAC;qBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBACpC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;gBAC5C,CAAC;gBACD,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,EAAE,CAAA;QACrB,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,KAAK,CAAA;IACtC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IACvC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAuC,EACvC,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;IACjD,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,IAAI,CAAC,GAAG,CACb,OAII,EAAE,EACN,cAAgC,EAAE,EAClC,EAAE;YACF,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;YAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YACvD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;YAChD,IAAI,WAAW,CAAC,WAAW;gBACzB,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,WAAW,CAAC,WAAW,CAAC,CAAA;YAC3D,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;gBAC1C,OAAO,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;oBACpC,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,MAAM,EAAE,WAAW,CAAC,MAAM;oBAC1B,OAAO;iBACR,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBACzB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;YACjD,OAAO,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,OAAO;gBACP,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;aACxE,CAAC,CAAA;QACJ,CAAC,CAAA;IACH,CAAC;IACD,OAAO,MAA2B,CAAA;AACpC,CAAC","sourcesContent":["import { OPERATION_ID_HEADER, type CallOptions, type Fetch } from './rpc-client'\n\nexport type RouteOperation<\n TMethod extends string,\n TPath extends string,\n TQuery = undefined,\n TBody = undefined,\n TOutput = undefined,\n TStream extends boolean = false,\n> = {\n method: TMethod\n path: TPath\n query?: TQuery\n body?: TBody\n output?: TOutput\n stream?: TStream\n}\n\ntype AnyOperation = {\n method: string\n path: string\n query?: unknown\n body?: unknown\n output?: unknown\n stream?: boolean\n}\n\ntype QueryOf<O extends AnyOperation> = NonNullable<O['query']>\ntype BodyOf<O extends AnyOperation> = NonNullable<O['body']>\ntype OutputOf<O extends AnyOperation> = NonNullable<O['output']>\n\nexport type RouteCallOptions = CallOptions\n\ntype PathParamNames<TPath extends string> =\n TPath extends `${string}{${infer TParam}}${infer TRest}`\n ? TParam extends `+${infer TName}`\n ? TName | PathParamNames<TRest>\n : TParam | PathParamNames<TRest>\n : never\n\ntype PathArgs<O extends AnyOperation> = [PathParamNames<O['path']>] extends [\n never,\n]\n ? object\n : { params: Record<PathParamNames<O['path']>, string> }\n\ntype QueryArgs<O extends AnyOperation> = [O['query']] extends [undefined]\n ? object\n : { query?: QueryOf<O> }\n\ntype BodyArgs<O extends AnyOperation> = [O['body']] extends [undefined]\n ? object\n : { body: BodyOf<O> }\n\ntype CallArgs<O extends AnyOperation> = PathArgs<O> & QueryArgs<O> & BodyArgs<O>\n\ntype HasRequiredArgs<O extends AnyOperation> = [\n PathParamNames<O['path']>,\n] extends [never]\n ? [O['body']] extends [undefined]\n ? false\n : true\n : true\n\nexport type RouteMethod<O extends AnyOperation> = (\n ...args: true extends HasRequiredArgs<O>\n ? [args: CallArgs<O>, options?: RouteCallOptions]\n : [args?: CallArgs<O>, options?: RouteCallOptions]\n) => true extends O['stream']\n ? AsyncIterable<OutputOf<O>>\n : Promise<OutputOf<O>>\n\nexport type ClientOf<TRoutes> = {\n [K in keyof TRoutes]: TRoutes[K] extends AnyOperation\n ? RouteMethod<TRoutes[K]>\n : never\n}\n\nexport type RestClientOptions = {\n baseUrl?: string\n fetch?: Fetch\n}\n\ntype RouteDescriptor = { method: string; path: string; stream?: boolean }\n\nfunction joinUrl(baseUrl: string | undefined, path: string): string {\n if (!baseUrl) return path\n return `${baseUrl.replace(/\\/$/, '')}/${path.replace(/^\\//, '')}`\n}\n\nfunction fillPath(\n path: string,\n params: Record<string, string> | undefined,\n): string {\n return path.replace(/\\{(\\+?)([^}]+)\\}/g, (_, catchAll, name: string) => {\n const value = params?.[name]\n if (value === undefined) throw new Error(`Missing path parameter: ${name}`)\n if (catchAll) return value.split('/').map(encodeURIComponent).join('/')\n return encodeURIComponent(value)\n })\n}\n\nfunction buildUrl(\n baseUrl: string | undefined,\n path: string,\n query: Record<string, unknown> | undefined,\n): string {\n const url = joinUrl(baseUrl, path)\n if (!query) return url\n const params = new URLSearchParams()\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue\n params.set(key, typeof value === 'string' ? value : JSON.stringify(value))\n }\n return params.size ? `${url}?${params}` : url\n}\n\nasync function requestJson(\n fetcher: Fetch,\n url: string,\n init: RequestInit,\n): Promise<unknown> {\n const response = await fetcher(url, init)\n if (!response.ok)\n throw new Error(`${response.status} ${await response.text()}`)\n if (response.status === 204) return undefined\n return response.json()\n}\n\nasync function* streamJsonEvents(\n fetcher: Fetch,\n url: string,\n init: RequestInit,\n): AsyncGenerator<unknown> {\n const response = await fetcher(url, init)\n if (!response.ok || !response.body) {\n throw new Error(`Stream request failed (${response.status})`)\n }\n const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()\n let buffer = ''\n let data: string[] = []\n const flush = () => {\n if (!data.length) return undefined\n const value = JSON.parse(data.join('\\n')) as unknown\n data = []\n return value\n }\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += value\n let newline = buffer.indexOf('\\n')\n while (newline !== -1) {\n const line = buffer.slice(0, newline).replace(/\\r$/, '')\n buffer = buffer.slice(newline + 1)\n if (line === '') {\n const event = flush()\n if (event !== undefined) yield event\n } else if (line.startsWith('data:')) {\n data.push(line.slice(5).replace(/^ /, ''))\n }\n newline = buffer.indexOf('\\n')\n }\n }\n const event = flush()\n if (event !== undefined) yield event\n } finally {\n await reader.cancel().catch(() => {})\n }\n}\n\nexport function createRestClient<TRoutes>(\n routes: Record<string, RouteDescriptor>,\n options: RestClientOptions = {},\n): ClientOf<TRoutes> {\n const fetcher = options.fetch ?? globalThis.fetch\n const client: Record<string, unknown> = {}\n for (const [name, route] of Object.entries(routes)) {\n client[name] = (\n args: {\n params?: Record<string, string>\n query?: Record<string, unknown>\n body?: unknown\n } = {},\n callOptions: RouteCallOptions = {},\n ) => {\n const path = fillPath(route.path, args.params)\n const url = buildUrl(options.baseUrl, path, args.query)\n const headers = new Headers(callOptions.headers)\n if (callOptions.operationId)\n headers.set(OPERATION_ID_HEADER, callOptions.operationId)\n if (route.stream) {\n headers.set('accept', 'text/event-stream')\n return streamJsonEvents(fetcher, url, {\n method: route.method,\n signal: callOptions.signal,\n headers,\n })\n }\n if (args.body !== undefined)\n headers.set('content-type', 'application/json')\n return requestJson(fetcher, url, {\n method: route.method,\n signal: callOptions.signal,\n headers,\n ...(args.body === undefined ? {} : { body: JSON.stringify(args.body) }),\n })\n }\n }\n return client as ClientOf<TRoutes>\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export { createClient, createRpcClient, OPERATION_ID_HEADER, type AnyBunderstackApp, type BunderstackClient, type CallOptions, type ClientOptions, type ClientCarrier, type DirectClient, type Fetch, type InferApiRouter, type RpcClient, } from './rpc-client.js';
2
+ export { createLiveView, type LiveView, type LiveViewFrame, type LiveViewOptions, type LiveViewSnapshot, type LiveViewStatus, type MutationMethod, } from './live-view.js';
3
+ export { DEFAULT_KEEPALIVE_MS, DEFAULT_LIVENESS_FACTOR, isRealtimeHeartbeat, openRealtimeStream, type RealtimeAction, type RealtimeChange, type RealtimeClock, type RealtimeEvent, type RealtimeHeartbeat, type RealtimeProcedure, type RealtimeStreamOptions, type RealtimeSyncHandle, } from './realtime.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { createClient, createRpcClient, OPERATION_ID_HEADER, } from './rpc-client.js';
2
+ export { createLiveView, } from './live-view.js';
3
+ export { DEFAULT_KEEPALIVE_MS, DEFAULT_LIVENESS_FACTOR, isRealtimeHeartbeat, openRealtimeStream, } from './realtime.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,GAUpB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,cAAc,GAOf,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,GASnB,MAAM,YAAY,CAAA","sourcesContent":["export {\n createClient,\n createRpcClient,\n OPERATION_ID_HEADER,\n type AnyBunderstackApp,\n type BunderstackClient,\n type CallOptions,\n type ClientOptions,\n type ClientCarrier,\n type DirectClient,\n type Fetch,\n type InferApiRouter,\n type RpcClient,\n} from './rpc-client'\nexport {\n createLiveView,\n type LiveView,\n type LiveViewFrame,\n type LiveViewOptions,\n type LiveViewSnapshot,\n type LiveViewStatus,\n type MutationMethod,\n} from './live-view'\nexport {\n DEFAULT_KEEPALIVE_MS,\n DEFAULT_LIVENESS_FACTOR,\n isRealtimeHeartbeat,\n openRealtimeStream,\n type RealtimeAction,\n type RealtimeChange,\n type RealtimeClock,\n type RealtimeEvent,\n type RealtimeHeartbeat,\n type RealtimeProcedure,\n type RealtimeStreamOptions,\n type RealtimeSyncHandle,\n} from './realtime'\n"]}
@@ -0,0 +1,49 @@
1
+ import type { CallOptions } from './rpc-client.js';
2
+ export type LiveViewFrame<T> = {
3
+ type: 'snapshot';
4
+ items: T[];
5
+ operationId?: string;
6
+ } | {
7
+ type: 'upsert';
8
+ record: T;
9
+ afterId?: string | null;
10
+ operationId?: string;
11
+ } | {
12
+ type: 'remove';
13
+ id: string;
14
+ operationId?: string;
15
+ } | {
16
+ type: 'heartbeat';
17
+ intervalMs?: number;
18
+ };
19
+ export type LiveViewStatus = 'connecting' | 'ready' | 'reconnecting' | 'error' | 'closed';
20
+ export type LiveViewSnapshot<T> = {
21
+ readonly items: readonly T[];
22
+ readonly status: LiveViewStatus;
23
+ readonly error: unknown;
24
+ };
25
+ export type LiveViewOptions<T> = {
26
+ subscribe: (options: {
27
+ signal: AbortSignal;
28
+ }) => AsyncIterable<LiveViewFrame<T>> | Promise<AsyncIterable<LiveViewFrame<T>>>;
29
+ getKey?: (record: T) => string;
30
+ createOperationId?: () => string;
31
+ retryMs?: number;
32
+ /** Reconnect after this multiple of the server heartbeat interval is silent. */
33
+ livenessFactor?: number;
34
+ /** Maximum wait after HTTP success for the matching live frame. */
35
+ ackTimeoutMs?: number;
36
+ };
37
+ export type MutationMethod<TArgs, TResult> = (args: TArgs, options: CallOptions & {
38
+ operationId: string;
39
+ }) => Promise<TResult>;
40
+ export type LiveView<T> = {
41
+ getSnapshot(): LiveViewSnapshot<T>;
42
+ subscribe(listener: () => void): () => void;
43
+ mutate<TArgs, TResult>(method: MutationMethod<TArgs, TResult>, args: TArgs, options?: Omit<CallOptions, 'operationId'>): Promise<TResult>;
44
+ resync(): void;
45
+ close(): void;
46
+ readonly done: Promise<void>;
47
+ };
48
+ export declare function createLiveView<T>(options: LiveViewOptions<T>): LiveView<T>;
49
+ //# sourceMappingURL=live-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-view.d.ts","sourceRoot":"","sources":["../../src/client/live-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,MAAM,MAAM,aAAa,CAAC,CAAC,IACvB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,CAAC,CAAA;IACT,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9C,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,OAAO,GACP,cAAc,GACd,OAAO,GACP,QAAQ,CAAA;AAEZ,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,SAAS,EAAE,CAAC,OAAO,EAAE;QACnB,MAAM,EAAE,WAAW,CAAA;KACpB,KACG,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAA;IAC9B,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,cAAc,CAAC,KAAK,EAAE,OAAO,IAAI,CAC3C,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,WAAW,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,KAC3C,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IACxB,WAAW,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAClC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,MAAM,CAAC,KAAK,EAAE,OAAO,EACnB,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,EACtC,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,GACzC,OAAO,CAAC,OAAO,CAAC,CAAA;IACnB,MAAM,IAAI,IAAI,CAAA;IACd,KAAK,IAAI,IAAI,CAAA;IACb,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B,CAAA;AAQD,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAqL1E"}
@@ -0,0 +1,184 @@
1
+ export function createLiveView(options) {
2
+ const controller = new AbortController();
3
+ const listeners = new Set();
4
+ const waiters = new Map();
5
+ const getKey = options.getKey ?? ((record) => String(record.id));
6
+ let current;
7
+ let forceReconnect = false;
8
+ let hasSnapshot = false;
9
+ let snapshot = {
10
+ items: [],
11
+ status: 'connecting',
12
+ error: undefined,
13
+ };
14
+ const publish = (next) => {
15
+ snapshot = next;
16
+ for (const listener of listeners)
17
+ listener();
18
+ };
19
+ const apply = (frame) => {
20
+ if (frame.type !== 'heartbeat') {
21
+ let items;
22
+ if (frame.type === 'snapshot') {
23
+ hasSnapshot = true;
24
+ items = [...frame.items];
25
+ }
26
+ else if (frame.type === 'remove') {
27
+ items = snapshot.items.filter((record) => getKey(record) !== String(frame.id));
28
+ }
29
+ else {
30
+ const key = getKey(frame.record);
31
+ const index = snapshot.items.findIndex((record) => getKey(record) === key);
32
+ const next = snapshot.items.filter((record) => getKey(record) !== key);
33
+ if (frame.afterId === undefined) {
34
+ if (index === -1)
35
+ next.push(frame.record);
36
+ else
37
+ next.splice(index, 0, frame.record);
38
+ }
39
+ else {
40
+ const anchor = frame.afterId === null
41
+ ? -1
42
+ : next.findIndex((record) => getKey(record) === String(frame.afterId));
43
+ const placement = frame.afterId !== null && anchor === -1 ? next.length : anchor + 1;
44
+ next.splice(placement, 0, frame.record);
45
+ }
46
+ items = next;
47
+ }
48
+ publish({ items, status: 'ready', error: undefined });
49
+ if (frame.type === 'snapshot') {
50
+ // A fresh snapshot is authoritative. Once the write request succeeded,
51
+ // it safely settles operations whose matching event was lost while the
52
+ // live connection was down.
53
+ for (const [operationId, waiter] of waiters) {
54
+ if (!waiter.httpSucceeded)
55
+ continue;
56
+ waiter.resolve();
57
+ waiters.delete(operationId);
58
+ }
59
+ }
60
+ }
61
+ if ('operationId' in frame && frame.operationId) {
62
+ waiters.get(frame.operationId)?.resolve();
63
+ waiters.delete(frame.operationId);
64
+ }
65
+ };
66
+ const done = (async () => {
67
+ let attempt = 0;
68
+ while (!controller.signal.aborted) {
69
+ current = new AbortController();
70
+ const signal = AbortSignal.any([controller.signal, current.signal]);
71
+ let heartbeatIntervalMs;
72
+ let livenessTimer;
73
+ const armLivenessTimer = () => {
74
+ if (livenessTimer)
75
+ clearTimeout(livenessTimer);
76
+ if (!heartbeatIntervalMs)
77
+ return;
78
+ livenessTimer = setTimeout(() => current?.abort(), heartbeatIntervalMs * (options.livenessFactor ?? 2.5));
79
+ };
80
+ try {
81
+ const frames = await options.subscribe({ signal });
82
+ for await (const frame of frames) {
83
+ if (signal.aborted)
84
+ break;
85
+ attempt = 0;
86
+ apply(frame);
87
+ if (frame.type === 'heartbeat' && frame.intervalMs) {
88
+ heartbeatIntervalMs = frame.intervalMs;
89
+ }
90
+ armLivenessTimer();
91
+ }
92
+ if (!signal.aborted)
93
+ throw new Error('Live view closed');
94
+ }
95
+ catch (error) {
96
+ if (controller.signal.aborted)
97
+ break;
98
+ publish({
99
+ ...snapshot,
100
+ status: hasSnapshot ? 'reconnecting' : 'error',
101
+ error: hasSnapshot ? undefined : error,
102
+ });
103
+ }
104
+ finally {
105
+ if (livenessTimer)
106
+ clearTimeout(livenessTimer);
107
+ }
108
+ if (!controller.signal.aborted) {
109
+ const delayMs = forceReconnect
110
+ ? 0
111
+ : Math.min(30_000, (options.retryMs ?? 1_000) * 2 ** attempt++);
112
+ forceReconnect = false;
113
+ if (delayMs > 0) {
114
+ await new Promise((resolve) => {
115
+ const timer = setTimeout(done, delayMs);
116
+ function done() {
117
+ clearTimeout(timer);
118
+ controller.signal.removeEventListener('abort', done);
119
+ resolve();
120
+ }
121
+ controller.signal.addEventListener('abort', done, { once: true });
122
+ });
123
+ }
124
+ }
125
+ }
126
+ if (controller.signal.aborted) {
127
+ publish({ ...snapshot, status: 'closed' });
128
+ }
129
+ })();
130
+ return {
131
+ getSnapshot: () => snapshot,
132
+ subscribe(listener) {
133
+ listeners.add(listener);
134
+ return () => listeners.delete(listener);
135
+ },
136
+ async mutate(method, args, callOptions = {}) {
137
+ const operationId = options.createOperationId?.() ?? crypto.randomUUID();
138
+ let waiter;
139
+ const acknowledged = new Promise((resolve, reject) => {
140
+ waiter = { httpSucceeded: false, resolve, reject };
141
+ });
142
+ // The HTTP request may fail before this promise is awaited. Attaching a
143
+ // handler now prevents that rejection from surfacing as unhandled.
144
+ void acknowledged.catch(() => { });
145
+ waiters.set(operationId, waiter);
146
+ let timer;
147
+ try {
148
+ const result = await method(args, { ...callOptions, operationId });
149
+ waiter.httpSucceeded = true;
150
+ const timedOut = new Promise((_resolve, reject) => {
151
+ timer = setTimeout(() => {
152
+ const error = new Error(`Operation ${operationId} acknowledgement timed out`);
153
+ waiters.delete(operationId);
154
+ reject(error);
155
+ }, options.ackTimeoutMs ?? 30_000);
156
+ });
157
+ await Promise.race([acknowledged, timedOut]);
158
+ return result;
159
+ }
160
+ catch (error) {
161
+ waiters.delete(operationId);
162
+ waiter.reject(error);
163
+ throw error;
164
+ }
165
+ finally {
166
+ if (timer)
167
+ clearTimeout(timer);
168
+ }
169
+ },
170
+ resync() {
171
+ forceReconnect = true;
172
+ current?.abort();
173
+ },
174
+ close() {
175
+ controller.abort();
176
+ const error = new DOMException('Live view closed', 'AbortError');
177
+ for (const waiter of waiters.values())
178
+ waiter.reject(error);
179
+ waiters.clear();
180
+ },
181
+ done,
182
+ };
183
+ }
184
+ //# sourceMappingURL=live-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-view.js","sourceRoot":"","sources":["../../src/client/live-view.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CallOptions } from './rpc-client'\n\nexport type LiveViewFrame<T> =\n | { type: 'snapshot'; items: T[]; operationId?: string }\n | {\n type: 'upsert'\n record: T\n afterId?: string | null\n operationId?: string\n }\n | { type: 'remove'; id: string; operationId?: string }\n | { type: 'heartbeat'; intervalMs?: number }\n\nexport type LiveViewStatus =\n | 'connecting'\n | 'ready'\n | 'reconnecting'\n | 'error'\n | 'closed'\n\nexport type LiveViewSnapshot<T> = {\n readonly items: readonly T[]\n readonly status: LiveViewStatus\n readonly error: unknown\n}\n\nexport type LiveViewOptions<T> = {\n subscribe: (options: {\n signal: AbortSignal\n }) =>\n | AsyncIterable<LiveViewFrame<T>>\n | Promise<AsyncIterable<LiveViewFrame<T>>>\n getKey?: (record: T) => string\n createOperationId?: () => string\n retryMs?: number\n /** Reconnect after this multiple of the server heartbeat interval is silent. */\n livenessFactor?: number\n /** Maximum wait after HTTP success for the matching live frame. */\n ackTimeoutMs?: number\n}\n\nexport type MutationMethod<TArgs, TResult> = (\n args: TArgs,\n options: CallOptions & { operationId: string },\n) => Promise<TResult>\n\nexport type LiveView<T> = {\n getSnapshot(): LiveViewSnapshot<T>\n subscribe(listener: () => void): () => void\n mutate<TArgs, TResult>(\n method: MutationMethod<TArgs, TResult>,\n args: TArgs,\n options?: Omit<CallOptions, 'operationId'>,\n ): Promise<TResult>\n resync(): void\n close(): void\n readonly done: Promise<void>\n}\n\ntype Waiter = {\n httpSucceeded: boolean\n resolve: () => void\n reject: (error: unknown) => void\n}\n\nexport function createLiveView<T>(options: LiveViewOptions<T>): LiveView<T> {\n const controller = new AbortController()\n const listeners = new Set<() => void>()\n const waiters = new Map<string, Waiter>()\n const getKey =\n options.getKey ?? ((record: T) => String((record as { id: unknown }).id))\n let current: AbortController | undefined\n let forceReconnect = false\n let hasSnapshot = false\n let snapshot: LiveViewSnapshot<T> = {\n items: [],\n status: 'connecting',\n error: undefined,\n }\n\n const publish = (next: LiveViewSnapshot<T>) => {\n snapshot = next\n for (const listener of listeners) listener()\n }\n\n const apply = (frame: LiveViewFrame<T>) => {\n if (frame.type !== 'heartbeat') {\n let items: readonly T[]\n if (frame.type === 'snapshot') {\n hasSnapshot = true\n items = [...frame.items]\n } else if (frame.type === 'remove') {\n items = snapshot.items.filter(\n (record) => getKey(record) !== String(frame.id),\n )\n } else {\n const key = getKey(frame.record)\n const index = snapshot.items.findIndex(\n (record) => getKey(record) === key,\n )\n const next = snapshot.items.filter((record) => getKey(record) !== key)\n if (frame.afterId === undefined) {\n if (index === -1) next.push(frame.record)\n else next.splice(index, 0, frame.record)\n } else {\n const anchor =\n frame.afterId === null\n ? -1\n : next.findIndex(\n (record) => getKey(record) === String(frame.afterId),\n )\n const placement =\n frame.afterId !== null && anchor === -1 ? next.length : anchor + 1\n next.splice(placement, 0, frame.record)\n }\n items = next\n }\n publish({ items, status: 'ready', error: undefined })\n if (frame.type === 'snapshot') {\n // A fresh snapshot is authoritative. Once the write request succeeded,\n // it safely settles operations whose matching event was lost while the\n // live connection was down.\n for (const [operationId, waiter] of waiters) {\n if (!waiter.httpSucceeded) continue\n waiter.resolve()\n waiters.delete(operationId)\n }\n }\n }\n if ('operationId' in frame && frame.operationId) {\n waiters.get(frame.operationId)?.resolve()\n waiters.delete(frame.operationId)\n }\n }\n\n const done = (async () => {\n let attempt = 0\n while (!controller.signal.aborted) {\n current = new AbortController()\n const signal = AbortSignal.any([controller.signal, current.signal])\n let heartbeatIntervalMs: number | undefined\n let livenessTimer: ReturnType<typeof setTimeout> | undefined\n const armLivenessTimer = () => {\n if (livenessTimer) clearTimeout(livenessTimer)\n if (!heartbeatIntervalMs) return\n livenessTimer = setTimeout(\n () => current?.abort(),\n heartbeatIntervalMs * (options.livenessFactor ?? 2.5),\n )\n }\n try {\n const frames = await options.subscribe({ signal })\n for await (const frame of frames) {\n if (signal.aborted) break\n attempt = 0\n apply(frame)\n if (frame.type === 'heartbeat' && frame.intervalMs) {\n heartbeatIntervalMs = frame.intervalMs\n }\n armLivenessTimer()\n }\n if (!signal.aborted) throw new Error('Live view closed')\n } catch (error) {\n if (controller.signal.aborted) break\n publish({\n ...snapshot,\n status: hasSnapshot ? 'reconnecting' : 'error',\n error: hasSnapshot ? undefined : error,\n })\n } finally {\n if (livenessTimer) clearTimeout(livenessTimer)\n }\n if (!controller.signal.aborted) {\n const delayMs = forceReconnect\n ? 0\n : Math.min(30_000, (options.retryMs ?? 1_000) * 2 ** attempt++)\n forceReconnect = false\n if (delayMs > 0) {\n await new Promise<void>((resolve) => {\n const timer = setTimeout(done, delayMs)\n function done() {\n clearTimeout(timer)\n controller.signal.removeEventListener('abort', done)\n resolve()\n }\n controller.signal.addEventListener('abort', done, { once: true })\n })\n }\n }\n }\n if (controller.signal.aborted) {\n publish({ ...snapshot, status: 'closed' })\n }\n })()\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async mutate(method, args, callOptions = {}) {\n const operationId = options.createOperationId?.() ?? crypto.randomUUID()\n let waiter!: Waiter\n const acknowledged = new Promise<void>((resolve, reject) => {\n waiter = { httpSucceeded: false, resolve, reject }\n })\n // The HTTP request may fail before this promise is awaited. Attaching a\n // handler now prevents that rejection from surfacing as unhandled.\n void acknowledged.catch(() => {})\n waiters.set(operationId, waiter)\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n const result = await method(args, { ...callOptions, operationId })\n waiter.httpSucceeded = true\n const timedOut = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n const error = new Error(\n `Operation ${operationId} acknowledgement timed out`,\n )\n waiters.delete(operationId)\n reject(error)\n }, options.ackTimeoutMs ?? 30_000)\n })\n await Promise.race([acknowledged, timedOut])\n return result\n } catch (error) {\n waiters.delete(operationId)\n waiter.reject(error)\n throw error\n } finally {\n if (timer) clearTimeout(timer)\n }\n },\n resync() {\n forceReconnect = true\n current?.abort()\n },\n close() {\n controller.abort()\n const error = new DOMException('Live view closed', 'AbortError')\n for (const waiter of waiters.values()) waiter.reject(error)\n waiters.clear()\n },\n done,\n }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { LiveView, LiveViewSnapshot } from './live-view.js';
2
+ /** React owns scheduling; the shared LiveView remains an external store. */
3
+ export declare function useLiveView<T>(view: LiveView<T>): LiveViewSnapshot<T>;
4
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/client/react.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE7D,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAMrE"}
@@ -0,0 +1,6 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ /** React owns scheduling; the shared LiveView remains an external store. */
3
+ export function useLiveView(view) {
4
+ return useSyncExternalStore(view.subscribe, view.getSnapshot, view.getSnapshot);
5
+ }
6
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/client/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAI5C,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAI,IAAiB;IAC9C,OAAO,oBAAoB,CACzB,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,WAAW,CACjB,CAAA;AACH,CAAC","sourcesContent":["import { useSyncExternalStore } from 'react'\n\nimport type { LiveView, LiveViewSnapshot } from './live-view'\n\n/** React owns scheduling; the shared LiveView remains an external store. */\nexport function useLiveView<T>(view: LiveView<T>): LiveViewSnapshot<T> {\n return useSyncExternalStore(\n view.subscribe,\n view.getSnapshot,\n view.getSnapshot,\n )\n}\n"]}
@@ -0,0 +1,54 @@
1
+ export type RealtimeAction = 'create' | 'update' | 'delete';
2
+ export type RealtimeChange = {
3
+ table: string;
4
+ action: RealtimeAction;
5
+ record: Record<string, unknown>;
6
+ operationId?: string;
7
+ };
8
+ export type RealtimeHeartbeat = {
9
+ type: 'heartbeat';
10
+ intervalMs?: number;
11
+ };
12
+ export type RealtimeEvent = RealtimeChange | RealtimeHeartbeat;
13
+ export type RealtimeProcedure = {
14
+ call(input: {
15
+ tables: string[];
16
+ }, options?: {
17
+ signal?: AbortSignal;
18
+ lastEventId?: string;
19
+ }): Promise<AsyncIterable<RealtimeEvent>>;
20
+ };
21
+ export type RealtimeSyncHandle = {
22
+ close(): void;
23
+ done: Promise<void>;
24
+ };
25
+ export type RealtimeClock = {
26
+ setTimeout(fn: () => void, ms: number): unknown;
27
+ clearTimeout(handle: unknown): void;
28
+ };
29
+ export declare const DEFAULT_KEEPALIVE_MS = 5000;
30
+ export declare const DEFAULT_LIVENESS_FACTOR = 2.5;
31
+ export type RealtimeStreamOptions = {
32
+ subscribe: (options: {
33
+ signal: AbortSignal;
34
+ lastEventId?: string;
35
+ }) => Promise<AsyncIterable<RealtimeEvent>>;
36
+ onChange: (change: RealtimeChange) => void;
37
+ onReconnect: () => void | Promise<void>;
38
+ onError?: (error: unknown) => void;
39
+ onRetry?: (retry: {
40
+ attempt: number;
41
+ delayMs: number;
42
+ }) => void;
43
+ signal: AbortSignal;
44
+ retryMs?: number;
45
+ maxRetryMs?: number;
46
+ livenessFactor?: number;
47
+ defaultKeepaliveMs?: number;
48
+ clock?: RealtimeClock;
49
+ random?: () => number;
50
+ };
51
+ export declare function isRealtimeHeartbeat(event: RealtimeEvent): event is RealtimeHeartbeat;
52
+ /** Shared reconnecting transport used by LiveView, Query, and DB adapters. */
53
+ export declare function openRealtimeStream(options: RealtimeStreamOptions): RealtimeSyncHandle;
54
+ //# sourceMappingURL=realtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/client/realtime.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC3D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AACD,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC1E,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,iBAAiB,CAAA;AAC9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CACF,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAC3B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAA;CACzC,CAAA;AACD,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AACvE,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/C,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CACpC,CAAA;AACD,eAAO,MAAM,oBAAoB,OAAQ,CAAA;AACzC,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAQ1C,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,CAAC,OAAO,EAAE;QACnB,MAAM,EAAE,WAAW,CAAA;QACnB,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,KAAK,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAA;IAC3C,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC1C,WAAW,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IAC/D,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,GACnB,KAAK,IAAI,iBAAiB,CAE5B;AAID,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAC7B,kBAAkB,CAqFpB"}