@narrative.io/jsonforms-provider-protocols 1.1.0-beta.4 → 1.1.0-beta.6

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
@@ -158,6 +158,67 @@ Control when data is fetched:
158
158
  - `onFocus` - Load when field receives focus
159
159
  - `query` - Load when user types (autocomplete)
160
160
 
161
+ ### Derive Functionality
162
+ Auto-populate fields from form data or external sources:
163
+
164
+ ```json
165
+ {
166
+ "type": "Control",
167
+ "scope": "#/properties/derived_field",
168
+ "options": {
169
+ "derive": "country",
170
+ "mode": "follow",
171
+ "readonly": true
172
+ }
173
+ }
174
+ ```
175
+
176
+ #### External Data Support
177
+ Access external data sources separate from form data using the `externalData()` syntax:
178
+
179
+ ```vue
180
+ <script setup>
181
+ // Provide external data to the form
182
+ const externalData = ref({
183
+ user: { preferences: { theme: "dark" } },
184
+ tree: { name: "Oak", type: "Deciduous" }
185
+ })
186
+
187
+ provide('externalData', externalData)
188
+ </script>
189
+ ```
190
+
191
+ ```json
192
+ {
193
+ "type": "Control",
194
+ "scope": "#/properties/tree_preference",
195
+ "options": {
196
+ "derive": "externalData(tree.name)",
197
+ "mode": "follow",
198
+ "readonly": true
199
+ }
200
+ }
201
+ ```
202
+
203
+ ### Error Handling
204
+ Control error display behavior with the `showError` property:
205
+
206
+ ```json
207
+ {
208
+ "provider": {
209
+ "protocol": "rest_api",
210
+ "config": {
211
+ "url": "https://api.example.com/data",
212
+ "showError": false,
213
+ "items": "$.data[*]",
214
+ "map": { "label": "$.name", "value": "$.id" }
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ When `showError` is `false`, failed requests return empty results instead of throwing errors. Defaults to `true`.
221
+
161
222
  ### Composables
162
223
  Use providers directly in your components:
163
224
 
@@ -17,6 +17,7 @@ export type RestApiCfg = {
17
17
  maxPages?: number;
18
18
  };
19
19
  auth?: AuthConfig;
20
+ showError?: boolean;
20
21
  };
21
22
  export declare const RestApiProtocol: () => Protocol<RestApiCfg>;
22
23
  //# sourceMappingURL=rest_api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rest_api.d.ts","sourceRoot":"","sources":["../../src/protocols/rest_api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,EAGR,UAAU,EACX,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IACrE,QAAQ,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AA0DF,eAAO,MAAM,eAAe,QAAO,QAAQ,CAAC,UAAU,CAiEpD,CAAC"}
1
+ {"version":3,"file":"rest_api.d.ts","sourceRoot":"","sources":["../../src/protocols/rest_api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,EAGR,UAAU,EACX,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IACrE,QAAQ,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA0DF,eAAO,MAAM,eAAe,QAAO,QAAQ,CAAC,UAAU,CAuEpD,CAAC"}
@@ -73,7 +73,12 @@ const RestApiProtocol = () => ({
73
73
  );
74
74
  }
75
75
  const res = await fetch(url.toString(), requestInit);
76
- if (!res.ok) throw new Error(`REST ${res.status}`);
76
+ if (!res.ok) {
77
+ if (cfg.showError !== false) {
78
+ throw new Error(`REST ${res.status}`);
79
+ }
80
+ return { items: [], ttl: 0 };
81
+ }
77
82
  const json = await res.json();
78
83
  const items = jp(json, cfg.items);
79
84
  for (const it of items) {
@@ -1 +1 @@
1
- {"version":3,"file":"rest_api.js","sources":["../../src/protocols/rest_api.ts"],"sourcesContent":["import { jp } from \"../core/jsonpath\";\nimport { renderObj, renderTpl } from \"../core/templating\";\nimport type {\n Protocol,\n ProviderItem,\n ProviderOutput,\n AuthConfig,\n} from \"../core/types\";\n\nexport type RestApiCfg = {\n url: string;\n method?: \"GET\" | \"POST\";\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n body?: unknown;\n items: string; // JSONPath to array; e.g. \"$.items[*]\"\n map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item\n paginate?: { cursorPath: string; param: string; maxPages?: number };\n auth?: AuthConfig;\n};\n\nfunction buildAuthHeaders(\n auth?: AuthConfig,\n globalAuth?: Record<string, unknown>,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (!auth) return headers;\n\n // Handle \"use\" reference to global auth\n if (auth.use && globalAuth?.[auth.use]) {\n const globalValue = globalAuth[auth.use];\n const value =\n typeof globalValue === \"function\" ? globalValue() : globalValue;\n\n if (auth.use === \"apiKey\") {\n headers[\"X-API-Key\"] = String(value);\n } else if (auth.use === \"bearer\") {\n headers[\"Authorization\"] = `Bearer ${value}`;\n } else if (auth.use === \"token\") {\n headers[\"Authorization\"] = `Token ${value}`;\n }\n return headers;\n }\n\n // Handle direct auth values\n if (auth.apiKey) {\n const value =\n typeof auth.apiKey === \"function\" ? auth.apiKey() : auth.apiKey;\n headers[\"X-API-Key\"] = String(value);\n }\n\n if (auth.bearer) {\n const value =\n typeof auth.bearer === \"function\" ? auth.bearer() : auth.bearer;\n headers[\"Authorization\"] = `Bearer ${value}`;\n }\n\n if (auth.token) {\n const value = typeof auth.token === \"function\" ? auth.token() : auth.token;\n headers[\"Authorization\"] = `Token ${value}`;\n }\n\n // Handle custom auth fields\n for (const [key, value] of Object.entries(auth)) {\n if (\n ![\"use\", \"apiKey\", \"bearer\", \"token\"].includes(key) &&\n value !== undefined\n ) {\n const authValue = typeof value === \"function\" ? value() : value;\n headers[key] = String(authValue);\n }\n }\n\n return headers;\n}\n\nexport const RestApiProtocol = (): Protocol<RestApiCfg> => ({\n protocol: \"rest_api\",\n async resolve(cfg, ctx): Promise<ProviderOutput> {\n const ac = ctx.signal;\n const out: ProviderItem[] = [];\n let cursor: unknown = null;\n let page = 0;\n do {\n const renderedUrl = renderTpl(cfg.url, { data: ctx.data, ui: ctx.ui });\n\n // Check if URL has unresolved template variables (empty string after template rendering)\n if (cfg.url.includes(\"{{\") && renderedUrl.endsWith(\"/\")) {\n return { items: [], ttl: 0 };\n }\n\n const url = new URL(renderedUrl);\n const q = renderObj(cfg.query ?? {}, {\n data: ctx.data,\n ui: ctx.ui,\n }) as Record<string, unknown>;\n for (const [k, v] of Object.entries(q))\n if (v !== undefined && v !== \"\") url.searchParams.set(k, String(v));\n if (cursor && cfg.paginate)\n url.searchParams.set(cfg.paginate.param, String(cursor));\n\n // Build headers with auth\n const baseHeaders = renderObj(cfg.headers ?? {}, {\n data: ctx.data,\n }) as Record<string, string>;\n const authHeaders = buildAuthHeaders(cfg.auth, ctx.auth);\n const headers = { ...baseHeaders, ...authHeaders };\n\n const method = cfg.method ?? \"GET\";\n const requestInit: RequestInit = {\n method,\n headers,\n signal: ac,\n };\n\n // Only include body for non-GET requests\n if (method !== \"GET\" && cfg.body) {\n requestInit.body = JSON.stringify(\n renderObj(cfg.body, { data: ctx.data }),\n );\n }\n\n const res = await fetch(url.toString(), requestInit);\n if (!res.ok) throw new Error(`REST ${res.status}`);\n const json = await res.json();\n const items = jp(json, cfg.items);\n for (const it of items) {\n const label = jp(it, cfg.map.label)[0];\n const value = jp(it, cfg.map.value)[0];\n const meta = cfg.map.meta\n ? Object.fromEntries(\n Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]]),\n )\n : undefined;\n out.push({ label: String(label ?? \"\"), value, meta });\n }\n cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;\n page += 1;\n } while (cfg.paginate && cursor && page < (cfg.paginate.maxPages ?? 5));\n return { items: out, ttl: 300 };\n },\n});\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rest_api.js","sources":["../../src/protocols/rest_api.ts"],"sourcesContent":["import { jp } from \"../core/jsonpath\";\nimport { renderObj, renderTpl } from \"../core/templating\";\nimport type {\n Protocol,\n ProviderItem,\n ProviderOutput,\n AuthConfig,\n} from \"../core/types\";\n\nexport type RestApiCfg = {\n url: string;\n method?: \"GET\" | \"POST\";\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n body?: unknown;\n items: string; // JSONPath to array; e.g. \"$.items[*]\"\n map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item\n paginate?: { cursorPath: string; param: string; maxPages?: number };\n auth?: AuthConfig;\n showError?: boolean; // Whether to show error messages, defaults to true\n};\n\nfunction buildAuthHeaders(\n auth?: AuthConfig,\n globalAuth?: Record<string, unknown>,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (!auth) return headers;\n\n // Handle \"use\" reference to global auth\n if (auth.use && globalAuth?.[auth.use]) {\n const globalValue = globalAuth[auth.use];\n const value =\n typeof globalValue === \"function\" ? globalValue() : globalValue;\n\n if (auth.use === \"apiKey\") {\n headers[\"X-API-Key\"] = String(value);\n } else if (auth.use === \"bearer\") {\n headers[\"Authorization\"] = `Bearer ${value}`;\n } else if (auth.use === \"token\") {\n headers[\"Authorization\"] = `Token ${value}`;\n }\n return headers;\n }\n\n // Handle direct auth values\n if (auth.apiKey) {\n const value =\n typeof auth.apiKey === \"function\" ? auth.apiKey() : auth.apiKey;\n headers[\"X-API-Key\"] = String(value);\n }\n\n if (auth.bearer) {\n const value =\n typeof auth.bearer === \"function\" ? auth.bearer() : auth.bearer;\n headers[\"Authorization\"] = `Bearer ${value}`;\n }\n\n if (auth.token) {\n const value = typeof auth.token === \"function\" ? auth.token() : auth.token;\n headers[\"Authorization\"] = `Token ${value}`;\n }\n\n // Handle custom auth fields\n for (const [key, value] of Object.entries(auth)) {\n if (\n ![\"use\", \"apiKey\", \"bearer\", \"token\"].includes(key) &&\n value !== undefined\n ) {\n const authValue = typeof value === \"function\" ? value() : value;\n headers[key] = String(authValue);\n }\n }\n\n return headers;\n}\n\nexport const RestApiProtocol = (): Protocol<RestApiCfg> => ({\n protocol: \"rest_api\",\n async resolve(cfg, ctx): Promise<ProviderOutput> {\n const ac = ctx.signal;\n const out: ProviderItem[] = [];\n let cursor: unknown = null;\n let page = 0;\n do {\n const renderedUrl = renderTpl(cfg.url, { data: ctx.data, ui: ctx.ui });\n\n // Check if URL has unresolved template variables (empty string after template rendering)\n if (cfg.url.includes(\"{{\") && renderedUrl.endsWith(\"/\")) {\n return { items: [], ttl: 0 };\n }\n\n const url = new URL(renderedUrl);\n const q = renderObj(cfg.query ?? {}, {\n data: ctx.data,\n ui: ctx.ui,\n }) as Record<string, unknown>;\n for (const [k, v] of Object.entries(q))\n if (v !== undefined && v !== \"\") url.searchParams.set(k, String(v));\n if (cursor && cfg.paginate)\n url.searchParams.set(cfg.paginate.param, String(cursor));\n\n // Build headers with auth\n const baseHeaders = renderObj(cfg.headers ?? {}, {\n data: ctx.data,\n }) as Record<string, string>;\n const authHeaders = buildAuthHeaders(cfg.auth, ctx.auth);\n const headers = { ...baseHeaders, ...authHeaders };\n\n const method = cfg.method ?? \"GET\";\n const requestInit: RequestInit = {\n method,\n headers,\n signal: ac,\n };\n\n // Only include body for non-GET requests\n if (method !== \"GET\" && cfg.body) {\n requestInit.body = JSON.stringify(\n renderObj(cfg.body, { data: ctx.data }),\n );\n }\n\n const res = await fetch(url.toString(), requestInit);\n if (!res.ok) {\n if (cfg.showError !== false) {\n throw new Error(`REST ${res.status}`);\n }\n // If showError is false, return empty items instead of throwing\n return { items: [], ttl: 0 };\n }\n const json = await res.json();\n const items = jp(json, cfg.items);\n for (const it of items) {\n const label = jp(it, cfg.map.label)[0];\n const value = jp(it, cfg.map.value)[0];\n const meta = cfg.map.meta\n ? Object.fromEntries(\n Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]]),\n )\n : undefined;\n out.push({ label: String(label ?? \"\"), value, meta });\n }\n cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;\n page += 1;\n } while (cfg.paginate && cursor && page < (cfg.paginate.maxPages ?? 5));\n return { items: out, ttl: 300 };\n },\n});\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDerive.d.ts","sourceRoot":"","sources":["../../../src/vue/composables/useDerive.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACxD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,UAAU,aAAa;IACrB,OAAO,EAAE,GAAG,CAAC;QACX,QAAQ,EAAE,cAAc,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,OAAO,CAAC;KACf,CAAC,CAAC;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,aAAa,QAwCjE"}
1
+ {"version":3,"file":"useDerive.d.ts","sourceRoot":"","sources":["../../../src/vue/composables/useDerive.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACxD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,UAAU,aAAa;IACrB,OAAO,EAAE,GAAG,CAAC;QACX,QAAQ,EAAE,cAAc,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,OAAO,CAAC;KACf,CAAC,CAAC;IACH,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACtD;AAED,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,aAAa,QAkDjE"}
@@ -5,6 +5,10 @@ function useDerive({ control, handleChange }) {
5
5
  value: {}
6
6
  });
7
7
  const rootData = computed(() => injectedFormData.value || {});
8
+ const injectedExternalData = inject("externalData", {
9
+ value: {}
10
+ });
11
+ const externalData = computed(() => injectedExternalData.value || {});
8
12
  const deriveConfig = computed(() => {
9
13
  const options = control.value.uischema?.options;
10
14
  return {
@@ -14,13 +18,17 @@ function useDerive({ control, handleChange }) {
14
18
  };
15
19
  });
16
20
  watch(
17
- [rootData, deriveConfig],
18
- ([data, config]) => {
21
+ [rootData, externalData, deriveConfig],
22
+ ([data, extData, config]) => {
19
23
  if (!config.expression || config.mode !== "follow") {
20
24
  return;
21
25
  }
22
26
  try {
23
- const derivedValue = resolveDeriveExpression(config.expression, data);
27
+ const derivedValue = resolveDeriveExpression(
28
+ config.expression,
29
+ data,
30
+ extData
31
+ );
24
32
  if (derivedValue !== control.value.data) {
25
33
  handleChange(control.value.path, derivedValue);
26
34
  }
@@ -34,7 +42,11 @@ function useDerive({ control, handleChange }) {
34
42
  { deep: true, immediate: true }
35
43
  );
36
44
  }
37
- function resolveDeriveExpression(expression, data) {
45
+ function resolveDeriveExpression(expression, data, externalData) {
46
+ if (expression.startsWith("externalData(") && expression.endsWith(")")) {
47
+ const propertyPath = expression.slice(13, -1);
48
+ return resolvePropertyPath(propertyPath, externalData);
49
+ }
38
50
  if (!expression.includes("(") && !expression.includes("+") && !expression.includes("?")) {
39
51
  return resolvePropertyPath(expression, data);
40
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useDerive.js","sources":["../../../src/vue/composables/useDerive.ts"],"sourcesContent":["import { computed, watch, inject, type Ref } from \"vue\";\nimport { type ControlElement } from \"@jsonforms/core\";\n\ninterface DeriveOptions {\n control: Ref<{\n uischema: ControlElement;\n path: string;\n data: unknown;\n }>;\n handleChange: (path: string, value: unknown) => void;\n}\n\nexport function useDerive({ control, handleChange }: DeriveOptions) {\n // Get the root form data from JSONForms context\n const injectedFormData = inject<{ value: unknown }>(\"formData\", {\n value: {},\n });\n const rootData = computed(() => injectedFormData.value || {});\n\n // Extract derive configuration from uischema options\n const deriveConfig = computed(() => {\n const options = control.value.uischema?.options as\n | { derive?: string; mode?: string }\n | undefined;\n return {\n expression: options?.derive,\n mode: options?.mode || \"follow\", // 'follow' = auto-update, 'manual' = user controlled\n };\n });\n\n // Watch for changes in form data and update derived field\n watch(\n [rootData, deriveConfig],\n ([data, config]) => {\n if (!config.expression || config.mode !== \"follow\") {\n return;\n }\n\n try {\n const derivedValue = resolveDeriveExpression(config.expression, data);\n if (derivedValue !== control.value.data) {\n handleChange(control.value.path, derivedValue);\n }\n } catch (error) {\n console.warn(\n `Failed to derive value for ${control.value.path}:`,\n error,\n );\n }\n },\n { deep: true, immediate: true },\n );\n}\n\nfunction resolveDeriveExpression(expression: string, data: unknown): unknown {\n // Handle simple property paths like \"country.name\"\n if (\n !expression.includes(\"(\") &&\n !expression.includes(\"+\") &&\n !expression.includes(\"?\")\n ) {\n return resolvePropertyPath(expression, data);\n }\n\n // For now, we'll only support simple property paths\n // Complex expressions would require a safe expression evaluator\n return resolvePropertyPath(expression, data);\n}\n\nfunction resolvePropertyPath(path: string, data: unknown): unknown {\n if (!path || !data) return \"\";\n\n const keys = path.split(\".\");\n let value: unknown = data;\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return null;\n }\n }\n\n return value;\n}\n"],"names":[],"mappings":";;AAYO,SAAS,UAAU,EAAE,SAAS,gBAA+B;AAElE,QAAM,mBAAmB,OAA2B,YAAY;AAAA,IAC9D,OAAO,CAAA;AAAA,EAAC,CACT;AACD,QAAM,WAAW,SAAS,MAAM,iBAAiB,SAAS,CAAA,CAAE;AAG5D,QAAM,eAAe,SAAS,MAAM;AAClC,UAAM,UAAU,QAAQ,MAAM,UAAU;AAGxC,WAAO;AAAA,MACL,YAAY,SAAS;AAAA,MACrB,MAAM,SAAS,QAAQ;AAAA;AAAA,IAAA;AAAA,EAE3B,CAAC;AAGD;AAAA,IACE,CAAC,UAAU,YAAY;AAAA,IACvB,CAAC,CAAC,MAAM,MAAM,MAAM;AAClB,UAAI,CAAC,OAAO,cAAc,OAAO,SAAS,UAAU;AAClD;AAAA,MACF;AAEA,UAAI;AACF,cAAM,eAAe,wBAAwB,OAAO,YAAY,IAAI;AACpE,YAAI,iBAAiB,QAAQ,MAAM,MAAM;AACvC,uBAAa,QAAQ,MAAM,MAAM,YAAY;AAAA,QAC/C;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ;AAAA,UACN,8BAA8B,QAAQ,MAAM,IAAI;AAAA,UAChD;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA,IACA,EAAE,MAAM,MAAM,WAAW,KAAA;AAAA,EAAK;AAElC;AAEA,SAAS,wBAAwB,YAAoB,MAAwB;AAE3E,MACE,CAAC,WAAW,SAAS,GAAG,KACxB,CAAC,WAAW,SAAS,GAAG,KACxB,CAAC,WAAW,SAAS,GAAG,GACxB;AACA,WAAO,oBAAoB,YAAY,IAAI;AAAA,EAC7C;AAIA,SAAO,oBAAoB,YAAY,IAAI;AAC7C;AAEA,SAAS,oBAAoB,MAAc,MAAwB;AACjE,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAE3B,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,QAAiB;AAErB,aAAW,OAAO,MAAM;AACtB,QAAI,SAAS,OAAO,UAAU,YAAY,OAAO,OAAO;AACtD,cAAS,MAAkC,GAAG;AAAA,IAChD,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"useDerive.js","sources":["../../../src/vue/composables/useDerive.ts"],"sourcesContent":["import { computed, watch, inject, type Ref } from \"vue\";\nimport { type ControlElement } from \"@jsonforms/core\";\n\ninterface DeriveOptions {\n control: Ref<{\n uischema: ControlElement;\n path: string;\n data: unknown;\n }>;\n handleChange: (path: string, value: unknown) => void;\n}\n\nexport function useDerive({ control, handleChange }: DeriveOptions) {\n // Get the root form data from JSONForms context\n const injectedFormData = inject<{ value: unknown }>(\"formData\", {\n value: {},\n });\n const rootData = computed(() => injectedFormData.value || {});\n\n // Get external data from context if available\n const injectedExternalData = inject<{ value: unknown }>(\"externalData\", {\n value: {},\n });\n const externalData = computed(() => injectedExternalData.value || {});\n\n // Extract derive configuration from uischema options\n const deriveConfig = computed(() => {\n const options = control.value.uischema?.options as\n | { derive?: string; mode?: string }\n | undefined;\n return {\n expression: options?.derive,\n mode: options?.mode || \"follow\", // 'follow' = auto-update, 'manual' = user controlled\n };\n });\n\n // Watch for changes in form data and external data and update derived field\n watch(\n [rootData, externalData, deriveConfig],\n ([data, extData, config]) => {\n if (!config.expression || config.mode !== \"follow\") {\n return;\n }\n\n try {\n const derivedValue = resolveDeriveExpression(\n config.expression,\n data,\n extData,\n );\n if (derivedValue !== control.value.data) {\n handleChange(control.value.path, derivedValue);\n }\n } catch (error) {\n console.warn(\n `Failed to derive value for ${control.value.path}:`,\n error,\n );\n }\n },\n { deep: true, immediate: true },\n );\n}\n\nfunction resolveDeriveExpression(\n expression: string,\n data: unknown,\n externalData?: unknown,\n): unknown {\n // Handle externalData() syntax\n if (expression.startsWith(\"externalData(\") && expression.endsWith(\")\")) {\n const propertyPath = expression.slice(13, -1); // Remove \"externalData(\" and \")\"\n return resolvePropertyPath(propertyPath, externalData);\n }\n\n // Handle simple property paths like \"country.name\"\n if (\n !expression.includes(\"(\") &&\n !expression.includes(\"+\") &&\n !expression.includes(\"?\")\n ) {\n return resolvePropertyPath(expression, data);\n }\n\n // For now, we'll only support simple property paths and externalData() calls\n // Complex expressions would require a safe expression evaluator\n return resolvePropertyPath(expression, data);\n}\n\nfunction resolvePropertyPath(path: string, data: unknown): unknown {\n if (!path || !data) return \"\";\n\n const keys = path.split(\".\");\n let value: unknown = data;\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = (value as Record<string, unknown>)[key];\n } else {\n return null;\n }\n }\n\n return value;\n}\n"],"names":[],"mappings":";;AAYO,SAAS,UAAU,EAAE,SAAS,gBAA+B;AAElE,QAAM,mBAAmB,OAA2B,YAAY;AAAA,IAC9D,OAAO,CAAA;AAAA,EAAC,CACT;AACD,QAAM,WAAW,SAAS,MAAM,iBAAiB,SAAS,CAAA,CAAE;AAG5D,QAAM,uBAAuB,OAA2B,gBAAgB;AAAA,IACtE,OAAO,CAAA;AAAA,EAAC,CACT;AACD,QAAM,eAAe,SAAS,MAAM,qBAAqB,SAAS,CAAA,CAAE;AAGpE,QAAM,eAAe,SAAS,MAAM;AAClC,UAAM,UAAU,QAAQ,MAAM,UAAU;AAGxC,WAAO;AAAA,MACL,YAAY,SAAS;AAAA,MACrB,MAAM,SAAS,QAAQ;AAAA;AAAA,IAAA;AAAA,EAE3B,CAAC;AAGD;AAAA,IACE,CAAC,UAAU,cAAc,YAAY;AAAA,IACrC,CAAC,CAAC,MAAM,SAAS,MAAM,MAAM;AAC3B,UAAI,CAAC,OAAO,cAAc,OAAO,SAAS,UAAU;AAClD;AAAA,MACF;AAEA,UAAI;AACF,cAAM,eAAe;AAAA,UACnB,OAAO;AAAA,UACP;AAAA,UACA;AAAA,QAAA;AAEF,YAAI,iBAAiB,QAAQ,MAAM,MAAM;AACvC,uBAAa,QAAQ,MAAM,MAAM,YAAY;AAAA,QAC/C;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ;AAAA,UACN,8BAA8B,QAAQ,MAAM,IAAI;AAAA,UAChD;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA,IACA,EAAE,MAAM,MAAM,WAAW,KAAA;AAAA,EAAK;AAElC;AAEA,SAAS,wBACP,YACA,MACA,cACS;AAET,MAAI,WAAW,WAAW,eAAe,KAAK,WAAW,SAAS,GAAG,GAAG;AACtE,UAAM,eAAe,WAAW,MAAM,IAAI,EAAE;AAC5C,WAAO,oBAAoB,cAAc,YAAY;AAAA,EACvD;AAGA,MACE,CAAC,WAAW,SAAS,GAAG,KACxB,CAAC,WAAW,SAAS,GAAG,KACxB,CAAC,WAAW,SAAS,GAAG,GACxB;AACA,WAAO,oBAAoB,YAAY,IAAI;AAAA,EAC7C;AAIA,SAAO,oBAAoB,YAAY,IAAI;AAC7C;AAEA,SAAS,oBAAoB,MAAc,MAAwB;AACjE,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAE3B,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,QAAiB;AAErB,aAAW,OAAO,MAAM;AACtB,QAAI,SAAS,OAAO,UAAU,YAAY,OAAO,OAAO;AACtD,cAAS,MAAkC,GAAG;AAAA,IAChD,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narrative.io/jsonforms-provider-protocols",
3
- "version": "1.1.0-beta.4",
3
+ "version": "1.1.0-beta.6",
4
4
  "description": "Dynamic data provider capabilities for JSONForms with Vue 3 integration",
5
5
  "type": "module",
6
6
  "author": "Narrative I/O",
@@ -17,6 +17,7 @@ export type RestApiCfg = {
17
17
  map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item
18
18
  paginate?: { cursorPath: string; param: string; maxPages?: number };
19
19
  auth?: AuthConfig;
20
+ showError?: boolean; // Whether to show error messages, defaults to true
20
21
  };
21
22
 
22
23
  function buildAuthHeaders(
@@ -122,7 +123,13 @@ export const RestApiProtocol = (): Protocol<RestApiCfg> => ({
122
123
  }
123
124
 
124
125
  const res = await fetch(url.toString(), requestInit);
125
- if (!res.ok) throw new Error(`REST ${res.status}`);
126
+ if (!res.ok) {
127
+ if (cfg.showError !== false) {
128
+ throw new Error(`REST ${res.status}`);
129
+ }
130
+ // If showError is false, return empty items instead of throwing
131
+ return { items: [], ttl: 0 };
132
+ }
126
133
  const json = await res.json();
127
134
  const items = jp(json, cfg.items);
128
135
  for (const it of items) {
@@ -17,6 +17,12 @@ export function useDerive({ control, handleChange }: DeriveOptions) {
17
17
  });
18
18
  const rootData = computed(() => injectedFormData.value || {});
19
19
 
20
+ // Get external data from context if available
21
+ const injectedExternalData = inject<{ value: unknown }>("externalData", {
22
+ value: {},
23
+ });
24
+ const externalData = computed(() => injectedExternalData.value || {});
25
+
20
26
  // Extract derive configuration from uischema options
21
27
  const deriveConfig = computed(() => {
22
28
  const options = control.value.uischema?.options as
@@ -28,16 +34,20 @@ export function useDerive({ control, handleChange }: DeriveOptions) {
28
34
  };
29
35
  });
30
36
 
31
- // Watch for changes in form data and update derived field
37
+ // Watch for changes in form data and external data and update derived field
32
38
  watch(
33
- [rootData, deriveConfig],
34
- ([data, config]) => {
39
+ [rootData, externalData, deriveConfig],
40
+ ([data, extData, config]) => {
35
41
  if (!config.expression || config.mode !== "follow") {
36
42
  return;
37
43
  }
38
44
 
39
45
  try {
40
- const derivedValue = resolveDeriveExpression(config.expression, data);
46
+ const derivedValue = resolveDeriveExpression(
47
+ config.expression,
48
+ data,
49
+ extData,
50
+ );
41
51
  if (derivedValue !== control.value.data) {
42
52
  handleChange(control.value.path, derivedValue);
43
53
  }
@@ -52,7 +62,17 @@ export function useDerive({ control, handleChange }: DeriveOptions) {
52
62
  );
53
63
  }
54
64
 
55
- function resolveDeriveExpression(expression: string, data: unknown): unknown {
65
+ function resolveDeriveExpression(
66
+ expression: string,
67
+ data: unknown,
68
+ externalData?: unknown,
69
+ ): unknown {
70
+ // Handle externalData() syntax
71
+ if (expression.startsWith("externalData(") && expression.endsWith(")")) {
72
+ const propertyPath = expression.slice(13, -1); // Remove "externalData(" and ")"
73
+ return resolvePropertyPath(propertyPath, externalData);
74
+ }
75
+
56
76
  // Handle simple property paths like "country.name"
57
77
  if (
58
78
  !expression.includes("(") &&
@@ -62,7 +82,7 @@ function resolveDeriveExpression(expression: string, data: unknown): unknown {
62
82
  return resolvePropertyPath(expression, data);
63
83
  }
64
84
 
65
- // For now, we'll only support simple property paths
85
+ // For now, we'll only support simple property paths and externalData() calls
66
86
  // Complex expressions would require a safe expression evaluator
67
87
  return resolvePropertyPath(expression, data);
68
88
  }