@happyvertical/smrt-web 0.49.3 → 0.49.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,12 @@ fetch one canonical bounded page. Visible runs update state; `background`,
15
15
  stale rows available while refreshing, cancels superseded visible runs, and can
16
16
  attach a query-scoped live subscription.
17
17
 
18
+ Data-surface actions use `executeSmrtWebDataSurfaceAction(transport, request)`.
19
+ Its request carries the server-issued revision and, for apply, an idempotency
20
+ key; the browser normalizer rejects malformed, oversized, or mismatched
21
+ preview/apply results before they reach UI state. The authenticated server still
22
+ derives authority and validates confirmation tokens.
23
+
18
24
  ## Installation
19
25
 
20
26
  ```bash
@@ -126,6 +132,7 @@ core.
126
132
  | --- | --- |
127
133
  | Collections | `createSmrtCollection`, `createSmrtWebClient`, `newLocalId` |
128
134
  | Remote queries | `createSmrtWebQuery`, `SmrtWebQueryTransport` |
135
+ | Data-surface actions | `executeSmrtWebDataSurfaceAction`, `SmrtWebDataSurfaceActionTransport` |
129
136
  | HTTP | `createDefinitionFetchers`, `unwrapListResult`, `unwrapItemResult` |
130
137
  | Offline | `offlineOutbox`, `getOutboxHandle` |
131
138
  | Persistence | `persistCollection`, `wipeDurableStore` |
@@ -21,7 +21,7 @@ var persistedMutationResults = /* @__PURE__ */ new WeakMap();
21
21
  var mutationTargetHydrators = /* @__PURE__ */ new WeakMap();
22
22
  //#endregion
23
23
  //#region src/data-query.ts
24
- var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
24
+ var FORBIDDEN_KEYS$1 = /* @__PURE__ */ new Set([
25
25
  "__proto__",
26
26
  "constructor",
27
27
  "prototype"
@@ -44,10 +44,10 @@ function plainObject(value, label) {
44
44
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} must be a plain object`);
45
45
  const prototype = Object.getPrototypeOf(value);
46
46
  if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${label} must be a plain object`);
47
- for (const key of Object.keys(value)) if (FORBIDDEN_KEYS.has(key)) throw new TypeError(`${label} contains a forbidden key`);
47
+ for (const key of Object.keys(value)) if (FORBIDDEN_KEYS$1.has(key)) throw new TypeError(`${label} contains a forbidden key`);
48
48
  return value;
49
49
  }
50
- function exactKeys(value, allowed, label) {
50
+ function exactKeys$1(value, allowed, label) {
51
51
  const keys = new Set(allowed);
52
52
  for (const key of Object.keys(value)) if (!keys.has(key)) throw new TypeError(`${label} contains ${key}`);
53
53
  }
@@ -111,14 +111,14 @@ function normalizeTotal(value) {
111
111
  const total = plainObject(value, "Data query total");
112
112
  const kind = stringValue(total.kind, "Data query total kind");
113
113
  if (kind === "unavailable") {
114
- exactKeys(total, ["kind", "reason"], "Data query unavailable total");
114
+ exactKeys$1(total, ["kind", "reason"], "Data query unavailable total");
115
115
  return {
116
116
  kind,
117
117
  ...total.reason === void 0 ? {} : { reason: stringValue(total.reason, "Data query total reason") }
118
118
  };
119
119
  }
120
120
  if (kind !== "exact" && kind !== "estimated") throw new TypeError("Data query total kind is invalid");
121
- exactKeys(total, [
121
+ exactKeys$1(total, [
122
122
  "kind",
123
123
  "value",
124
124
  "asOf"
@@ -136,7 +136,7 @@ function normalizeFacets(value, budget) {
136
136
  const fields = /* @__PURE__ */ new Set();
137
137
  return value.map((candidate, index) => {
138
138
  const facet = plainObject(candidate, `Data query facet ${index}`);
139
- exactKeys(facet, [
139
+ exactKeys$1(facet, [
140
140
  "field",
141
141
  "values",
142
142
  "truncated"
@@ -151,7 +151,7 @@ function normalizeFacets(value, budget) {
151
151
  field,
152
152
  values: facet.values.map((value2, valueIndex) => {
153
153
  const entry = plainObject(value2, `Data query facet ${index} value ${valueIndex}`);
154
- exactKeys(entry, ["value", "count"], "Data query facet value");
154
+ exactKeys$1(entry, ["value", "count"], "Data query facet value");
155
155
  return {
156
156
  value: scalar(entry.value, "Data query facet value", budget),
157
157
  count: nonNegativeInteger(entry.count, "Data query facet count")
@@ -163,7 +163,7 @@ function normalizeFacets(value, budget) {
163
163
  }
164
164
  function normalizeSmrtWebDataQueryResult(value) {
165
165
  const result = plainObject(value, "Data query result");
166
- exactKeys(result, [
166
+ exactKeys$1(result, [
167
167
  "version",
168
168
  "requestId",
169
169
  "queryFingerprint",
@@ -190,7 +190,7 @@ function normalizeSmrtWebDataQueryResult(value) {
190
190
  let page;
191
191
  if (result.page !== void 0) {
192
192
  const candidate = plainObject(result.page, "Data query page");
193
- exactKeys(candidate, [
193
+ exactKeys$1(candidate, [
194
194
  "kind",
195
195
  "limit",
196
196
  "offset",
@@ -227,7 +227,7 @@ function normalizeSmrtWebDataQueryResult(value) {
227
227
  }
228
228
  if (page && rows.length > page.limit) throw new TypeError("Data query rows exceed the declared page limit");
229
229
  const freshness = plainObject(result.freshness, "Data query freshness");
230
- exactKeys(freshness, ["state", "asOf"], "Data query freshness");
230
+ exactKeys$1(freshness, ["state", "asOf"], "Data query freshness");
231
231
  const state = stringValue(freshness.state, "Data query freshness state");
232
232
  if (state !== "fresh" && state !== "stale" && state !== "unknown") throw new TypeError("Data query freshness state is invalid");
233
233
  if (!Array.isArray(result.warnings) || result.warnings.length > 100 || result.warnings.some((item) => typeof item !== "string")) throw new TypeError("Data query warnings must be strings");
@@ -259,6 +259,120 @@ async function executeSmrtWebDataQuery(transport, request, options) {
259
259
  return result;
260
260
  }
261
261
  //#endregion
262
+ //#region src/data-surface-actions.ts
263
+ var MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES = 1e6;
264
+ var MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH = 256;
265
+ var MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH = 16;
266
+ var MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS = 1e3;
267
+ var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
268
+ "__proto__",
269
+ "constructor",
270
+ "prototype"
271
+ ]);
272
+ function object(value, label) {
273
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} must be a plain object`);
274
+ const prototype = Object.getPrototypeOf(value);
275
+ if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`${label} must be a plain object`);
276
+ for (const key of Object.keys(value)) if (FORBIDDEN_KEYS.has(key)) throw new TypeError(`${label} contains a forbidden key`);
277
+ return value;
278
+ }
279
+ function exactKeys(value, allowed, label) {
280
+ const allowedKeys = new Set(allowed);
281
+ for (const key of Object.keys(value)) if (!allowedKeys.has(key)) throw new TypeError(`${label} contains ${key}`);
282
+ }
283
+ function identifier(value, label) {
284
+ if (typeof value !== "string" || value.length === 0 || value.length > 256) throw new TypeError(`${label} must be a bounded non-empty string`);
285
+ return value;
286
+ }
287
+ function json(value, label, depth = 0) {
288
+ if (depth > 16) throw new TypeError(`${label} exceeds JSON depth`);
289
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
290
+ if (Array.isArray(value)) {
291
+ if (value.length > 1e3) throw new TypeError(`${label} exceeds the container-item limit`);
292
+ return value.map((entry, index) => json(entry, `${label}[${index}]`, depth + 1));
293
+ }
294
+ const source = object(value, label);
295
+ if (Object.keys(source).length > 1e3) throw new TypeError(`${label} exceeds the container-item limit`);
296
+ const result = /* @__PURE__ */ Object.create(null);
297
+ for (const [key, entry] of Object.entries(source)) result[key] = json(entry, `${label}.${key}`, depth + 1);
298
+ return result;
299
+ }
300
+ function identity(value) {
301
+ const source = object(value, "Data surface identity");
302
+ exactKeys(source, [
303
+ "surfaceId",
304
+ "kind",
305
+ "subject"
306
+ ], "Data surface identity");
307
+ const kind = source.kind;
308
+ if (![
309
+ "table",
310
+ "list",
311
+ "report",
312
+ "custom"
313
+ ].includes(kind)) throw new TypeError("Data surface identity kind is invalid");
314
+ const result = {
315
+ surfaceId: identifier(source.surfaceId, "Data surface identity surfaceId"),
316
+ kind
317
+ };
318
+ if (source.subject !== void 0) {
319
+ const subject = object(source.subject, "Data surface identity subject");
320
+ exactKeys(subject, [
321
+ "type",
322
+ "id",
323
+ "label"
324
+ ], "Data surface identity subject");
325
+ result.subject = {
326
+ type: identifier(subject.type, "Data surface identity subject type"),
327
+ id: identifier(subject.id, "Data surface identity subject id"),
328
+ ...subject.label === void 0 ? {} : { label: identifier(subject.label, "Data surface identity subject label") }
329
+ };
330
+ }
331
+ return result;
332
+ }
333
+ function sameIdentity(left, right) {
334
+ return left.surfaceId === right.surfaceId && left.kind === right.kind && left.subject?.type === right.subject?.type && left.subject?.id === right.subject?.id;
335
+ }
336
+ function normalizeSmrtWebDataSurfaceActionResult(value) {
337
+ const serialized = JSON.stringify(value);
338
+ if (serialized === void 0 || new TextEncoder().encode(serialized).byteLength > 1e6) throw new TypeError("Data surface action result exceeds the maximum byte limit");
339
+ const source = object(value, "Data surface action result");
340
+ exactKeys(source, [
341
+ "version",
342
+ "requestId",
343
+ "identity",
344
+ "actionId",
345
+ "phase",
346
+ "ok",
347
+ "reason",
348
+ "confirmationToken",
349
+ "details"
350
+ ], "Data surface action result");
351
+ if (source.version !== 1 || source.phase !== "preview" && source.phase !== "apply") throw new TypeError("Data surface action result has an invalid version or phase");
352
+ if (typeof source.ok !== "boolean") throw new TypeError("Data surface action result ok must be boolean");
353
+ const result = {
354
+ version: 1,
355
+ requestId: identifier(source.requestId, "Data surface action result requestId"),
356
+ identity: identity(source.identity),
357
+ actionId: identifier(source.actionId, "Data surface action result actionId"),
358
+ phase: source.phase,
359
+ ok: source.ok
360
+ };
361
+ if (source.reason !== void 0) result.reason = identifier(source.reason, "Data surface action result reason");
362
+ if (source.confirmationToken !== void 0) result.confirmationToken = identifier(source.confirmationToken, "Data surface action result confirmationToken");
363
+ if (source.details !== void 0) {
364
+ const details = json(source.details, "Data surface action result details");
365
+ if (Array.isArray(details) || details === null || typeof details !== "object") throw new TypeError("Data surface action result details must be an object");
366
+ result.details = details;
367
+ }
368
+ return result;
369
+ }
370
+ async function executeSmrtWebDataSurfaceAction(transport, request, options) {
371
+ const result = normalizeSmrtWebDataSurfaceActionResult(await transport.action(request, options));
372
+ if (result.requestId !== request.requestId || result.actionId !== request.actionId || result.phase !== request.phase || !sameIdentity(result.identity, request.identity)) throw new TypeError("Data surface action result does not match its request");
373
+ return result;
374
+ }
375
+ //#endregion
262
376
  //#region src/durable-store.ts
263
377
  function durableStoreNamespace(key) {
264
378
  const optional = (value) => value === void 0 ? "" : `_${encodeURIComponent(value)}`;
@@ -3037,6 +3151,6 @@ function createSmrtCollection(definition, options) {
3037
3151
  return handle;
3038
3152
  }
3039
3153
  //#endregion
3040
- export { MAX_SMRT_WEB_DATA_QUERY_OFFSET as A, offlineOutbox as C, MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS as D, wipeDurableStore as E, MAX_SMRT_WEB_DATA_QUERY_WARNINGS as F, executeSmrtWebDataQuery as I, normalizeSmrtWebDataQueryResult as L, MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES as M, MAX_SMRT_WEB_DATA_QUERY_ROWS as N, MAX_SMRT_WEB_DATA_QUERY_FACETS as O, MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH as P, runWrapMutation as R, getOutboxHandle as S, registerDurableResource as T, createSmrtWebEventSubscriber as _, createSmrtWebClient as a, DEFAULT_PERSIST_DEBOUNCE_MS as b, newLocalId as c, unwrapListResult as d, validateSmrtWebClient as f, createUpdateState as g, registerWebMcpTools as h, createSmrtCollection as i, MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT as j, MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES as k, throwIfSmrtWebError as l, registerWebMcpBespokeTool as m, buildListQuery as n, getEngineCollection as o, registerViewIntent as p, createDefinitionFetchers as r, invalidateSmrtWebCollections as s, SmrtWebRequestError as t, unwrapItemResult as u, liveInvalidation as v, durableStoreNamespace as w, persistCollection as x, createSmrtWebQuery as y };
3154
+ export { MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES as A, MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH as B, offlineOutbox as C, MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS as D, wipeDurableStore as E, MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES as F, executeSmrtWebDataQuery as H, MAX_SMRT_WEB_DATA_QUERY_OFFSET as I, MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT as L, normalizeSmrtWebDataSurfaceActionResult as M, MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS as N, MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH as O, MAX_SMRT_WEB_DATA_QUERY_FACETS as P, MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES as R, getOutboxHandle as S, registerDurableResource as T, normalizeSmrtWebDataQueryResult as U, MAX_SMRT_WEB_DATA_QUERY_WARNINGS as V, runWrapMutation as W, createSmrtWebEventSubscriber as _, createSmrtWebClient as a, DEFAULT_PERSIST_DEBOUNCE_MS as b, newLocalId as c, unwrapListResult as d, validateSmrtWebClient as f, createUpdateState as g, registerWebMcpTools as h, createSmrtCollection as i, executeSmrtWebDataSurfaceAction as j, MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH as k, throwIfSmrtWebError as l, registerWebMcpBespokeTool as m, buildListQuery as n, getEngineCollection as o, registerViewIntent as p, createDefinitionFetchers as r, invalidateSmrtWebCollections as s, SmrtWebRequestError as t, unwrapItemResult as u, liveInvalidation as v, durableStoreNamespace as w, persistCollection as x, createSmrtWebQuery as y, MAX_SMRT_WEB_DATA_QUERY_ROWS as z };
3041
3155
 
3042
- //# sourceMappingURL=src-BSrj5jW0.js.map
3156
+ //# sourceMappingURL=src-0IQGhPIM.js.map