@colixsystems/widget-sdk 0.105.0 → 0.107.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 CHANGED
@@ -68,7 +68,47 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
68
68
 
69
69
  ## Status
70
70
 
71
- `v0.105.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**.
71
+ `v0.107.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**.
72
+
73
+ ### What's new in 0.107.0 (contract 1.81.0)
74
+
75
+ **Widgets can render QR codes — `react-native-qrcode-svg` is now a vetted import (sc-6105).** There was no QR encoder on the allowlist, so a check-in code, an install link, a table-ordering code, or a payment hand-off could not be built at all. The only QR the platform produced was a PNG the backend renders for one specific BankID sign order, which encodes nothing else.
76
+
77
+ The package is vetted for **both platforms** and needs no split file: it draws with the already-vetted `react-native-svg`, so one component covers the web Player and the Expo export.
78
+
79
+ ```js
80
+ import QRCode from "react-native-qrcode-svg";
81
+ import { View } from "@colixsystems/widget-sdk";
82
+
83
+ export default function TicketCode({ ticketUrl }) {
84
+ return (
85
+ <View>
86
+ <QRCode value={ticketUrl} size={180} ecl="M" />
87
+ </View>
88
+ );
89
+ }
90
+ ```
91
+
92
+ `value` is the encoded string; `size`, `color`, `backgroundColor`, `quietZone`, `ecl` (`"L" | "M" | "Q" | "H"`) and the `logo*` props shape the render. Encoding happens on-device — no network call, so it works offline in both hosts.
93
+
94
+ One caveat carried over from the package: it documents a Metro transformer that injects a `TextEncoder` polyfill for React Native **below 0.75**. The export ships RN 0.85.3, which has `TextEncoder` as a global, so that transformer is intentionally not wired up — don't add it.
95
+
96
+ ### What's new in 0.106.0 (contract 1.80.0)
97
+
98
+ **A `required` `tableRef` can now say the table is the APP'S, not the widget's — `sharedTable: true` (sc-5982).** sc-2791's publish gate `manifest.requiredTableRefsHaveTemplate` counted only two kinds of satisfying table: one the widget seeds via `datastoreTemplate`, and one the host hands in when the in-Studio agent delegates a build. A widget that legitimately reads a table it does not own — a chart, a metric, a map, a manager view over another widget's table — could satisfy neither when publishing on its own, so the only way through the gate was to drop `required`. That trades a publish error for a worse runtime one: an unbound widget renders an empty tile and the author gets no warning. (In this repo it also took down every first-party publish for twelve days.)
99
+
100
+ `sharedTable: true` on a `tableRef` property says "required, but the author supplies the table; I seed nothing for it". It exempts the property from the seeding duty only — the property stays `required`, the author must still bind a table, and the host still refuses to place the widget unbound. Pick between the two shapes by asking who owns the data:
101
+
102
+ ```js
103
+ propertySchema: {
104
+ // The widget owns its data → seed it.
105
+ messagesTableId: { type: "tableRef", label: "Messages", required: true },
106
+ // The app owns the data, the author points at it → declare it shared.
107
+ sourceTableId: { type: "tableRef", label: "Source table", required: true, sharedTable: true },
108
+ }
109
+ ```
110
+
111
+ Leaving a `required` `tableRef` with neither a seeded table nor `sharedTable` is still rejected at publish, and the rejection still names the offending properties. Additive: every manifest that passed before passes now.
72
112
 
73
113
  ### What's new in 0.105.0 (contract unchanged)
74
114
 
@@ -547,7 +587,7 @@ useEffect(() => {
547
587
 
548
588
  ### What's new in 0.57.0
549
589
 
550
- **A `required: true` `tableRef` must ship a matching `datastoreTemplate` table (sc-2791).** A widget property that the author cannot skip — a `tableRef` you mark `required: true` — must have a table to bind on install, or the end user has nothing to pick and the widget can't load its data. The marketplace analyzer now enforces this at publish: the check `manifest.requiredTableRefsHaveTemplate` rejects a manifest whose `datastoreTemplate.tables` count is fewer than its `required` `tableRef` property count, naming the offending properties. Two ways to clear it: seed a `datastoreTemplate` table for each required `tableRef` (the default — the widget owns its data), OR make the property **optional** when it binds a table the app already owns (shared data the user names). This tightens the existing "always ship a `datastoreTemplate` for a data widget" guidance into an enforced rule for the `required` case. No export, type, or hook changed shape — additive publish-gate + documentation. `CONTRACT` is unchanged (no new field).
590
+ **A `required: true` `tableRef` must ship a matching `datastoreTemplate` table (sc-2791).** A widget property that the author cannot skip — a `tableRef` you mark `required: true` — must have a table to bind on install, or the end user has nothing to pick and the widget can't load its data. The marketplace analyzer now enforces this at publish: the check `manifest.requiredTableRefsHaveTemplate` rejects a manifest whose `datastoreTemplate.tables` count is fewer than its `required` `tableRef` property count, naming the offending properties. Two ways to clear it: seed a `datastoreTemplate` table for each required `tableRef` (the default — the widget owns its data), OR mark the property `sharedTable: true` when it binds a table the app already owns (0.106.0 it stays `required`, so the author must still bind one). Dropping `required` also clears the gate but is the wrong fix: an optional table prop lets the widget ship unbound and render an empty tile. This tightens the existing "always ship a `datastoreTemplate` for a data widget" guidance into an enforced rule for the `required` case. No export, type, or hook changed shape — additive publish-gate + documentation. `CONTRACT` is unchanged (no new field).
551
591
 
552
592
  ### What's new in 0.55.0
553
593
 
package/dist/contract.cjs CHANGED
@@ -1903,7 +1903,7 @@ const MANIFEST_SCHEMA = {
1903
1903
  type: "object",
1904
1904
  required: false,
1905
1905
  description:
1906
- "Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (must be declared earlier in the array). Tables persist across uninstalls.",
1906
+ "Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (must be declared earlier in the array). Tables persist across uninstalls. A `tableRef` property may also carry `sharedTable: true`, declaring that the table is the APP'S OWN — the author picks an existing table and the widget seeds nothing for it. That is the correct shape for a widget that reads data it does not own (a chart, a metric, a manager view over another widget's table). `sharedTable` exempts the property from needing a seeded table at publish (`manifest.requiredTableRefsHaveTemplate`) WITHOUT making it optional: the author must still bind one, and the host still refuses to place the widget unbound. Use it instead of dropping `required` — a widget that silently renders an empty tile is the defect it looks like a fix for.",
1907
1907
  },
1908
1908
  translations: {
1909
1909
  type: "object",
@@ -2457,6 +2457,13 @@ const VETTED_IMPORTS = [
2457
2457
  description:
2458
2458
  "Embeds web content in a native WebView. Native-only; on web render an <iframe> (or a View with the URL) in widget.web.jsx.",
2459
2459
  },
2460
+ {
2461
+ specifier: "react-native-qrcode-svg",
2462
+ platforms: ["web", "native"],
2463
+ category: "drawing",
2464
+ description:
2465
+ "Renders a QR code, drawn as SVG through the vetted react-native-svg — so one `<QRCode value={…} size={…} />` covers both platforms with no split file. Pure JS with no native module of its own: the web host resolves it and the Expo export pins it, exactly like date-fns. Its `text-encoding` polyfill is opt-in for React Native < 0.75 only; the export ships RN 0.85.3, which has a global TextEncoder, so the Metro transformer that package documents is deliberately not wired up.",
2466
+ },
2460
2467
  ];
2461
2468
 
2462
2469
  // sc-1064: CORE React infrastructure specifiers the host RESOLVES at runtime
@@ -3301,7 +3308,25 @@ const CONTRACT = deepFreeze({
3301
3308
  // per-scalar limits never stated, so the worst-case unauthenticated
3302
3309
  // payload is SMALLER than before. `__proto__`-style keys are refused at
3303
3310
  // every level. Additive: every value accepted before is accepted now.
3304
- version: "1.79.0",
3311
+ // 1.80.0: additive (sc-5982) — a `tableRef` property may declare
3312
+ // `sharedTable: true`: "required, but the author supplies the table; the
3313
+ // widget seeds nothing." The publish gate
3314
+ // `manifest.requiredTableRefsHaveTemplate` counted only seeded
3315
+ // `datastoreTemplate` tables and Mason-supplied bound tables, so a widget
3316
+ // that legitimately reads an app-owned table (Chart, Metric, Map, a
3317
+ // manager view) could never satisfy it when publishing standalone — which
3318
+ // blocked every first-party publish for twelve days. The flag separates
3319
+ // who PROVIDES the table from whether the author may skip it; `required`
3320
+ // still means required.
3321
+ // 1.81.0: additive (sc-6105) — `react-native-qrcode-svg` joins
3322
+ // VETTED_IMPORTS as a web+native `drawing` package. Widgets had no QR
3323
+ // encoder at all: the only QR the platform produced was the BankID PNG
3324
+ // the backend renders for a specific sign order, so a check-in code, an
3325
+ // install link, or a table-ordering code was unbuildable. It draws
3326
+ // through the already-vetted react-native-svg and carries no native
3327
+ // module of its own, so it is host-shimmed on web and pinned in the
3328
+ // export like date-fns. No existing entry changed shape.
3329
+ version: "1.81.0",
3305
3330
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
3306
3331
  hooks: HOOKS,
3307
3332
  primitives: PRIMITIVES,
package/dist/contract.js CHANGED
@@ -1903,7 +1903,7 @@ const MANIFEST_SCHEMA = {
1903
1903
  type: "object",
1904
1904
  required: false,
1905
1905
  description:
1906
- "Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (must be declared earlier in the array). Tables persist across uninstalls.",
1906
+ "Optional. Tables the widget needs, seeded into the workspace at install time. Authors wire them into the widget's `tableRef` properties via the Properties Panel — the SDK does not auto-bind. Limits: 8 tables, 24 columns per table. RELATION columns address siblings by `targetSuffix` (must be declared earlier in the array). Tables persist across uninstalls. A `tableRef` property may also carry `sharedTable: true`, declaring that the table is the APP'S OWN — the author picks an existing table and the widget seeds nothing for it. That is the correct shape for a widget that reads data it does not own (a chart, a metric, a manager view over another widget's table). `sharedTable` exempts the property from needing a seeded table at publish (`manifest.requiredTableRefsHaveTemplate`) WITHOUT making it optional: the author must still bind one, and the host still refuses to place the widget unbound. Use it instead of dropping `required` — a widget that silently renders an empty tile is the defect it looks like a fix for.",
1907
1907
  },
1908
1908
  translations: {
1909
1909
  type: "object",
@@ -2457,6 +2457,13 @@ const VETTED_IMPORTS = [
2457
2457
  description:
2458
2458
  "Embeds web content in a native WebView. Native-only; on web render an <iframe> (or a View with the URL) in widget.web.jsx.",
2459
2459
  },
2460
+ {
2461
+ specifier: "react-native-qrcode-svg",
2462
+ platforms: ["web", "native"],
2463
+ category: "drawing",
2464
+ description:
2465
+ "Renders a QR code, drawn as SVG through the vetted react-native-svg — so one `<QRCode value={…} size={…} />` covers both platforms with no split file. Pure JS with no native module of its own: the web host resolves it and the Expo export pins it, exactly like date-fns. Its `text-encoding` polyfill is opt-in for React Native < 0.75 only; the export ships RN 0.85.3, which has a global TextEncoder, so the Metro transformer that package documents is deliberately not wired up.",
2466
+ },
2460
2467
  ];
2461
2468
 
2462
2469
  // sc-1064: CORE React infrastructure specifiers the host RESOLVES at runtime
@@ -3301,7 +3308,25 @@ const CONTRACT = deepFreeze({
3301
3308
  // per-scalar limits never stated, so the worst-case unauthenticated
3302
3309
  // payload is SMALLER than before. `__proto__`-style keys are refused at
3303
3310
  // every level. Additive: every value accepted before is accepted now.
3304
- version: "1.79.0",
3311
+ // 1.80.0: additive (sc-5982) — a `tableRef` property may declare
3312
+ // `sharedTable: true`: "required, but the author supplies the table; the
3313
+ // widget seeds nothing." The publish gate
3314
+ // `manifest.requiredTableRefsHaveTemplate` counted only seeded
3315
+ // `datastoreTemplate` tables and Mason-supplied bound tables, so a widget
3316
+ // that legitimately reads an app-owned table (Chart, Metric, Map, a
3317
+ // manager view) could never satisfy it when publishing standalone — which
3318
+ // blocked every first-party publish for twelve days. The flag separates
3319
+ // who PROVIDES the table from whether the author may skip it; `required`
3320
+ // still means required.
3321
+ // 1.81.0: additive (sc-6105) — `react-native-qrcode-svg` joins
3322
+ // VETTED_IMPORTS as a web+native `drawing` package. Widgets had no QR
3323
+ // encoder at all: the only QR the platform produced was the BankID PNG
3324
+ // the backend renders for a specific sign order, so a check-in code, an
3325
+ // install link, or a table-ordering code was unbuildable. It draws
3326
+ // through the already-vetted react-native-svg and carries no native
3327
+ // module of its own, so it is host-shimmed on web and pinned in the
3328
+ // export like date-fns. No existing entry changed shape.
3329
+ version: "1.81.0",
3305
3330
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
3306
3331
  hooks: HOOKS,
3307
3332
  primitives: PRIMITIVES,
package/dist/dev-shims.js CHANGED
@@ -94,9 +94,12 @@ export const REACT_JSX_RUNTIME_EXPORTS = ["jsx", "jsxs", "Fragment", "jsxDEV"];
94
94
  // host namespace (sc-1064).
95
95
  // * `@colixsystems/widget-sdk` — the widget must route hooks through the SAME
96
96
  // SDK instance the host built (datastore/theme/navigation live there).
97
- // * `lucide-react-native`, `react-native-svg`, `date-fns` — vetted packages
98
- // the host already bundles + shims (`_hostVettedModules`); sharing the
99
- // host copy keeps icons/SVG on the host's react-native-web instance.
97
+ // * `lucide-react-native`, `react-native-svg`, `date-fns`,
98
+ // `react-native-qrcode-svg` — vetted packages the host already bundles +
99
+ // shims (`_hostVettedModules`); sharing the host copy keeps icons/SVG on
100
+ // the host's react-native-web instance. qrcode-svg draws through
101
+ // react-native-svg, so externalising it keeps the QR on that same
102
+ // instance instead of a bundle inlining a second one (sc-6105).
100
103
  const HOST_EXTERNAL_SPECIFIERS = [
101
104
  "react",
102
105
  "react/jsx-runtime",
@@ -107,6 +110,7 @@ const HOST_EXTERNAL_SPECIFIERS = [
107
110
  "lucide-react-native",
108
111
  "react-native-svg",
109
112
  "date-fns",
113
+ "react-native-qrcode-svg",
110
114
  // sc-1265: a bundled widget (and any vetted RN package it inlines, e.g.
111
115
  // react-native-gesture-handler) imports `react-native`; the host resolves it
112
116
  // to its OWN single react-native-web instance through a blob shim, so the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.105.0",
3
+ "version": "0.107.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",
@@ -48,7 +48,7 @@
48
48
  ],
49
49
  "scripts": {
50
50
  "build": "node scripts/build.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-invites.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-filestore-file.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-payments.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-section-empty.test.js src/__tests__/hooks-widget-event.test.js src/__tests__/hooks-widget-input.test.js src/__tests__/hooks-identification.test.js src/__tests__/hooks-subscription.test.js src/__tests__/hooks-volatile-query-key.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-translation-api.test.js src/__tests__/linter-page-url.test.js src/__tests__/linter-image-height.test.js src/__tests__/linter-measured-padding.test.js src/__tests__/linter-payment-error.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__/hooks-translate.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 src/__tests__/corner-radius.test.js src/__tests__/theme-components-parity.test.js src/__tests__/navigation-parity.test.js src/__tests__/theme-depth-tokens.test.js src/__tests__/toast-host.test.js src/__tests__/hooks-domain-error-mapping.test.js src/__tests__/linter-datastore-error.test.js src/__tests__/linter-write-gating.test.js src/__tests__/hooks-speech-to-text.test.js src/__tests__/hooks-bound-columns.test.js src/__tests__/hooks-stable-query.test.js src/__tests__/hooks-can-write.test.js src/__tests__/widget-route.test.js"
51
+ "test": "node --test src/__tests__/contract.test.js src/__tests__/vetted-imports-audit.test.js src/__tests__/hooks-users.test.js src/__tests__/hooks-groups.test.js src/__tests__/hooks-invites.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-filestore-file.test.js src/__tests__/hooks-mutation.test.js src/__tests__/hooks-payments.test.js src/__tests__/hooks-record-permissions.test.js src/__tests__/hooks-geolocation.test.js src/__tests__/hooks-section-empty.test.js src/__tests__/hooks-widget-event.test.js src/__tests__/hooks-widget-input.test.js src/__tests__/hooks-identification.test.js src/__tests__/hooks-subscription.test.js src/__tests__/hooks-volatile-query-key.test.js src/__tests__/linter-users-scope.test.js src/__tests__/linter-comments.test.js src/__tests__/linter-translation-api.test.js src/__tests__/linter-page-url.test.js src/__tests__/linter-image-height.test.js src/__tests__/linter-measured-padding.test.js src/__tests__/linter-payment-error.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__/hooks-translate.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 src/__tests__/corner-radius.test.js src/__tests__/theme-components-parity.test.js src/__tests__/navigation-parity.test.js src/__tests__/theme-depth-tokens.test.js src/__tests__/toast-host.test.js src/__tests__/hooks-domain-error-mapping.test.js src/__tests__/linter-datastore-error.test.js src/__tests__/linter-write-gating.test.js src/__tests__/hooks-speech-to-text.test.js src/__tests__/hooks-bound-columns.test.js src/__tests__/hooks-stable-query.test.js src/__tests__/hooks-can-write.test.js src/__tests__/widget-route.test.js"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=18"
package/src/dev-shims.js CHANGED
@@ -94,9 +94,12 @@ export const REACT_JSX_RUNTIME_EXPORTS = ["jsx", "jsxs", "Fragment", "jsxDEV"];
94
94
  // host namespace (sc-1064).
95
95
  // * `@colixsystems/widget-sdk` — the widget must route hooks through the SAME
96
96
  // SDK instance the host built (datastore/theme/navigation live there).
97
- // * `lucide-react-native`, `react-native-svg`, `date-fns` — vetted packages
98
- // the host already bundles + shims (`_hostVettedModules`); sharing the
99
- // host copy keeps icons/SVG on the host's react-native-web instance.
97
+ // * `lucide-react-native`, `react-native-svg`, `date-fns`,
98
+ // `react-native-qrcode-svg` — vetted packages the host already bundles +
99
+ // shims (`_hostVettedModules`); sharing the host copy keeps icons/SVG on
100
+ // the host's react-native-web instance. qrcode-svg draws through
101
+ // react-native-svg, so externalising it keeps the QR on that same
102
+ // instance instead of a bundle inlining a second one (sc-6105).
100
103
  const HOST_EXTERNAL_SPECIFIERS = [
101
104
  "react",
102
105
  "react/jsx-runtime",
@@ -107,6 +110,7 @@ const HOST_EXTERNAL_SPECIFIERS = [
107
110
  "lucide-react-native",
108
111
  "react-native-svg",
109
112
  "date-fns",
113
+ "react-native-qrcode-svg",
110
114
  // sc-1265: a bundled widget (and any vetted RN package it inlines, e.g.
111
115
  // react-native-gesture-handler) imports `react-native`; the host resolves it
112
116
  // to its OWN single react-native-web instance through a blob shim, so the