@auersperg/flare 0.3.9 → 0.3.10

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 (34) hide show
  1. package/README.md +3 -1
  2. package/dist/cli/{chunk-5s6zv1h9.js → chunk-jrptpjmm.js} +1 -1
  3. package/dist/cli/{chunk-0s0vrjtw.js → chunk-m0698kap.js} +9 -6
  4. package/dist/cli/{chunk-5yg1zj5n.js → chunk-znktbexz.js} +6 -6
  5. package/dist/cli/{chunk-vx7kmczm.js → chunk-zt96ghm7.js} +12 -6
  6. package/dist/cli/flare.js +5 -5
  7. package/dist/platform/admin.js +31 -23
  8. package/dist/platform/dashboard/_astro/{auth-view.EASEF9BN.js → auth-view.BhUFPmNv.js} +1 -1
  9. package/dist/platform/dashboard/_astro/{cli-approval.DwZVHdyo.js → cli-approval.emPTVRqn.js} +1 -1
  10. package/dist/platform/dashboard/_astro/client.4z54xJ_z.js +1 -0
  11. package/dist/platform/dashboard/_astro/{common.TAClb6G9.js → common.9pujiaSZ.js} +1 -1
  12. package/dist/platform/dashboard/_astro/{data-view.BaHEU4Xa.js → data-view.C-VU8Oxm.js} +1 -1
  13. package/dist/platform/dashboard/_astro/{functions-view.BGOYuDwu.js → functions-view.3yLrtS0h.js} +1 -1
  14. package/dist/platform/dashboard/_astro/{index.astro_astro_type_script_index_0_lang.Blg5hU9g.js → index.astro_astro_type_script_index_0_lang.Dsfj1oeZ.js} +2 -2
  15. package/dist/platform/dashboard/_astro/{input-group.DUp-h0xx.js → input-group.BZ1TZT7L.js} +1 -1
  16. package/dist/platform/dashboard/_astro/{sdk-view.2ErPn4cO.js → sdk-view.BAiQKNm8.js} +1 -1
  17. package/dist/platform/dashboard/_astro/{services-view.iZujitxl.js → services-view.BSrqpyr5.js} +1 -1
  18. package/dist/platform/dashboard/_astro/{settings-view.CqK-mrlB.js → settings-view.C_eYjlIE.js} +1 -1
  19. package/dist/platform/dashboard/bootstrap/facet.js +64 -46
  20. package/dist/platform/dashboard/bootstrap/openapi.json +1 -1
  21. package/dist/platform/dashboard/bootstrap/release.json +2 -2
  22. package/dist/platform/dashboard/index.html +1 -1
  23. package/dist/platform/manifest.json +36 -36
  24. package/dist/platform/runtime.js +99 -66
  25. package/dist/sdk/backup.js +4 -4
  26. package/dist/sdk/client.js +22 -41
  27. package/dist/sdk/cloudflare.js +10 -11
  28. package/dist/sdk/database.js +45 -27
  29. package/dist/sdk/http.d.ts +2 -0
  30. package/dist/sdk/http.js +27 -0
  31. package/dist/sdk/schema.d.ts +2 -2
  32. package/dist/sdk/schema.js +7 -6
  33. package/package.json +2 -2
  34. package/dist/platform/dashboard/_astro/client.BUUZgEFj.js +0 -1
@@ -1,6 +1,6 @@
1
1
  import { FlareError, boundedJson, stableJson } from "./protocol.js";
2
2
  import {
3
- documentValue,
3
+ documentJson,
4
4
  fieldSql,
5
5
  quote
6
6
  } from "./schema.js";
@@ -64,7 +64,7 @@ export function database(sql, schema, writable, touched = new Set, changed = new
64
64
  function query(name) {
65
65
  const definition = table(name);
66
66
  function make(index, filters, direction) {
67
- const page = (options) => task(async () => {
67
+ const page = (options, withCursor = true) => task(async () => {
68
68
  table(name);
69
69
  size(options.limit);
70
70
  const fields = [
@@ -73,9 +73,18 @@ export function database(sql, schema, writable, touched = new Set, changed = new
73
73
  "_id"
74
74
  ];
75
75
  const expressions = fields.map(fieldSql);
76
- const signature = stableJson({ name, index, filters, direction });
76
+ const signature = withCursor ? stableJson({ name, index, filters, direction }) : "";
77
+ const fixed = filters.filter((f) => f.operator === "=");
77
78
  const where = filters.map((f) => `${fieldSql(f.field)} ${f.operator === "=" ? "IS" : f.operator} ?`);
78
79
  const bindings = filters.map((f) => bind(f.value));
80
+ const rowLimit = options.limit + (withCursor ? 1 : 0);
81
+ const rows = [];
82
+ const read = (terms = [], values = [], orderStart = fixed.length) => {
83
+ if (rows.length >= rowLimit)
84
+ return;
85
+ const conditions = [...terms, ...where];
86
+ rows.push(...sql.exec(`SELECT *${withCursor ? "," + expressions.map((e, i) => `${e} AS __k${i}`).join(",") : ""} FROM ${quote(name)} ${conditions.length ? "WHERE " + conditions.join(" AND ") : ""} ORDER BY ${expressions.slice(orderStart).map((e) => `${e} ${direction}`).join(",")} LIMIT ?`, ...values, ...bindings, rowLimit - rows.length).toArray());
87
+ };
79
88
  if (options.cursor != null) {
80
89
  let token;
81
90
  try {
@@ -85,23 +94,32 @@ export function database(sql, schema, writable, touched = new Set, changed = new
85
94
  } catch {
86
95
  throw new FlareError("INVALID_CURSOR", "Invalid cursor");
87
96
  }
88
- if (token.signature !== signature || !Array.isArray(token.values) || token.values.length !== fields.length || !token.values.every(scalar))
97
+ if (!token || token.signature !== signature || !Array.isArray(token.values) || token.values.length !== fields.length || !token.values.every(scalar) || typeof token.values.at(-2) !== "number" || typeof token.values.at(-1) !== "string")
89
98
  throw new FlareError("INVALID_CURSOR", "Cursor belongs to a different query");
90
- const terms = expressions.map((expr, i) => {
91
- const prefix = expressions.slice(0, i).map((e) => `${e} IS ?`);
92
- bindings.push(...token.values.slice(0, i).map(bind));
93
- const value = token.values[i];
94
- if (value === null)
95
- prefix.push(direction === "asc" ? `${expr} IS NOT NULL` : "0");
96
- else {
97
- prefix.push(direction === "asc" ? `${expr} > ?` : `(${expr} < ? OR ${expr} IS NULL)`);
98
- bindings.push(bind(value));
99
+ if (fixed.some((f, i) => bind(f.value) !== bind(token.values[i])))
100
+ throw new FlareError("INVALID_CURSOR", "Cursor violates index range");
101
+ const first = fixed.length;
102
+ for (let i = expressions.length - 2;i >= first && rows.length < rowLimit; i--) {
103
+ const prefix = expressions.slice(first, i).map((e) => `${e} IS ?`);
104
+ const values = token.values.slice(first, i).map(bind);
105
+ const expr = expressions[i], value = token.values[i];
106
+ if (i === expressions.length - 2) {
107
+ read([
108
+ ...prefix,
109
+ `(${expressions.slice(i).join(",")}) ${direction === "asc" ? ">" : "<"} (?,?)`
110
+ ], [...values, ...token.values.slice(i).map(bind)], i);
111
+ } else if (direction === "asc") {
112
+ read([
113
+ ...prefix,
114
+ value === null ? `${expr} IS NOT NULL` : `${expr} > ?`
115
+ ], value === null ? values : [...values, bind(value)], i);
116
+ } else if (value !== null) {
117
+ read([...prefix, `${expr} < ?`], [...values, bind(value)], i);
118
+ read([...prefix, `${expr} IS NULL`], values, i + 1);
99
119
  }
100
- return "(" + prefix.join(" AND ") + ")";
101
- });
102
- where.push("(" + terms.join(" OR ") + ")");
103
- }
104
- const rows = sql.exec(`SELECT *,${expressions.map((e, i) => `${e} AS __k${i}`).join(",")} FROM ${quote(name)} ${where.length ? "WHERE " + where.join(" AND ") : ""} ORDER BY ${expressions.map((e) => `${e} ${direction}`).join(",")} LIMIT ?`, ...bindings, options.limit + 1).toArray();
120
+ }
121
+ } else
122
+ read();
105
123
  let more = rows.length > options.limit;
106
124
  if (more)
107
125
  rows.pop();
@@ -117,7 +135,7 @@ export function database(sql, schema, writable, touched = new Set, changed = new
117
135
  bytes += length;
118
136
  }
119
137
  const last = rows[docs.length - 1];
120
- const continueCursor = more && last ? btoa(String.fromCharCode(...new TextEncoder().encode(boundedJson({
138
+ const continueCursor = withCursor && more && last ? btoa(String.fromCharCode(...new TextEncoder().encode(boundedJson({
121
139
  signature,
122
140
  values: expressions.map((_, i) => last[`__k${i}`])
123
141
  }, 6144)))) : null;
@@ -174,14 +192,14 @@ export function database(sql, schema, writable, touched = new Set, changed = new
174
192
  paginate: page,
175
193
  take(limit) {
176
194
  return task(async () => {
177
- const result = await page({ limit });
195
+ const result = await page({ limit }, false);
178
196
  if (!result.isDone && result.page.length < limit)
179
197
  throw new FlareError("TOO_LARGE", "Use paginate for large documents");
180
198
  return result.page;
181
199
  }, false);
182
200
  },
183
201
  first() {
184
- return task(async () => (await page({ limit: 1 })).page[0] ?? null, false);
202
+ return task(async () => (await page({ limit: 1 }, false)).page[0] ?? null, false);
185
203
  }
186
204
  });
187
205
  }
@@ -192,10 +210,10 @@ export function database(sql, schema, writable, touched = new Set, changed = new
192
210
  insert(name, input) {
193
211
  return task(async () => {
194
212
  writing();
195
- const definition = table(name), value = await documentValue(definition, input);
213
+ const definition = table(name), value = await documentJson(definition, input);
196
214
  writing();
197
215
  const id = crypto.randomUUID();
198
- sql.exec(`INSERT INTO ${quote(name)} VALUES (?,?,?)`, id, Date.now(), stableJson(value));
216
+ sql.exec(`INSERT INTO ${quote(name)} VALUES (?,?,?)`, id, Date.now(), value);
199
217
  changed.add(name);
200
218
  return id;
201
219
  });
@@ -211,12 +229,12 @@ export function database(sql, schema, writable, touched = new Set, changed = new
211
229
  const row = sql.exec(`SELECT _data FROM ${quote(name)} WHERE _id=?`, id).toArray()[0];
212
230
  if (!row)
213
231
  throw new FlareError("NOT_FOUND", "Document not found");
214
- const value = await documentValue(definition, {
232
+ const value = await documentJson(definition, {
215
233
  ...JSON.parse(String(row._data)),
216
234
  ...patch
217
235
  });
218
236
  writing();
219
- sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, stableJson(value), id);
237
+ sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, value, id);
220
238
  changed.add(name);
221
239
  });
222
240
  },
@@ -225,9 +243,9 @@ export function database(sql, schema, writable, touched = new Set, changed = new
225
243
  writing();
226
244
  const definition = table(name);
227
245
  checkedId(id);
228
- const value = await documentValue(definition, input);
246
+ const value = await documentJson(definition, input);
229
247
  writing();
230
- const rows = sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=? RETURNING _id`, stableJson(value), id).toArray();
248
+ const rows = sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=? RETURNING _id`, value, id).toArray();
231
249
  if (!rows.length)
232
250
  throw new FlareError("NOT_FOUND", "Document not found");
233
251
  changed.add(name);
@@ -0,0 +1,2 @@
1
+ /** Internal bounded transport reader shared by the SDK and platform. */
2
+ export declare function readBody(body: ReadableStream<Uint8Array>, limit: number, tooLarge: () => never): Promise<Uint8Array>;
@@ -0,0 +1,27 @@
1
+ export async function readBody(body, limit, tooLarge) {
2
+ const reader = body.getReader();
3
+ const chunks = [];
4
+ let size = 0;
5
+ try {
6
+ for (;; ) {
7
+ const { done, value } = await reader.read();
8
+ if (done)
9
+ break;
10
+ size += value.byteLength;
11
+ if (size > limit)
12
+ tooLarge();
13
+ chunks.push(value);
14
+ }
15
+ } finally {
16
+ await reader.cancel().catch(() => {});
17
+ }
18
+ if (chunks.length === 1)
19
+ return chunks[0];
20
+ const bytes = new Uint8Array(size);
21
+ let offset = 0;
22
+ for (const chunk of chunks) {
23
+ bytes.set(chunk, offset);
24
+ offset += chunk.byteLength;
25
+ }
26
+ return bytes;
27
+ }
@@ -1,5 +1,4 @@
1
1
  import type { StandardSchemaV1, StandardJSONSchemaV1 } from "@standard-schema/spec";
2
- import { type Json } from "./protocol.js";
3
2
  export type SqlValue = ArrayBuffer | string | number | null;
4
3
  /** The small structural portion of native storage needed by the adapter; no Worker globals in public types. */
5
4
  export interface NativeSql {
@@ -39,5 +38,6 @@ export declare function defineTable<V extends Validator>(validator: V): Table<V,
39
38
  export declare function schemaContract(schema: Schema): SchemaContract;
40
39
  export declare function assertSchemaContract(contract: SchemaContract): void;
41
40
  export declare function defineSchema<const S extends Schema>(schema: S): S;
42
- export declare function documentValue(table: TableDefinition, input: unknown): Promise<Record<string, Json>>;
41
+ /** Validate once and return the canonical JSON written by all storage paths. */
42
+ export declare function documentJson(table: TableDefinition, input: unknown): Promise<string>;
43
43
  export declare function applySchema(storage: NativeStorage, schema: Schema): Promise<void>;
@@ -92,7 +92,7 @@ function documentSchema(table) {
92
92
  }
93
93
  return doc;
94
94
  }
95
- export async function documentValue(table, input) {
95
+ export async function documentJson(table, input) {
96
96
  boundedJson(input, 65536);
97
97
  const properties = documentSchema(table).properties ?? {};
98
98
  if (!input || Array.isArray(input) || typeof input !== "object" || Object.keys(input).some((k) => !Object.hasOwn(properties, k)))
@@ -100,7 +100,7 @@ export async function documentValue(table, input) {
100
100
  const result = await table.validator["~standard"].validate(input);
101
101
  if (result.issues || !result.value || Array.isArray(result.value) || typeof result.value !== "object" || Object.keys(result.value).some((k) => !Object.hasOwn(properties, k)))
102
102
  throw new FlareError("INVALID_INPUT", "Document failed schema validation");
103
- return JSON.parse(boundedJson(result.value, 65536));
103
+ return boundedJson(result.value, 65536);
104
104
  }
105
105
  export async function applySchema(storage, schema) {
106
106
  const contract = schemaContract(schema), encoded = stableJson(contract);
@@ -118,13 +118,14 @@ export async function applySchema(storage, schema) {
118
118
  }
119
119
  for (const [name, definition] of Object.entries(contract)) {
120
120
  storage.sql.exec(`CREATE TABLE IF NOT EXISTS ${quote(name)} (_id TEXT PRIMARY KEY, _creationTime REAL NOT NULL, _data TEXT NOT NULL CHECK(json_valid(_data)))`);
121
+ const changed = !old[name] || stableJson(old[name].document) !== stableJson(definition.document);
121
122
  let cursor = "";
122
- while (!old[name] || stableJson(old[name].document) !== stableJson(definition.document)) {
123
+ while (changed) {
123
124
  const rows = storage.sql.exec(`SELECT _id,_data FROM ${quote(name)} WHERE _id > ? ORDER BY _id LIMIT 100`, cursor).toArray();
124
125
  for (const row of rows) {
125
- const value = await documentValue(schema[name], JSON.parse(String(row._data)));
126
- if (stableJson(value) !== stableJson(JSON.parse(String(row._data))))
127
- storage.sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, stableJson(value), row._id);
126
+ const value = await documentJson(schema[name], JSON.parse(String(row._data)));
127
+ if (value !== stableJson(JSON.parse(String(row._data))))
128
+ storage.sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, value, row._id);
128
129
  }
129
130
  if (rows.length < 100)
130
131
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auersperg/flare",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Flare client, server declarations and development CLI",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@standard-schema/spec": "1.1.0",
39
- "zod": "4.6.5",
39
+ "zod": "4.6.6",
40
40
  "json-schema-to-typescript": "16.0.0"
41
41
  },
42
42
  "engines": {
@@ -1 +0,0 @@
1
- var e=Object.freeze({inputBytes:65536,outputBytes:262144,frameBytes:8192,subscriptions:64,fileBytes:10485760}),t=class extends Error{code;ambiguous;constructor(e,t,n=!1){super(t),this.code=e,this.ambiguous=n,this.name=`FlareError`}};function n(e){if(e.length>256||!/^[A-Za-z][A-Za-z0-9_]*(\/[A-Za-z][A-Za-z0-9_]*)*$/.test(e)||e.split(`/`).some(e=>[`__proto__`,`prototype`,`constructor`].includes(e)))throw new t(`INVALID_PATH`,`Invalid function path`)}function r(e,t){return n(e),Object.freeze({path:e,kind:t})}function i(e){let n=new Set;function r(e,i){if(i>40)throw new t(`INVALID_JSON`,`JSON nesting limit exceeded`);if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||!e||n.has(e))throw new t(`INVALID_JSON`,`Expected lossless JSON`);if(!Array.isArray(e)&&Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)throw new t(`INVALID_JSON`,`Expected plain JSON object`);if(Object.getOwnPropertySymbols(e).length)throw new t(`INVALID_JSON`,`Symbol keys are unsupported`);n.add(e);let a;if(Array.isArray(e))a=Array.from(e,e=>r(e,i+1));else{let n=Object.create(null);for(let a of Object.keys(e).sort()){let o=Object.getOwnPropertyDescriptor(e,a);if(!(`value`in o))throw new t(`INVALID_JSON`,`Getters are unsupported`);n[a]=r(o.value,i+1)}a=n}return n.delete(e),a}return JSON.stringify(r(e,0))}function a(e,n){let r=i(e);if(new TextEncoder().encode(r).byteLength>n)throw new t(`TOO_LARGE`,`JSON payload exceeds limit`);return r}function o(n){if(n.size>e.fileBytes)throw new t(`TOO_LARGE`,`Maximum file size: 10 MiB`);if(!n.name||n.name.length>120||/[\x00-\x1f\x7f/\\]/.test(n.name))throw new t(`INVALID_INPUT`,`File name must be 1–120 characters, without path separators`);return{name:n.name,size:n.size,type:[`text/plain`,`image/png`,`image/jpeg`,`image/webp`,`application/pdf`].includes(n.type)?n.type:`application/octet-stream`}}async function s(n,r,i={}){if(r.size>e.fileBytes)throw new t(`TOO_LARGE`,`Maximum file size: 10 MiB`);let a=new URL(n.url);if(a.username||a.password||a.protocol!==`https:`&&!(a.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(a.hostname)))throw new t(`INVALID_URL`,`Expected secure upload URL`);let o;try{o=await(i.fetch??globalThis.fetch)(a.href,{method:`POST`,body:r,redirect:`error`,credentials:`omit`,signal:i.signal??AbortSignal.timeout(i.timeoutMs??12e4)})}catch{throw new t(`UPLOAD_UNKNOWN`,`Connection interrupted or upload timed out; refresh the file list before trying again`,!0)}let s=await o.json().catch(()=>null);if(!o.ok){let e=s&&typeof s==`object`&&`error`in s&&typeof s.error==`string`?s.error:`HTTP_${o.status}`;throw new t(e,`Upload failed: ${e}`,o.status>=500)}if(!s||typeof s!=`object`||!(`id`in s)||typeof s.id!=`string`)throw new t(`INVALID_RESPONSE`,`Upload outcome is unknown; refresh the file list before uploading again`,!0);return{id:s.id}}var c=new Set([`UNAUTHORIZED`,`AUTH_EXPIRED`,`FORBIDDEN`,`AUTH_DISABLED`,`HTTP_401`,`HTTP_403`]);function l(r){let o=new URL(r.url);if(!/^https?:$/.test(o.protocol)||o.username||o.password||o.search||o.hash)throw new t(`INVALID_URL`,`Expected backend HTTP URL`);if(o.protocol===`http:`&&![`localhost`,`127.0.0.1`,`[::1]`].includes(o.hostname))throw new t(`INVALID_URL`,`Remote backends require HTTPS`);o.pathname=o.pathname.replace(/\/$/,``);let s=r.fetch??globalThis.fetch;if(!s)throw new t(`TRANSPORT_UNAVAILABLE`,`Provide a fetch implementation`);let l=new Map,u=!1,d,f=!1,p=0,m=0,h=0,g,_=new Set,v=e=>`${o.href.replace(/\/$/,``)}${e}`,y=(e,t,n)=>{for(let r of e.observers)try{r[t]?.(t===`next`?JSON.parse(i(n)):n)}catch{}};function b(){if(u)throw new t(`DISPOSED`,`Client is disposed`)}async function x(n,i,o){b();let c=a(i,e.inputBytes),l;try{l=await r.getToken?.()??null}catch{throw new t(`AUTH_UNAVAILABLE`,`Cannot obtain access token`)}b();let u=new AbortController,d=setTimeout(()=>u.abort(),r.requestTimeoutMs??3e4);try{let r=await s(v(n),{method:`POST`,headers:{"Content-Type":`application/json`,...l?{Authorization:`Bearer ${l}`}:{}},body:c,signal:u.signal,redirect:`error`});if(!r.body)throw new t(`INVALID_RESPONSE`,`Missing response body`,o);let i=r.body.getReader(),d=0,f=[];try{for(;;){let{done:n,value:r}=await i.read();if(n)break;if(d+=r.byteLength,d>e.outputBytes)throw new t(`INVALID_RESPONSE`,`Response exceeds limit`,o);f.push(r)}}finally{await i.cancel().catch(()=>{})}let p=new Uint8Array(d),m=0;for(let e of f)p.set(e,m),m+=e.byteLength;let h;try{h=JSON.parse(new TextDecoder(`utf-8`,{fatal:!0}).decode(p))}catch(e){throw r.ok?e:new t(`HTTP_${r.status}`,`Backend request failed`,o&&r.status>=500)}if(!r.ok)throw new t(h&&typeof h==`object`&&`error`in h&&typeof h.error==`string`&&/^[A-Z][A-Z0-9_]{0,63}$/.test(h.error)?h.error:`HTTP_${r.status}`,`Backend request failed`,o&&r.status>=500);if(!h||typeof h!=`object`||!(`result`in h))throw new t(`INVALID_RESPONSE`,`Invalid protocol envelope`,o);return a(h.result,e.outputBytes),h.result}catch(e){throw e instanceof t?e:e instanceof SyntaxError?new t(`INVALID_RESPONSE`,`Invalid JSON response`,o):new t(`TRANSPORT_ERROR`,o?`Write outcome is unknown; do not replay automatically`:`Backend request interrupted`,o)}finally{clearTimeout(d)}}function S(t){d?.send(a(t,e.frameBytes))}function C(e){S({type:`subscribe`,id:e.id,path:e.ref.path,args:e.args})}function w(){if(u||!l.size||g)return;let e=Math.min(3e4,250*2**Math.min(p++,7));g=setTimeout(()=>{g=void 0,T()},e)}async function T(){if(u||f||d||!l.size)return;f=!0;let n=++m;try{let o=await x(`/v1/live/ticket`,{},!1);if(u||n!==m||!l.size)return;if(!o||typeof o.ticket!=`string`||o.ticket.length>1024)throw new t(`INVALID_RESPONSE`,`Invalid live ticket`);let s=new URL(v(`/v1/live`));s.protocol=s.protocol===`https:`?`wss:`:`ws:`,s.searchParams.set(`ticket`,o.ticket);let f=r.createWebSocket?r.createWebSocket(s.href):new globalThis.WebSocket(s.href);d=f;let h=()=>{if(!(d!==f||u)){p=0;for(let e of l.values())e.revision=-1,e.hasValue=!1,e.terminal||C(e)}},g=n=>{if(!(d!==f||u))try{if(typeof n.data!=`string`||new TextEncoder().encode(n.data).byteLength>e.outputBytes+1024)throw Error(`Invalid frame`);let r=JSON.parse(n.data);if(r.type===`ready`)return;let o=[...l.values()].find(e=>e.id===r.id);if(!o||o.terminal)return;if(r.type===`error`){o.terminal=!0,c.has(r.code)&&(o.hasValue=!1,o.value=void 0),y(o,`error`,new t(typeof r.code==`string`?r.code:`LIVE_ERROR`,`Live subscription failed`));return}if(r.type!==`snapshot`||!Number.isSafeInteger(r.revision)||r.revision<0||!(`result`in r))throw Error(`Invalid snapshot`);let s=a(r.result,e.outputBytes);if(r.revision>o.revision){let e=!o.hasValue||i(o.value)!==s;o.revision=r.revision,o.value=r.result,o.hasValue=!0,e&&y(o,`next`,o.value)}S({type:`ack`,id:o.id,revision:o.revision})}catch{f.close(1002,`Invalid protocol frame`)}},b=e=>{if(E(),d===f){d=void 0;for(let n of l.values())e.code===1008&&(n.hasValue=!1,n.value=void 0,n.revision=-1),n.terminal||y(n,`error`,new t(e.code===1008?`AUTH_EXPIRED`:`LIVE_DISCONNECTED`,`Live reconnecting; next snapshot will be current`));w()}},T=()=>f.close(),E=()=>{f.removeEventListener(`open`,h),f.removeEventListener(`message`,g),f.removeEventListener(`close`,b),f.removeEventListener(`error`,T),_.delete(E)};_.add(E),f.addEventListener(`open`,h),f.addEventListener(`message`,g),f.addEventListener(`close`,b),f.addEventListener(`error`,T)}catch(e){if(u||n!==m)return;for(let n of l.values())e instanceof t&&c.has(e.code)&&(n.hasValue=!1,n.value=void 0,n.revision=-1),y(n,`error`,e instanceof t?e:new t(`LIVE_UNAVAILABLE`,`Live connection failed`));w()}finally{f=!1,!u&&n!==m&&!d&&l.size&&w()}}function E(e){return async(r,i)=>{if(n(r.path),r.kind!==e)throw new t(`INVALID_KIND`,`Expected a ${e} function`);return x(`/v1/call`,{path:r.path,kind:r.kind,args:i},r.kind===`mutation`||r.kind===`action`)}}return{query:E(`query`),mutation:E(`mutation`),action:E(`action`),onUpdate(r,o,s,c){let u={next:s,error:c};if(b(),n(r.path),r.kind!==`query`)throw new t(`INVALID_KIND`,`Only queries are subscribable`);a({type:`subscribe`,id:`subscription-9999999999`,path:r.path,args:o},e.frameBytes);let f=i({path:r.path,args:o}),p=l.get(f);if(!p){if(l.size>=e.subscriptions)throw new t(`TOO_MANY_SUBSCRIPTIONS`,`Subscription limit exceeded`);p={id:`subscription-${++h}`,ref:r,args:JSON.parse(i(o)),observers:new Set,revision:-1,hasValue:!1,terminal:!1},l.set(f,p),d?.readyState===1&&C(p)}let v={next:u.next,error:u.error};p.observers.add(v),p.hasValue&&y({...p,observers:new Set([u])},`next`,p.value),T();let x=!1;return()=>{if(!x&&(x=!0,p.observers.delete(v),p.observers.size||(l.delete(f),d?.readyState===1&&S({type:`unsubscribe`,id:p.id})),!l.size)){++m,g&&clearTimeout(g),g=void 0;let e=d;d=void 0;for(let e of[..._])e();e?.close(1e3,`No subscriptions`)}}},refreshAuth(){b(),++m,g&&clearTimeout(g),g=void 0;for(let e of[..._])e();let e=d;d=void 0,e?.close(1e3,`Authorization changed`);for(let e of l.values())e.hasValue=!1,e.value=void 0,e.revision=-1,e.terminal=!1,y(e,`error`,new t(`AUTH_CHANGED`,`Authorization changed; cached value invalidated`));T()},dispose(){if(!u){u=!0,++m,g&&clearTimeout(g),g=void 0;for(let e of[..._])e();l.clear(),d?.close(1e3,`Disposed`),d=void 0}}}}export{r as i,o as n,s as r,l as t};