@colixsystems/widget-sdk 0.58.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/contract.cjs +35 -1
- package/dist/contract.js +35 -1
- package/dist/hooks.js +74 -0
- package/dist/host.d.ts +13 -0
- package/dist/host.js +12 -0
- package/dist/index.js +1 -0
- package/dist/index.native.js +3 -0
- package/dist/property-schema.js +53 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -52,7 +52,15 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
52
52
|
|
|
53
53
|
## Status
|
|
54
54
|
|
|
55
|
-
`v0.
|
|
55
|
+
`v0.60.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
|
|
56
|
+
|
|
57
|
+
### What's new in 0.60.0
|
|
58
|
+
|
|
59
|
+
**The host fills `propertySchema` defaults onto props (sc-3228).** The platform host now applies a widget's manifest `default`s at its render boundary: for every leaf a page left unset it substitutes the declared `default`; an explicitly-bound value passes through untouched. The web Player and the native Expo export both do this against the widget's `propertySchema`, so an unset `columnRef` / field binding arrives as its declared default (e.g. `"Title"`) on BOTH hosts instead of `undefined`. **For widget authors this means: read `props.<field>` directly — the in-code `props.titleField || "Title"` fallback pattern is no longer needed and should be removed.** This is done for you by the host; there is no author API to call. (The resolver lives at the host-only subpath `@colixsystems/widget-sdk/host`, consumed by the platform hosts, not by widgets.) `CONTRACT.version` → `1.41.0`. Additive — no existing export changed signature.
|
|
60
|
+
|
|
61
|
+
### What's new in 0.59.0
|
|
62
|
+
|
|
63
|
+
**Display a stored file by id — `useFilestoreFile(fileId)` (sc-3031).** A new FILESTORE read hook that resolves ONE file id to a displayable URL. Given a file id the app stored — chiefly a datastore `FILE` column, which holds a filestore file id (a string), never bytes — it fetches the record via `ctx.filestore.files.get(id)` and returns `{ file, url, loading, error, refetch }`, where `url` is the record's `presigned_url` absolutized by the client so it renders on the web Player **and** the native Expo export alike. Drop `url` straight into `<Image source={{ uri: url }} />`. An empty id makes no round-trip (`{ file: null, url: null }`); a deleted / not-found id degrades the same way (`url` stays null, `error` carries the wire error) so a display widget shows its fallback instead of crashing. This closes the upload→store→display loop: `useFilestoreUpload(...).upload(file)` → write `file.id` into the `FILE` column → `useFilestoreFile(id).url` to show it. Requires the `files.read:*` scope. `CONTRACT.version` → `1.40.0`. Additive — one new read hook; no existing export changed signature.
|
|
56
64
|
|
|
57
65
|
### What's new in 0.58.0
|
|
58
66
|
|
|
@@ -210,6 +218,7 @@ Also: `useFileSignatures(fileIds)` is now **self-scoped** (the caller's own sign
|
|
|
210
218
|
|
|
211
219
|
**Filestore browsing + BankID file signing for widgets (REQ-FS / REQ-SIGN).** Three new hooks read a newly-injected `ctx.filestore` (the `@colixsystems/filestore-client`, now constructed by both the web and native hosts):
|
|
212
220
|
- `useFilestoreFiles({ spaceType, folderId?, q?, type? })` → `{ files, loading, error, refetch }` — browses the end-user's Filestore space. The hook resolves `owner_id` from the host context (tenant for a project space, the app user for a personal space), so the widget only picks the space.
|
|
221
|
+
- `useFilestoreFile(fileId)` → `{ file, url, loading, error, refetch }` — resolves ONE file id to a displayable `url` (its `presigned_url`, absolutized for web + native) via `ctx.filestore.files.get(id)`. A datastore `FILE` column holds — and reads back as — that **bare file-id string**; it is NOT hydrated into an object the way a `RELATION` (`{ id, label }`) or `USER` (`{ id, name }`) column is, so pass the value straight to the hook (no `{ id }` / `{ url }` unwrapping guard). Empty / deleted / not-found ids resolve to `url: null` so a display widget shows a fallback. Requires `files.read:*`.
|
|
213
222
|
- `useFilestoreFolders({ spaceType, parentFolderId?, q?, enabled? })` → `{ folders, loading, error, refetch }` — the folder-navigation companion to `useFilestoreFiles`; pass `enabled:false` to suspend fetching.
|
|
214
223
|
- `useFilestoreUpload({ spaceType, folderId? })` → `{ upload, uploading, error, lastUploaded }` — POSTs a multipart upload to `ctx.filestore.files.upload`. Resolves `owner_id` from the host context (like the read hooks) so the widget only picks the space + destination folder. Pair with the `<FilePicker>` primitive for the visible trigger. Requires the `files.write:*` scope.
|
|
215
224
|
- `useFileSignature(fileId)` → `{ status, qr, signerName, verdict, initiate, refresh, cancel, verify, … }` — drives a BankID signing flow for a file (the backend hashes the bytes server-side, binds the digest into the signature, and verifies the proof offline).
|
package/dist/contract.cjs
CHANGED
|
@@ -214,6 +214,29 @@ const HOOKS = [
|
|
|
214
214
|
requiredContextSlice: ["filestore.files"],
|
|
215
215
|
scopes: ["files.read:*"],
|
|
216
216
|
},
|
|
217
|
+
{
|
|
218
|
+
name: "useFilestoreFile",
|
|
219
|
+
signature: "useFilestoreFile(fileId)",
|
|
220
|
+
description:
|
|
221
|
+
"Resolve ONE Filestore file id to a displayable URL. Given a file id " +
|
|
222
|
+
"the app stored (e.g. a datastore FILE column, which holds a filestore " +
|
|
223
|
+
"file id — never bytes), the hook fetches the record via " +
|
|
224
|
+
"ctx.filestore.files.get(id) and surfaces its `presigned_url` as `url`, " +
|
|
225
|
+
"ready to drop into <Image source={{ uri: url }} />. The URL is " +
|
|
226
|
+
"absolutized by the client so it loads on web AND native. An empty id " +
|
|
227
|
+
"collapses to { file: null, url: null } with no round-trip; a deleted / " +
|
|
228
|
+
"not-found id degrades the same way (url stays null, error carries the " +
|
|
229
|
+
"wire error) so a display widget shows its fallback instead of crashing.",
|
|
230
|
+
returnShape: {
|
|
231
|
+
file: "FilestoreFile | null",
|
|
232
|
+
url: "string | null",
|
|
233
|
+
loading: "boolean",
|
|
234
|
+
error: "Error | null",
|
|
235
|
+
refetch: "() => Promise<void>",
|
|
236
|
+
},
|
|
237
|
+
requiredContextSlice: ["filestore.files"],
|
|
238
|
+
scopes: ["files.read:*"],
|
|
239
|
+
},
|
|
217
240
|
{
|
|
218
241
|
name: "useFilestoreUpload",
|
|
219
242
|
signature: "useFilestoreUpload({ spaceType, folderId? })",
|
|
@@ -1864,7 +1887,18 @@ const CONTRACT = deepFreeze({
|
|
|
1864
1887
|
// resolved live by `created_at` desc with limit 1; recordId ignored). The
|
|
1865
1888
|
// Data Value widget reads it to show a live "latest entry". Existing
|
|
1866
1889
|
// bindings have no `mode` and read as static — additive, minor bump.
|
|
1867
|
-
|
|
1890
|
+
// 1.40.0: additive (sc-3031) — `useFilestoreFile(fileId)` resolves one
|
|
1891
|
+
// filestore file id (as held in a datastore FILE column) to a
|
|
1892
|
+
// displayable `url` (its presigned_url, absolutized for web + native).
|
|
1893
|
+
// New read hook, no existing behaviour changes — minor bump.
|
|
1894
|
+
// 1.41.0: additive (sc-3228) — the host (web Player + native export) now
|
|
1895
|
+
// fills a widget's propertySchema `default`s onto props at the render
|
|
1896
|
+
// boundary, so an unset `columnRef` / field binding arrives as its declared
|
|
1897
|
+
// default instead of undefined. A widget reads `props.<field>` directly —
|
|
1898
|
+
// no in-code `|| "fallback"`. Backed by a host-only subpath export
|
|
1899
|
+
// (`@colixsystems/widget-sdk/host` -> resolveProps); the author-facing entry
|
|
1900
|
+
// is unchanged. No existing behaviour changes — minor bump.
|
|
1901
|
+
version: "1.41.0",
|
|
1868
1902
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1869
1903
|
hooks: HOOKS,
|
|
1870
1904
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -214,6 +214,29 @@ const HOOKS = [
|
|
|
214
214
|
requiredContextSlice: ["filestore.files"],
|
|
215
215
|
scopes: ["files.read:*"],
|
|
216
216
|
},
|
|
217
|
+
{
|
|
218
|
+
name: "useFilestoreFile",
|
|
219
|
+
signature: "useFilestoreFile(fileId)",
|
|
220
|
+
description:
|
|
221
|
+
"Resolve ONE Filestore file id to a displayable URL. Given a file id " +
|
|
222
|
+
"the app stored (e.g. a datastore FILE column, which holds a filestore " +
|
|
223
|
+
"file id — never bytes), the hook fetches the record via " +
|
|
224
|
+
"ctx.filestore.files.get(id) and surfaces its `presigned_url` as `url`, " +
|
|
225
|
+
"ready to drop into <Image source={{ uri: url }} />. The URL is " +
|
|
226
|
+
"absolutized by the client so it loads on web AND native. An empty id " +
|
|
227
|
+
"collapses to { file: null, url: null } with no round-trip; a deleted / " +
|
|
228
|
+
"not-found id degrades the same way (url stays null, error carries the " +
|
|
229
|
+
"wire error) so a display widget shows its fallback instead of crashing.",
|
|
230
|
+
returnShape: {
|
|
231
|
+
file: "FilestoreFile | null",
|
|
232
|
+
url: "string | null",
|
|
233
|
+
loading: "boolean",
|
|
234
|
+
error: "Error | null",
|
|
235
|
+
refetch: "() => Promise<void>",
|
|
236
|
+
},
|
|
237
|
+
requiredContextSlice: ["filestore.files"],
|
|
238
|
+
scopes: ["files.read:*"],
|
|
239
|
+
},
|
|
217
240
|
{
|
|
218
241
|
name: "useFilestoreUpload",
|
|
219
242
|
signature: "useFilestoreUpload({ spaceType, folderId? })",
|
|
@@ -1864,7 +1887,18 @@ const CONTRACT = deepFreeze({
|
|
|
1864
1887
|
// resolved live by `created_at` desc with limit 1; recordId ignored). The
|
|
1865
1888
|
// Data Value widget reads it to show a live "latest entry". Existing
|
|
1866
1889
|
// bindings have no `mode` and read as static — additive, minor bump.
|
|
1867
|
-
|
|
1890
|
+
// 1.40.0: additive (sc-3031) — `useFilestoreFile(fileId)` resolves one
|
|
1891
|
+
// filestore file id (as held in a datastore FILE column) to a
|
|
1892
|
+
// displayable `url` (its presigned_url, absolutized for web + native).
|
|
1893
|
+
// New read hook, no existing behaviour changes — minor bump.
|
|
1894
|
+
// 1.41.0: additive (sc-3228) — the host (web Player + native export) now
|
|
1895
|
+
// fills a widget's propertySchema `default`s onto props at the render
|
|
1896
|
+
// boundary, so an unset `columnRef` / field binding arrives as its declared
|
|
1897
|
+
// default instead of undefined. A widget reads `props.<field>` directly —
|
|
1898
|
+
// no in-code `|| "fallback"`. Backed by a host-only subpath export
|
|
1899
|
+
// (`@colixsystems/widget-sdk/host` -> resolveProps); the author-facing entry
|
|
1900
|
+
// is unchanged. No existing behaviour changes — minor bump.
|
|
1901
|
+
version: "1.41.0",
|
|
1868
1902
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1869
1903
|
hooks: HOOKS,
|
|
1870
1904
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -1502,6 +1502,80 @@ export function useFilestoreFiles(options) {
|
|
|
1502
1502
|
return { files, loading, error, refetch };
|
|
1503
1503
|
}
|
|
1504
1504
|
|
|
1505
|
+
/**
|
|
1506
|
+
* sc-3031 — resolve ONE Filestore file id to a displayable URL. Returns
|
|
1507
|
+
* `{ file, url, loading, error, refetch }`. Given a file id the app stored
|
|
1508
|
+
* (e.g. a datastore FILE column, which holds a filestore file id — never
|
|
1509
|
+
* bytes), the hook fetches the file record via `ctx.filestore.files.get(id)`
|
|
1510
|
+
* and surfaces its `presigned_url` as `url`, ready to drop straight into an
|
|
1511
|
+
* `<Image source={{ uri: url }} />`. The URL is absolutized by the filestore
|
|
1512
|
+
* client, so it loads on the web Player AND the native export.
|
|
1513
|
+
*
|
|
1514
|
+
* An empty / null id collapses to `{ file: null, url: null }` with no network
|
|
1515
|
+
* round-trip; a deleted or not-found id degrades the same way (`url` stays
|
|
1516
|
+
* null and `error` carries the wire error) so a display widget shows its
|
|
1517
|
+
* fallback instead of crashing.
|
|
1518
|
+
*/
|
|
1519
|
+
export function useFilestoreFile(fileId) {
|
|
1520
|
+
const ctx = useWidgetContextOrThrow("useFilestoreFile");
|
|
1521
|
+
if (
|
|
1522
|
+
!ctx.filestore ||
|
|
1523
|
+
!ctx.filestore.files ||
|
|
1524
|
+
typeof ctx.filestore.files.get !== "function"
|
|
1525
|
+
) {
|
|
1526
|
+
throw new Error(
|
|
1527
|
+
"useFilestoreFile: host did not inject a filestore client (ctx.filestore.files.get)",
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
const [file, setFile] = useState(null);
|
|
1532
|
+
const [loading, setLoading] = useState(Boolean(fileId));
|
|
1533
|
+
const [error, setError] = useState(null);
|
|
1534
|
+
|
|
1535
|
+
const filesRef = useRef(ctx.filestore.files);
|
|
1536
|
+
filesRef.current = ctx.filestore.files;
|
|
1537
|
+
const idRef = useRef(fileId);
|
|
1538
|
+
idRef.current = fileId;
|
|
1539
|
+
const runRef = useRef(0);
|
|
1540
|
+
|
|
1541
|
+
const doFetch = useCallback(async () => {
|
|
1542
|
+
const myRun = ++runRef.current;
|
|
1543
|
+
const id = idRef.current;
|
|
1544
|
+
if (!id) {
|
|
1545
|
+
setLoading(false);
|
|
1546
|
+
setError(null);
|
|
1547
|
+
setFile(null);
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
setLoading(true);
|
|
1551
|
+
setError(null);
|
|
1552
|
+
try {
|
|
1553
|
+
const row = await filesRef.current.get(id);
|
|
1554
|
+
if (runRef.current !== myRun) return;
|
|
1555
|
+
setFile(row || null);
|
|
1556
|
+
setLoading(false);
|
|
1557
|
+
} catch (err) {
|
|
1558
|
+
if (runRef.current !== myRun) return;
|
|
1559
|
+
setError(err);
|
|
1560
|
+
setFile(null);
|
|
1561
|
+
setLoading(false);
|
|
1562
|
+
}
|
|
1563
|
+
}, []);
|
|
1564
|
+
|
|
1565
|
+
useEffect(() => {
|
|
1566
|
+
doFetch();
|
|
1567
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1568
|
+
}, [fileId]);
|
|
1569
|
+
|
|
1570
|
+
const refetch = useCallback(async () => {
|
|
1571
|
+
await doFetch();
|
|
1572
|
+
}, [doFetch]);
|
|
1573
|
+
|
|
1574
|
+
const url =
|
|
1575
|
+
file && typeof file.presigned_url === "string" ? file.presigned_url : null;
|
|
1576
|
+
return { file, url, loading, error, refetch };
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1505
1579
|
/**
|
|
1506
1580
|
* sc-1378 — upload a file into the end-user's Filestore space. Returns
|
|
1507
1581
|
* `{ upload, uploading, error, lastUploaded }`. The widget passes the
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Host-integration surface — not the author-facing widget API. See host.js.
|
|
2
|
+
import type { WidgetPropertySchema } from "./index";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Host render-boundary helper: fills a widget's propertySchema `default`s onto
|
|
6
|
+
* props for unset (undefined/null) leaves; explicitly-bound values pass
|
|
7
|
+
* through. Never validates or coerces; always returns the merged object.
|
|
8
|
+
* Applied by the platform hosts, never by widget authors.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveProps<T = Record<string, unknown>>(
|
|
11
|
+
schema: WidgetPropertySchema,
|
|
12
|
+
props: unknown,
|
|
13
|
+
): T;
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Host-integration surface — NOT part of the author-facing widget API.
|
|
2
|
+
// These exports are consumed only by the platform hosts that render widgets
|
|
3
|
+
// (the web Player / Studio Canvas and the native Expo export), never by a
|
|
4
|
+
// widget author. Authors receive already-correct props and never call these.
|
|
5
|
+
//
|
|
6
|
+
// `resolveProps` lives here (re-exported from property-schema.js, the single
|
|
7
|
+
// source of schema semantics) so the host can fill a widget's declared
|
|
8
|
+
// propertySchema `default`s onto props at its render boundary. It is
|
|
9
|
+
// deliberately kept off the main entry (`@colixsystems/widget-sdk`) so it does
|
|
10
|
+
// not appear in the author import surface or the Developer guide.
|
|
11
|
+
|
|
12
|
+
export { resolveProps } from "./property-schema.js";
|
package/dist/index.js
CHANGED
package/dist/index.native.js
CHANGED
|
@@ -18,11 +18,14 @@ export {
|
|
|
18
18
|
useAsset,
|
|
19
19
|
useAssetsByTag,
|
|
20
20
|
useFilestoreFiles,
|
|
21
|
+
useFilestoreFile,
|
|
21
22
|
useFilestoreUpload,
|
|
22
23
|
usePdfExport,
|
|
23
24
|
useFilestoreFolders,
|
|
24
25
|
useFileSignature,
|
|
25
26
|
useFileSignatures,
|
|
27
|
+
useFileRoster,
|
|
28
|
+
useFolderPermissions,
|
|
26
29
|
useDatastoreMutation,
|
|
27
30
|
useDirectory,
|
|
28
31
|
useUsers,
|
package/dist/property-schema.js
CHANGED
|
@@ -243,3 +243,56 @@ export function validateProps(schema, props) {
|
|
|
243
243
|
}
|
|
244
244
|
return errors.length === 0 ? { ok: true, value: out } : { ok: false, errors };
|
|
245
245
|
}
|
|
246
|
+
|
|
247
|
+
// Defaults are static config; clone object/array ones so a widget mutating
|
|
248
|
+
// its props can never corrupt the shared manifest default.
|
|
249
|
+
function cloneDefault(value) {
|
|
250
|
+
if (value === null || typeof value !== "object") return value;
|
|
251
|
+
return JSON.parse(JSON.stringify(value));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function resolveLeaf(def, value) {
|
|
255
|
+
if (!isPlainObject(def)) return value;
|
|
256
|
+
if (value === undefined || value === null) {
|
|
257
|
+
return def.default !== undefined ? cloneDefault(def.default) : value;
|
|
258
|
+
}
|
|
259
|
+
if (
|
|
260
|
+
def.type === "object" &&
|
|
261
|
+
isPlainObject(def.properties) &&
|
|
262
|
+
isPlainObject(value)
|
|
263
|
+
) {
|
|
264
|
+
const out = { ...value };
|
|
265
|
+
for (const [k, child] of Object.entries(def.properties)) {
|
|
266
|
+
out[k] = resolveLeaf(child, value[k]);
|
|
267
|
+
}
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
// Fill per-element defaults for an author-supplied array<object> (e.g. a
|
|
271
|
+
// `columns` list whose item objects carry field-level defaults).
|
|
272
|
+
if (def.type === "array" && isPlainObject(def.items) && Array.isArray(value)) {
|
|
273
|
+
return value.map((item) => resolveLeaf(def.items, item));
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Fills in a widget's `propertySchema` defaults on `props` for the host render
|
|
280
|
+
* boundary: any leaf the author left unset (undefined/null) takes its declared
|
|
281
|
+
* `default`; an explicitly-set value passes through untouched. Unlike
|
|
282
|
+
* {@link validateProps} it never validates or coerces and always returns the
|
|
283
|
+
* merged object — a partially-configured binding stays valid while the author
|
|
284
|
+
* is still picking. Applied once by the host (web Player + native export) so a
|
|
285
|
+
* widget reads `props.<field>` directly without in-code fallbacks.
|
|
286
|
+
* @param {Record<string, any>} schema
|
|
287
|
+
* @param {unknown} props
|
|
288
|
+
* @returns {Record<string, unknown>}
|
|
289
|
+
*/
|
|
290
|
+
export function resolveProps(schema, props) {
|
|
291
|
+
const input = isPlainObject(props) ? props : {};
|
|
292
|
+
if (!isPlainObject(schema)) return input;
|
|
293
|
+
const out = { ...input };
|
|
294
|
+
for (const [k, def] of Object.entries(schema)) {
|
|
295
|
+
out[k] = resolveLeaf(def, input[k]);
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
298
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
"import": "./dist/dev-shims.js",
|
|
23
23
|
"default": "./dist/dev-shims.js"
|
|
24
24
|
},
|
|
25
|
+
"./host": {
|
|
26
|
+
"types": "./dist/host.d.ts",
|
|
27
|
+
"react-native": "./dist/host.js",
|
|
28
|
+
"import": "./dist/host.js",
|
|
29
|
+
"default": "./dist/host.js"
|
|
30
|
+
},
|
|
25
31
|
"./contract": {
|
|
26
32
|
"types": "./dist/index.d.ts",
|
|
27
33
|
"require": "./dist/contract.cjs",
|
|
@@ -42,7 +48,7 @@
|
|
|
42
48
|
],
|
|
43
49
|
"scripts": {
|
|
44
50
|
"build": "node scripts/build.js",
|
|
45
|
-
"test": "node --test src/__tests__/contract.test.js src/__tests__/hooks-users.test.js src/__tests__/hooks-groups.test.js src/__tests__/hooks-schema.test.js src/__tests__/hooks-assets-by-tag.test.js src/__tests__/hooks-filestore-upload.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-subscription.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-platform.test.js src/__tests__/linter-react-import.test.js src/__tests__/lucide-icon-names.test.js src/__tests__/lucideIconName.test.js src/__tests__/manifest-actions.test.js src/__tests__/widget-translations.test.js src/__tests__/devserver.test.js src/__tests__/host-externals.test.js src/__tests__/datetimepicker.test.js"
|
|
51
|
+
"test": "node --test src/__tests__/contract.test.js src/__tests__/hooks-users.test.js src/__tests__/hooks-groups.test.js src/__tests__/hooks-schema.test.js src/__tests__/hooks-assets-by-tag.test.js src/__tests__/hooks-filestore-upload.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-subscription.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-platform.test.js src/__tests__/linter-react-import.test.js src/__tests__/lucide-icon-names.test.js src/__tests__/lucideIconName.test.js src/__tests__/manifest-actions.test.js src/__tests__/widget-translations.test.js src/__tests__/devserver.test.js src/__tests__/host-externals.test.js src/__tests__/datetimepicker.test.js src/__tests__/property-schema-resolve.test.js"
|
|
46
52
|
},
|
|
47
53
|
"engines": {
|
|
48
54
|
"node": ">=18"
|