@pebble-software/bonsai 0.0.6 → 0.0.7

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/index.d.ts CHANGED
@@ -156,7 +156,29 @@ export declare const EXTRACTOR_ALLOWLIST: Record<string, (eventOrValue: unknown)
156
156
  */
157
157
  export declare type JsonSchema = Record<string, unknown>;
158
158
 
159
- export declare function jsonSchemaToZod(schema: JsonSchema): z.ZodType<unknown>;
159
+ export declare function jsonSchemaToZod(schema: JsonSchema, options?: JsonSchemaToZodOptions): z.ZodType<unknown>;
160
+
161
+ declare interface JsonSchemaToZodOptions {
162
+ /**
163
+ * Accept a `"{{expression}}"` binding anywhere a typed value is declared.
164
+ *
165
+ * A template author writes `checked: "{{form.agree}}"` and the binding is resolved at
166
+ * render time, long after validation runs — so a schema saying `{"type":"boolean"}`
167
+ * rejects a template that would render perfectly. The hand-rolled registry has always
168
+ * worked around this by declaring `z.union([z.string(), z.boolean()])` by hand on every
169
+ * affected prop. This is that convention made available to manifests whose schemas were
170
+ * machine-derived from component sources and so cannot hand-write it.
171
+ *
172
+ * Opt-in rather than automatic: `bonsai.generated.json` doubles as an LLM tool
173
+ * definition, and a prop that reads "boolean" there is worth keeping precise.
174
+ *
175
+ * Narrower than the hand-rolled convention on purpose — only binding-*shaped* strings
176
+ * are admitted, not every string. `{"type":"string","minLength":1}` therefore still
177
+ * rejects `""`, and an `enum` still rejects a value outside it, both of which a blanket
178
+ * `z.string()` union would silently discard.
179
+ */
180
+ allowBindings?: boolean;
181
+ }
160
182
 
161
183
  export declare interface LoadManifestOptions extends ResolveManifestOptions {
162
184
  allowedOrigins?: string[];
package/dist/index.js CHANGED
@@ -3825,10 +3825,10 @@ const Mt = {
3825
3825
  extractor: (e) => e
3826
3826
  }
3827
3827
  };
3828
- function Yi(e) {
3828
+ function Ki(e) {
3829
3829
  return e;
3830
3830
  }
3831
- function Ki(e) {
3831
+ function Zi(e) {
3832
3832
  return e;
3833
3833
  }
3834
3834
  const Bo = ["bindings", "formField", "ssr", "pdf"], jo = ["children", "bindings", "ssr", "pdf"];
@@ -4608,7 +4608,7 @@ function ii({
4608
4608
  }, l = te(a, 0, "root", void 0, void 0, "root-0");
4609
4609
  return /* @__PURE__ */ d(ie.Provider, { value: n ?? Ce, children: r === "pdf" ? /* @__PURE__ */ d("div", { "data-mode": r, className: "group bonsai-root w-full", children: l }) : /* @__PURE__ */ d("form", { "data-mode": r, className: "group bonsai-root", onSubmit: s, children: l }) });
4610
4610
  }
4611
- function Zi(e) {
4611
+ function Qi(e) {
4612
4612
  return typeof window > "u" ? /* @__PURE__ */ d(oi, { ...e }) : /* @__PURE__ */ d(si, { ...e });
4613
4613
  }
4614
4614
  function si(e) {
@@ -4682,7 +4682,7 @@ function xe(e, a, r, n, o) {
4682
4682
  (p, m) => xe(p, `${a}.children[${m}]`, r, n, o)
4683
4683
  ));
4684
4684
  }
4685
- function Qi(e, a, r = {}) {
4685
+ function es(e, a, r = {}) {
4686
4686
  const n = [], o = [];
4687
4687
  if (r.pin) {
4688
4688
  const s = di(r.pin, a);
@@ -4692,58 +4692,62 @@ function Qi(e, a, r = {}) {
4692
4692
  }
4693
4693
  return xe(e, "root", a, r.mode ?? "web", n), { valid: n.length === 0, errors: n, warnings: o };
4694
4694
  }
4695
- function ve(e) {
4695
+ const ui = t.string().refine((e) => e.startsWith("{{") && e.endsWith("}}"), {
4696
+ message: 'Expected a value of the declared type, or a "{{binding}}" expression'
4697
+ });
4698
+ function ve(e, a = {}) {
4699
+ const r = (o) => a.allowBindings ? t.union([ui, o]) : o;
4696
4700
  if ("const" in e)
4697
- return t.literal(e.const);
4701
+ return r(t.literal(e.const));
4698
4702
  if (Array.isArray(e.enum)) {
4699
- const r = e.enum;
4700
- if (r.length === 0)
4703
+ const o = e.enum;
4704
+ if (o.length === 0)
4701
4705
  throw new Error("jsonSchemaToZod: enum must have at least one value");
4702
- const [n, ...o] = r;
4703
- return o.reduce((s, l) => t.union([s, t.literal(l)]), t.literal(n));
4706
+ const [s, ...l] = o;
4707
+ return r(l.reduce((u, p) => t.union([u, t.literal(p)]), t.literal(s)));
4704
4708
  }
4705
- const a = e.type;
4706
- switch (a) {
4709
+ const n = e.type;
4710
+ switch (n) {
4707
4711
  case "string": {
4708
- let r = t.string();
4709
- return typeof e.minLength == "number" && (r = r.min(e.minLength)), typeof e.maxLength == "number" && (r = r.max(e.maxLength)), typeof e.pattern == "string" && (r = r.regex(new RegExp(e.pattern))), r;
4712
+ let o = t.string();
4713
+ return typeof e.minLength == "number" && (o = o.min(e.minLength)), typeof e.maxLength == "number" && (o = o.max(e.maxLength)), typeof e.pattern == "string" && (o = o.regex(new RegExp(e.pattern))), r(o);
4710
4714
  }
4711
4715
  case "number":
4712
4716
  case "integer": {
4713
- let r = t.number();
4714
- return a === "integer" && (r = r.int()), typeof e.minimum == "number" && (r = r.min(e.minimum)), typeof e.maximum == "number" && (r = r.max(e.maximum)), r;
4717
+ let o = t.number();
4718
+ return n === "integer" && (o = o.int()), typeof e.minimum == "number" && (o = o.min(e.minimum)), typeof e.maximum == "number" && (o = o.max(e.maximum)), r(o);
4715
4719
  }
4716
4720
  case "boolean":
4717
- return t.boolean();
4721
+ return r(t.boolean());
4718
4722
  case "array": {
4719
- const r = e.items ? ve(e.items) : t.unknown();
4720
- return t.array(r);
4723
+ const o = e.items ? ve(e.items, a) : t.unknown();
4724
+ return r(t.array(o));
4721
4725
  }
4722
4726
  case "object": {
4723
- const r = e.properties ?? {}, n = new Set(e.required ?? []), o = {};
4724
- for (const [l, u] of Object.entries(r)) {
4725
- const p = ve(u);
4726
- o[l] = n.has(l) ? p : p.optional();
4727
+ const o = e.properties ?? {}, s = new Set(e.required ?? []), l = {};
4728
+ for (const [p, m] of Object.entries(o)) {
4729
+ const x = ve(m, a);
4730
+ l[p] = s.has(p) ? x : x.optional();
4727
4731
  }
4728
- let s = t.object(o);
4729
- return e.additionalProperties === !1 && (s = s.strict()), s;
4732
+ let u = t.object(l);
4733
+ return e.additionalProperties === !1 && (u = u.strict()), u;
4730
4734
  }
4731
4735
  default:
4732
4736
  return t.unknown();
4733
4737
  }
4734
4738
  }
4735
- const ui = t.object({
4739
+ const pi = t.object({
4736
4740
  valueProp: t.string().min(1),
4737
4741
  changeProp: t.string().min(1),
4738
4742
  extract: t.string().min(1)
4739
- }).strict(), pi = t.object({
4743
+ }).strict(), mi = t.object({
4740
4744
  name: t.string().min(1),
4741
4745
  canonicalName: t.string().min(1).optional(),
4742
4746
  // Case A: a key resolved against `base`'s exports, never a module path to import.
4743
4747
  entry: t.string().min(1),
4744
4748
  propsSchema: t.record(t.string(), t.unknown()).optional(),
4745
4749
  capabilities: t.array(t.enum(["children", "bindings", "events", "formField", "ssr", "pdf"])).optional(),
4746
- controlled: ui.optional(),
4750
+ controlled: pi.optional(),
4747
4751
  css: t.array(t.string()).optional()
4748
4752
  }).strict(), Aa = t.object({
4749
4753
  $schema: t.string().optional(),
@@ -4754,12 +4758,12 @@ const ui = t.object({
4754
4758
  name: t.literal("bonsai"),
4755
4759
  api: t.string().min(1)
4756
4760
  }).strict(),
4757
- components: t.array(pi).min(1),
4761
+ components: t.array(mi).min(1),
4758
4762
  integrity: t.object({
4759
4763
  algorithm: t.literal("sha256"),
4760
4764
  manifestHash: t.string().min(1)
4761
4765
  }).strict().optional()
4762
- }).strict(), mi = "1", Yt = {
4766
+ }).strict(), gi = "1", Yt = {
4763
4767
  identity: (e) => e,
4764
4768
  targetValue: (e) => e?.target?.value,
4765
4769
  targetChecked: (e) => e?.target?.checked,
@@ -4770,7 +4774,7 @@ class H extends Error {
4770
4774
  super(a), this.name = "ManifestLoadError";
4771
4775
  }
4772
4776
  }
4773
- function gi(e, a) {
4777
+ function hi(e, a) {
4774
4778
  return e ? a ? t.any().superRefine((r, n) => {
4775
4779
  const o = e.safeParse(r);
4776
4780
  if (!o.success)
@@ -4782,11 +4786,11 @@ function gi(e, a) {
4782
4786
  n.addIssue({ code: "custom", message: l.message, path: l.path });
4783
4787
  }) : e : a;
4784
4788
  }
4785
- async function hi(e) {
4789
+ async function fi(e) {
4786
4790
  const a = new TextEncoder().encode(e), r = await crypto.subtle.digest("SHA-256", a);
4787
4791
  return `sha256:${Array.from(new Uint8Array(r)).map((o) => o.toString(16).padStart(2, "0")).join("")}`;
4788
4792
  }
4789
- function fi(e) {
4793
+ function bi(e) {
4790
4794
  const a = Yt[e];
4791
4795
  if (!a)
4792
4796
  throw new H(
@@ -4794,7 +4798,7 @@ function fi(e) {
4794
4798
  );
4795
4799
  return a;
4796
4800
  }
4797
- function bi(e, a, r) {
4801
+ function xi(e, a, r) {
4798
4802
  const n = a.components[e.entry];
4799
4803
  if (!n)
4800
4804
  throw new H(
@@ -4803,21 +4807,21 @@ function bi(e, a, r) {
4803
4807
  const o = e.propsSchema ? ve(e.propsSchema) : void 0, s = e.controlled ? {
4804
4808
  valueProp: e.controlled.valueProp,
4805
4809
  changeProp: e.controlled.changeProp,
4806
- extract: fi(e.controlled.extract)
4810
+ extract: bi(e.controlled.extract)
4807
4811
  } : n.controlled;
4808
4812
  return [
4809
4813
  e.canonicalName ?? e.name,
4810
4814
  {
4811
4815
  component: n.component,
4812
- propsSchema: gi(n.propsSchema, o),
4816
+ propsSchema: hi(n.propsSchema, o),
4813
4817
  capabilities: e.capabilities ?? n.capabilities,
4814
4818
  controlled: s,
4815
4819
  assets: e.css ? { css: e.css } : n.assets
4816
4820
  }
4817
4821
  ];
4818
4822
  }
4819
- function xi(e, a) {
4820
- const r = Aa.parse(e), n = a.engineApi ?? mi;
4823
+ function vi(e, a) {
4824
+ const r = Aa.parse(e), n = a.engineApi ?? gi;
4821
4825
  if (r.engine.api !== n)
4822
4826
  throw new H(
4823
4827
  `Registry manifest "${r.id}" targets engine.api "${r.engine.api}", but this build supports "${n}"`
@@ -4833,7 +4837,7 @@ function xi(e, a) {
4833
4837
  `Registry manifest "${r.id}" is ${a.rawManifestSize} bytes, exceeding the limit of ${s}`
4834
4838
  );
4835
4839
  const l = Object.fromEntries(
4836
- r.components.map((u) => bi(u, a.base, r.id))
4840
+ r.components.map((u) => xi(u, a.base, r.id))
4837
4841
  );
4838
4842
  return {
4839
4843
  id: r.id,
@@ -4845,10 +4849,10 @@ function xi(e, a) {
4845
4849
  };
4846
4850
  }
4847
4851
  const ye = /* @__PURE__ */ new Map();
4848
- function es() {
4852
+ function ts() {
4849
4853
  ye.clear();
4850
4854
  }
4851
- function vi(e, a) {
4855
+ function yi(e, a) {
4852
4856
  if (!e.integrity)
4853
4857
  throw new H(
4854
4858
  `Registry manifest "${e.id}" has no integrity block — a manifest hash is required before use`
@@ -4858,7 +4862,7 @@ function vi(e, a) {
4858
4862
  `Registry manifest "${e.id}" failed its integrity check: expected "${e.integrity.manifestHash}", computed "${a}"`
4859
4863
  );
4860
4864
  }
4861
- async function ts(e, a) {
4865
+ async function as(e, a) {
4862
4866
  let r;
4863
4867
  if (typeof e == "string") {
4864
4868
  const m = new URL(e);
@@ -4874,18 +4878,18 @@ async function ts(e, a) {
4874
4878
  r = await v.text();
4875
4879
  } else
4876
4880
  r = JSON.stringify(e);
4877
- const n = Aa.parse(JSON.parse(r)), { integrity: o, ...s } = n, l = await hi(JSON.stringify(s));
4878
- vi(n, l);
4881
+ const n = Aa.parse(JSON.parse(r)), { integrity: o, ...s } = n, l = await fi(JSON.stringify(s));
4882
+ yi(n, l);
4879
4883
  const u = ye.get(l);
4880
4884
  if (u) return u;
4881
- const p = xi(n, { ...a, rawManifestSize: r.length, manifestHash: l });
4885
+ const p = vi(n, { ...a, rawManifestSize: r.length, manifestHash: l });
4882
4886
  return ye.set(l, p), p;
4883
4887
  }
4884
- function as(e, a) {
4888
+ function rs(e, a) {
4885
4889
  const r = { ...e?.properties ?? {}, ...a?.properties ?? {} }, n = [.../* @__PURE__ */ new Set([...e?.required ?? [], ...a?.required ?? []])];
4886
4890
  return { type: "object", properties: r, required: n };
4887
4891
  }
4888
- function yi(e, a) {
4892
+ function wi(e, a) {
4889
4893
  const r = new Map(e.map((s) => [s.url, s.registryHash])), n = new Map(a.map((s) => [s.url, s.registryHash])), o = [];
4890
4894
  for (const [s, l] of n) {
4891
4895
  const u = r.get(s);
@@ -4899,8 +4903,8 @@ function Kt(e) {
4899
4903
  const a = /(\d+)\.\d+\.\d+/.exec(e);
4900
4904
  return a ? Number(a[1]) : void 0;
4901
4905
  }
4902
- function rs(e) {
4903
- const a = [], r = yi(e.artifactRegistries, e.configRegistries);
4906
+ function ns(e) {
4907
+ const a = [], r = wi(e.artifactRegistries, e.configRegistries);
4904
4908
  if (r && a.push({
4905
4909
  check: "stale-artifact",
4906
4910
  level: "fail",
@@ -4915,31 +4919,31 @@ function rs(e) {
4915
4919
  }
4916
4920
  return a;
4917
4921
  }
4918
- function ns(e) {
4922
+ function os(e) {
4919
4923
  const a = e.filter((r) => r.level === "fail");
4920
4924
  if (a.length > 0)
4921
4925
  throw new Error(a.map((r) => r.message).join("; "));
4922
4926
  }
4923
4927
  export {
4924
- Zi as Bonsai,
4928
+ Qi as Bonsai,
4925
4929
  Yt as EXTRACTOR_ALLOWLIST,
4926
4930
  H as ManifestLoadError,
4927
4931
  Aa as RegistryManifestSchema,
4928
- ns as assertNoFailingDrift,
4929
- rs as checkArtifactDrift,
4930
- es as clearManifestCache,
4932
+ os as assertNoFailingDrift,
4933
+ ns as checkArtifactDrift,
4934
+ ts as clearManifestCache,
4931
4935
  Mt as componentMap,
4932
- gi as composePropsSchema,
4933
- hi as computeManifestHash,
4936
+ hi as composePropsSchema,
4937
+ fi as computeManifestHash,
4934
4938
  Uo as controlledInputMap,
4935
- Ki as defineBonsaiRegistry,
4936
- Yi as defineComponent,
4939
+ Zi as defineBonsaiRegistry,
4940
+ Ki as defineComponent,
4937
4941
  ve as jsonSchemaToZod,
4938
- ts as loadRegistryManifest,
4939
- as as mergeGeneratedWithOverrides,
4942
+ as as loadRegistryManifest,
4943
+ rs as mergeGeneratedWithOverrides,
4940
4944
  Ce as pebbleRegistry,
4941
- xi as resolveManifest,
4945
+ vi as resolveManifest,
4942
4946
  di as resolveRegistryPin,
4943
4947
  Ko as useUpdateData,
4944
- Qi as validateTemplate
4948
+ es as validateTemplate
4945
4949
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pebble-software/bonsai",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/PebbleSoftwareInc/pebble-bonsai.git"