@colixsystems/widget-sdk 0.117.0 → 0.118.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/dist/host.d.ts CHANGED
@@ -59,10 +59,14 @@ export function normaliseWidgetStyleFields(
59
59
  export function normaliseWidgetStyles(raw: unknown): ThemeWidgetStyles;
60
60
 
61
61
  /**
62
- * REQ-THEME-15 host render-boundary helper: folds the theme's per-component
63
- * tokens into a widget's props as `style` DEFAULTS, with the author's
64
- * per-instance values winning. Returns the same `props` reference when the theme
65
- * sets nothing for this widget. Applied by the platform hosts, never by authors.
62
+ * REQ-THEME-15 host render-boundary helper: folds the widget's declared
63
+ * `styleSchema` defaults and the theme's per-component tokens into a widget's
64
+ * props as `style` DEFAULTS, with the author's per-instance values winning.
65
+ *
66
+ * sc-6750 — precedence, weakest first: `styleSchema` default ->
67
+ * palette/`components.<scope>` -> `widgetStyles[manifestId]` -> per-instance
68
+ * `props.style`. Returns the same `props` reference when neither the theme nor
69
+ * the schema sets anything. Applied by the platform hosts, never by authors.
66
70
  */
67
71
  export function applyThemeComponentStyle<T = Record<string, unknown>>(
68
72
  manifestId: string,
package/dist/linter.js CHANGED
@@ -56,152 +56,9 @@ const CONTRACT_RULES = CONTRACT.bannedApis.map((b) =>
56
56
  _ruleForIdentifier(b.identifier, b.reason),
57
57
  );
58
58
 
59
- // Replace the *content* of comments and string / template literals with
60
- // spaces so the banned-identifier scan only ever sees executable code. A
61
- // banned host-escape identifier (`window`, `document`, `eval`, `process`, …)
62
- // is only dangerous as a real identifier reference — never as prose in a
63
- // `//` comment or as character data inside a string — so matching the bare
64
- // word there is a false positive that blocks an otherwise-clean widget (a
65
- // comment that reads "the hour window the grid renders" must not trip
66
- // `no-window`).
67
- //
68
- // Newlines are preserved verbatim so reported line numbers still line up
69
- // with the original source. Template-literal `${ … }` expression holes are
70
- // left intact: real code lives there and must still be scanned (`${window}`
71
- // is a genuine escape). Backslash escapes inside strings/templates are
72
- // consumed so an escaped quote (`"\""`) doesn't end the literal early.
73
- function _stripNonCode(source, { keepStrings = false } = {}) {
74
- let out = "";
75
- const n = source.length;
76
- let mode = "code"; // code | line | block | sq | dq | tmpl
77
- // Brace depth, plus a stack of the depths at which an enclosing template
78
- // literal resumes — lets a `${ … }` hole (which may itself contain `{}`,
79
- // strings, or nested templates) be told apart from the literal text.
80
- let braceDepth = 0;
81
- const tmplStack = [];
82
- const keep = (ch) => {
83
- out += ch;
84
- };
85
- const blank = (ch) => {
86
- out += ch === "\n" || ch === "\r" ? ch : " ";
87
- };
88
- // String / template CONTENT: blanked for the banned-identifier scan
89
- // (prose must not trip `no-window`), kept for the host-API-URL scan,
90
- // whose whole job is to find a URL literal.
91
- const str = keepStrings ? keep : blank;
92
- let i = 0;
93
- while (i < n) {
94
- const ch = source[i];
95
- const nx = source[i + 1];
96
- if (mode === "code") {
97
- if (ch === "/" && nx === "/") {
98
- mode = "line";
99
- blank(ch);
100
- blank(nx);
101
- i += 2;
102
- } else if (ch === "/" && nx === "*") {
103
- mode = "block";
104
- blank(ch);
105
- blank(nx);
106
- i += 2;
107
- } else if (ch === "'") {
108
- mode = "sq";
109
- str(ch);
110
- i += 1;
111
- } else if (ch === '"') {
112
- mode = "dq";
113
- str(ch);
114
- i += 1;
115
- } else if (ch === "`") {
116
- mode = "tmpl";
117
- str(ch);
118
- i += 1;
119
- } else if (ch === "{") {
120
- braceDepth += 1;
121
- keep(ch);
122
- i += 1;
123
- } else if (ch === "}") {
124
- braceDepth -= 1;
125
- if (
126
- tmplStack.length > 0 &&
127
- tmplStack[tmplStack.length - 1] === braceDepth
128
- ) {
129
- tmplStack.pop();
130
- mode = "tmpl";
131
- str(ch);
132
- } else {
133
- keep(ch);
134
- }
135
- i += 1;
136
- } else {
137
- keep(ch);
138
- i += 1;
139
- }
140
- } else if (mode === "line") {
141
- if (ch === "\n") {
142
- mode = "code";
143
- keep(ch);
144
- } else {
145
- blank(ch);
146
- }
147
- i += 1;
148
- } else if (mode === "block") {
149
- if (ch === "*" && nx === "/") {
150
- mode = "code";
151
- blank(ch);
152
- blank(nx);
153
- i += 2;
154
- } else {
155
- blank(ch);
156
- i += 1;
157
- }
158
- } else if (mode === "sq" || mode === "dq") {
159
- const quote = mode === "sq" ? "'" : '"';
160
- if (ch === "\\") {
161
- str(ch);
162
- if (i + 1 < n) str(nx);
163
- i += 2;
164
- } else if (ch === quote) {
165
- mode = "code";
166
- str(ch);
167
- i += 1;
168
- } else if (ch === "\n") {
169
- // A bare newline terminates an unterminated string in JS; bail back
170
- // to code so malformed input can't blank the rest of the file.
171
- mode = "code";
172
- keep(ch);
173
- i += 1;
174
- } else {
175
- str(ch);
176
- i += 1;
177
- }
178
- } else {
179
- // mode === "tmpl"
180
- if (ch === "\\") {
181
- str(ch);
182
- if (i + 1 < n) str(nx);
183
- i += 2;
184
- } else if (ch === "`") {
185
- mode = "code";
186
- str(ch);
187
- i += 1;
188
- } else if (ch === "$" && nx === "{") {
189
- // Enter an expression hole. Remember the brace depth the template
190
- // resumes at, then count the `{` so its matching `}` is recognised.
191
- tmplStack.push(braceDepth);
192
- braceDepth += 1;
193
- mode = "code";
194
- keep(ch);
195
- keep(nx);
196
- i += 2;
197
- } else {
198
- str(ch);
199
- i += 1;
200
- }
201
- }
202
- }
203
- return out;
204
- }
59
+ // sc-6086: moved to source-mask.js so the packer's scanner masks source the
60
+ // same way this linter does. Alias kept the rules below read _stripNonCode.
61
+ import { stripNonCode as _stripNonCode } from "./source-mask.js";
205
62
 
206
63
  // Extra rules that don't map 1:1 to a banned identifier in the contract:
207
64
  // host-internal imports that widgets must never touch.
@@ -0,0 +1,162 @@
1
+ // sc-6086: the shared source masker, extracted verbatim from linter.js so the
2
+ // linter, the packer's import scanner, and the dev-server guard all agree on
3
+ // what is code and what is a comment or string literal (CLAUDE.md §3).
4
+ //
5
+ // Length-preserving: every blanked character becomes a space and newlines are
6
+ // kept, so an index into the mask is the SAME index in the original source.
7
+ // That is what lets a caller match against the mask and splice the original.
8
+
9
+ // Replace the *content* of comments and string / template literals with
10
+ // spaces so the banned-identifier scan only ever sees executable code. A
11
+ // banned host-escape identifier (`window`, `document`, `eval`, `process`, …)
12
+ // is only dangerous as a real identifier reference — never as prose in a
13
+ // `//` comment or as character data inside a string — so matching the bare
14
+ // word there is a false positive that blocks an otherwise-clean widget (a
15
+ // comment that reads "the hour window the grid renders" must not trip
16
+ // `no-window`).
17
+ //
18
+ // Newlines are preserved verbatim so reported line numbers still line up
19
+ // with the original source. Template-literal `${ … }` expression holes are
20
+ // left intact: real code lives there and must still be scanned (`${window}`
21
+ // is a genuine escape). Backslash escapes inside strings/templates are
22
+ // consumed so an escaped quote (`"\""`) doesn't end the literal early.
23
+ export function stripNonCode(
24
+ source,
25
+ { keepStrings = false, keepTemplates = keepStrings } = {},
26
+ ) {
27
+ let out = "";
28
+ const n = source.length;
29
+ let mode = "code"; // code | line | block | sq | dq | tmpl
30
+ // Brace depth, plus a stack of the depths at which an enclosing template
31
+ // literal resumes — lets a `${ … }` hole (which may itself contain `{}`,
32
+ // strings, or nested templates) be told apart from the literal text.
33
+ let braceDepth = 0;
34
+ const tmplStack = [];
35
+ const keep = (ch) => {
36
+ out += ch;
37
+ };
38
+ const blank = (ch) => {
39
+ out += ch === "\n" || ch === "\r" ? ch : " ";
40
+ };
41
+ // String / template CONTENT: blanked for the banned-identifier scan
42
+ // (prose must not trip `no-window`), kept for the host-API-URL scan,
43
+ // whose whole job is to find a URL literal.
44
+ const str = keepStrings ? keep : blank;
45
+ // An import specifier is always a quoted string, never a template literal —
46
+ // so the import scanner keeps quoted content and blanks templates, which is
47
+ // what stops an `import … from "./x"` line inside a backtick block from
48
+ // reading as a real statement.
49
+ const tmplStr = keepTemplates ? keep : blank;
50
+ let i = 0;
51
+ while (i < n) {
52
+ const ch = source[i];
53
+ const nx = source[i + 1];
54
+ if (mode === "code") {
55
+ if (ch === "/" && nx === "/") {
56
+ mode = "line";
57
+ blank(ch);
58
+ blank(nx);
59
+ i += 2;
60
+ } else if (ch === "/" && nx === "*") {
61
+ mode = "block";
62
+ blank(ch);
63
+ blank(nx);
64
+ i += 2;
65
+ } else if (ch === "'") {
66
+ mode = "sq";
67
+ str(ch);
68
+ i += 1;
69
+ } else if (ch === '"') {
70
+ mode = "dq";
71
+ str(ch);
72
+ i += 1;
73
+ } else if (ch === "`") {
74
+ mode = "tmpl";
75
+ tmplStr(ch);
76
+ i += 1;
77
+ } else if (ch === "{") {
78
+ braceDepth += 1;
79
+ keep(ch);
80
+ i += 1;
81
+ } else if (ch === "}") {
82
+ braceDepth -= 1;
83
+ if (
84
+ tmplStack.length > 0 &&
85
+ tmplStack[tmplStack.length - 1] === braceDepth
86
+ ) {
87
+ tmplStack.pop();
88
+ mode = "tmpl";
89
+ str(ch);
90
+ } else {
91
+ keep(ch);
92
+ }
93
+ i += 1;
94
+ } else {
95
+ keep(ch);
96
+ i += 1;
97
+ }
98
+ } else if (mode === "line") {
99
+ if (ch === "\n") {
100
+ mode = "code";
101
+ keep(ch);
102
+ } else {
103
+ blank(ch);
104
+ }
105
+ i += 1;
106
+ } else if (mode === "block") {
107
+ if (ch === "*" && nx === "/") {
108
+ mode = "code";
109
+ blank(ch);
110
+ blank(nx);
111
+ i += 2;
112
+ } else {
113
+ blank(ch);
114
+ i += 1;
115
+ }
116
+ } else if (mode === "sq" || mode === "dq") {
117
+ const quote = mode === "sq" ? "'" : '"';
118
+ if (ch === "\\") {
119
+ str(ch);
120
+ if (i + 1 < n) str(nx);
121
+ i += 2;
122
+ } else if (ch === quote) {
123
+ mode = "code";
124
+ str(ch);
125
+ i += 1;
126
+ } else if (ch === "\n") {
127
+ // A bare newline terminates an unterminated string in JS; bail back
128
+ // to code so malformed input can't blank the rest of the file.
129
+ mode = "code";
130
+ keep(ch);
131
+ i += 1;
132
+ } else {
133
+ str(ch);
134
+ i += 1;
135
+ }
136
+ } else {
137
+ // mode === "tmpl"
138
+ if (ch === "\\") {
139
+ tmplStr(ch);
140
+ if (i + 1 < n) tmplStr(nx);
141
+ i += 2;
142
+ } else if (ch === "`") {
143
+ mode = "code";
144
+ tmplStr(ch);
145
+ i += 1;
146
+ } else if (ch === "$" && nx === "{") {
147
+ // Enter an expression hole. Remember the brace depth the template
148
+ // resumes at, then count the `{` so its matching `}` is recognised.
149
+ tmplStack.push(braceDepth);
150
+ braceDepth += 1;
151
+ mode = "code";
152
+ keep(ch);
153
+ keep(nx);
154
+ i += 2;
155
+ } else {
156
+ tmplStr(ch);
157
+ i += 1;
158
+ }
159
+ }
160
+ }
161
+ return out;
162
+ }
@@ -245,6 +245,51 @@ function normaliseWidgetStyles(raw) {
245
245
  return out;
246
246
  }
247
247
 
248
+ // Defaults are static manifest config; clone the structured ones so a widget
249
+ // mutating props.style can never corrupt the shared manifest default. Mirrors
250
+ // cloneDefault in property-schema.js.
251
+ function cloneStyleDefault(value) {
252
+ if (value === null || typeof value !== "object") return value;
253
+ return JSON.parse(JSON.stringify(value));
254
+ }
255
+
256
+ // sc-6750: one styleSchema leaf's declared default, with the SAME nesting
257
+ // semantics resolveLeaf (property-schema.js) gives the property side — an
258
+ // `object` def whose own default is absent still contributes its children's.
259
+ function styleLeafDefault(def) {
260
+ if (!isPlainObject(def)) return undefined;
261
+ if (def.default !== undefined) return cloneStyleDefault(def.default);
262
+ if (def.type !== "object" || !isPlainObject(def.properties)) return undefined;
263
+ const nested = {};
264
+ for (const [child, childDef] of Object.entries(def.properties)) {
265
+ if (!isUsableStyleKey(child)) continue;
266
+ const value = styleLeafDefault(childDef);
267
+ if (value !== undefined) nested[child] = value;
268
+ }
269
+ return Object.keys(nested).length > 0 ? nested : undefined;
270
+ }
271
+
272
+ /**
273
+ * sc-6750 — the widget author's OWN declared style baseline: each
274
+ * `styleSchema` leaf's `default`.
275
+ *
276
+ * The weakest layer by design, seeded beneath every theme layer: a widget's
277
+ * declared default must never out-rank the workspace theme, or every already-
278
+ * published widget would stop following the theme it follows today.
279
+ *
280
+ * @returns {Record<string, unknown>} field → declared default; `{}` when none.
281
+ */
282
+ function styleSchemaDefaults(styleSchema) {
283
+ if (!isPlainObject(styleSchema)) return {};
284
+ const out = {};
285
+ for (const [field, def] of Object.entries(styleSchema)) {
286
+ if (!isUsableStyleKey(field)) continue;
287
+ const value = styleLeafDefault(def);
288
+ if (value !== undefined) out[field] = value;
289
+ }
290
+ return out;
291
+ }
292
+
248
293
  /**
249
294
  * The per-component style fields that apply to one widget, keyed by the
250
295
  * `styleSchema` field name the widget actually reads. A widget may sit in more
@@ -255,12 +300,19 @@ function normaliseWidgetStyles(raw) {
255
300
  * before a value becomes a widget's style, and a host that folded the theme in
256
301
  * without normalising must not be able to hand a widget malformed input.
257
302
  *
258
- * @returns {Record<string, string|number>|null} null when nothing applies.
303
+ * Layered weakest-first: the widget's own `styleSchema` defaults, then the
304
+ * component scopes, then `widgetStyles[manifestId]`. The caller spreads the
305
+ * per-instance `props.style` last, so the full order is
306
+ * styleSchema default -> palette/scopes -> widgetStyles -> props.style.
307
+ *
308
+ * @returns {Record<string, unknown>|null} null when nothing applies.
259
309
  */
260
310
  function componentStyleFor(manifestId, components, styleSchema, widgetStyles) {
261
311
  if (typeof manifestId !== "string") return null;
262
312
  const validated = normaliseThemeComponents(components);
263
- const out = {};
313
+ // sc-6750: the author's declared defaults are the WEAKEST layer, so they go
314
+ // in first and every theme layer below overwrites them.
315
+ const out = styleSchemaDefaults(styleSchema);
264
316
  for (const [scope, definition] of Object.entries(CONTRACT.themeComponents)) {
265
317
  const tokens = validated[scope];
266
318
  if (!tokens) continue;
@@ -305,20 +357,28 @@ function componentStyleFor(manifestId, components, styleSchema, widgetStyles) {
305
357
  }
306
358
 
307
359
  /**
308
- * Fold the theme's per-component tokens into a widget's props as `style`
309
- * DEFAULTS. The author's per-instance REQ-THEME-13 values are spread last and
310
- * therefore always win the theme token is the app-wide baseline, the
311
- * Properties Panel is the final word.
360
+ * Fold the widget's declared `styleSchema` defaults and the theme's
361
+ * per-component tokens into a widget's props as `style` DEFAULTS. The author's
362
+ * per-instance REQ-THEME-13 values are spread last and therefore always win
363
+ * the theme token is the app-wide baseline, the Properties Panel the final word.
364
+ *
365
+ * sc-6750: a `styleSchema` leaf's own `default` is resolved here too, as the
366
+ * BOTTOM layer. It is the widget author's baseline, so a workspace theme still
367
+ * out-ranks it and a field the author never defaulted follows the theme exactly
368
+ * as before — precedence: styleSchema default -> palette/`components.<scope>`
369
+ * -> `widgetStyles[manifestId]` -> per-instance `props.style`.
312
370
  *
313
- * Returns the SAME `props` reference when the theme sets nothing for this
314
- * widget, so an unthemed app takes no extra render work and behaves exactly as
315
- * it did before REQ-THEME-15.
371
+ * Returns the SAME `props` reference when neither the theme nor the schema sets
372
+ * anything for this widget, so an unthemed app with no declared style defaults
373
+ * takes no extra render work and behaves exactly as it did before REQ-THEME-15.
316
374
  *
317
375
  * @param {string} manifestId — the widget's canonical manifest id.
318
376
  * @param {object} theme — the resolved widget theme (`workspace.theme`); its
319
377
  * `components` slice is read.
320
378
  * @param {object} props — the widget's resolved props (post-`resolveProps`).
321
- * @returns {object} props, with `style` folded when the theme applies.
379
+ * @param {object} [styleSchema] the widget's declared style fields; their
380
+ * `default`s form the bottom style layer.
381
+ * @returns {object} props, with `style` folded when anything applies.
322
382
  */
323
383
  function applyThemeComponentStyle(manifestId, theme, props, styleSchema) {
324
384
  const themed = componentStyleFor(
@@ -237,6 +237,51 @@ export function normaliseWidgetStyles(raw) {
237
237
  return out;
238
238
  }
239
239
 
240
+ // Defaults are static manifest config; clone the structured ones so a widget
241
+ // mutating props.style can never corrupt the shared manifest default. Mirrors
242
+ // cloneDefault in property-schema.js.
243
+ function cloneStyleDefault(value) {
244
+ if (value === null || typeof value !== "object") return value;
245
+ return JSON.parse(JSON.stringify(value));
246
+ }
247
+
248
+ // sc-6750: one styleSchema leaf's declared default, with the SAME nesting
249
+ // semantics resolveLeaf (property-schema.js) gives the property side — an
250
+ // `object` def whose own default is absent still contributes its children's.
251
+ function styleLeafDefault(def) {
252
+ if (!isPlainObject(def)) return undefined;
253
+ if (def.default !== undefined) return cloneStyleDefault(def.default);
254
+ if (def.type !== "object" || !isPlainObject(def.properties)) return undefined;
255
+ const nested = {};
256
+ for (const [child, childDef] of Object.entries(def.properties)) {
257
+ if (!isUsableStyleKey(child)) continue;
258
+ const value = styleLeafDefault(childDef);
259
+ if (value !== undefined) nested[child] = value;
260
+ }
261
+ return Object.keys(nested).length > 0 ? nested : undefined;
262
+ }
263
+
264
+ /**
265
+ * sc-6750 — the widget author's OWN declared style baseline: each
266
+ * `styleSchema` leaf's `default`.
267
+ *
268
+ * The weakest layer by design, seeded beneath every theme layer: a widget's
269
+ * declared default must never out-rank the workspace theme, or every already-
270
+ * published widget would stop following the theme it follows today.
271
+ *
272
+ * @returns {Record<string, unknown>} field → declared default; `{}` when none.
273
+ */
274
+ function styleSchemaDefaults(styleSchema) {
275
+ if (!isPlainObject(styleSchema)) return {};
276
+ const out = {};
277
+ for (const [field, def] of Object.entries(styleSchema)) {
278
+ if (!isUsableStyleKey(field)) continue;
279
+ const value = styleLeafDefault(def);
280
+ if (value !== undefined) out[field] = value;
281
+ }
282
+ return out;
283
+ }
284
+
240
285
  /**
241
286
  * The per-component style fields that apply to one widget, keyed by the
242
287
  * `styleSchema` field name the widget actually reads. A widget may sit in more
@@ -247,12 +292,19 @@ export function normaliseWidgetStyles(raw) {
247
292
  * before a value becomes a widget's style, and a host that folded the theme in
248
293
  * without normalising must not be able to hand a widget malformed input.
249
294
  *
250
- * @returns {Record<string, string|number>|null} null when nothing applies.
295
+ * Layered weakest-first: the widget's own `styleSchema` defaults, then the
296
+ * component scopes, then `widgetStyles[manifestId]`. The caller spreads the
297
+ * per-instance `props.style` last, so the full order is
298
+ * styleSchema default -> palette/scopes -> widgetStyles -> props.style.
299
+ *
300
+ * @returns {Record<string, unknown>|null} null when nothing applies.
251
301
  */
252
302
  function componentStyleFor(manifestId, components, styleSchema, widgetStyles) {
253
303
  if (typeof manifestId !== "string") return null;
254
304
  const validated = normaliseThemeComponents(components);
255
- const out = {};
305
+ // sc-6750: the author's declared defaults are the WEAKEST layer, so they go
306
+ // in first and every theme layer below overwrites them.
307
+ const out = styleSchemaDefaults(styleSchema);
256
308
  for (const [scope, definition] of Object.entries(CONTRACT.themeComponents)) {
257
309
  const tokens = validated[scope];
258
310
  if (!tokens) continue;
@@ -297,20 +349,28 @@ function componentStyleFor(manifestId, components, styleSchema, widgetStyles) {
297
349
  }
298
350
 
299
351
  /**
300
- * Fold the theme's per-component tokens into a widget's props as `style`
301
- * DEFAULTS. The author's per-instance REQ-THEME-13 values are spread last and
302
- * therefore always win the theme token is the app-wide baseline, the
303
- * Properties Panel is the final word.
352
+ * Fold the widget's declared `styleSchema` defaults and the theme's
353
+ * per-component tokens into a widget's props as `style` DEFAULTS. The author's
354
+ * per-instance REQ-THEME-13 values are spread last and therefore always win
355
+ * the theme token is the app-wide baseline, the Properties Panel the final word.
356
+ *
357
+ * sc-6750: a `styleSchema` leaf's own `default` is resolved here too, as the
358
+ * BOTTOM layer. It is the widget author's baseline, so a workspace theme still
359
+ * out-ranks it and a field the author never defaulted follows the theme exactly
360
+ * as before — precedence: styleSchema default -> palette/`components.<scope>`
361
+ * -> `widgetStyles[manifestId]` -> per-instance `props.style`.
304
362
  *
305
- * Returns the SAME `props` reference when the theme sets nothing for this
306
- * widget, so an unthemed app takes no extra render work and behaves exactly as
307
- * it did before REQ-THEME-15.
363
+ * Returns the SAME `props` reference when neither the theme nor the schema sets
364
+ * anything for this widget, so an unthemed app with no declared style defaults
365
+ * takes no extra render work and behaves exactly as it did before REQ-THEME-15.
308
366
  *
309
367
  * @param {string} manifestId — the widget's canonical manifest id.
310
368
  * @param {object} theme — the resolved widget theme (`workspace.theme`); its
311
369
  * `components` slice is read.
312
370
  * @param {object} props — the widget's resolved props (post-`resolveProps`).
313
- * @returns {object} props, with `style` folded when the theme applies.
371
+ * @param {object} [styleSchema] the widget's declared style fields; their
372
+ * `default`s form the bottom style layer.
373
+ * @returns {object} props, with `style` folded when anything applies.
314
374
  */
315
375
  export function applyThemeComponentStyle(manifestId, theme, props, styleSchema) {
316
376
  const themed = componentStyleFor(
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.117.0",
3
+ "version": "0.118.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
+ "homepage": "https://github.com/Colix-AB/AppStudio",
5
6
  "type": "module",
6
7
  "main": "./dist/index.js",
7
8
  "module": "./dist/index.js",
@@ -48,7 +49,7 @@
48
49
  ],
49
50
  "scripts": {
50
51
  "build": "node scripts/build.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-hardcoded-design.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__/interaction-lift.test.js src/__tests__/toast-host.test.js src/__tests__/overlay-tokens.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-camera.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
+ "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-hardcoded-design.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__/flatten-entry.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__/interaction-lift.test.js src/__tests__/toast-host.test.js src/__tests__/overlay-tokens.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-camera.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
53
  },
53
54
  "engines": {
54
55
  "node": ">=18"
@@ -83,5 +84,8 @@
83
84
  "sdk",
84
85
  "low-code"
85
86
  ],
86
- "license": "MIT"
87
+ "license": "MIT",
88
+ "devDependencies": {
89
+ "@babel/parser": "^7.29.7"
90
+ }
87
91
  }