@makehq/forman-schema 1.3.4 → 1.4.1

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.cjs CHANGED
@@ -42,6 +42,10 @@ function containsIMLExpression(value) {
42
42
  if (typeof value !== "string") return false;
43
43
  return value.indexOf("{{") > -1 && value.indexOf("}}") > -1;
44
44
  }
45
+ function isPrimitiveIMLExpression(value) {
46
+ if (typeof value !== "string") return false;
47
+ return value.lastIndexOf("{{") === 0 && value.indexOf("}}") === value.length - 2;
48
+ }
45
49
  var API_ENDPOINTS = {
46
50
  account: "api://connections/{{kind}}",
47
51
  aiagent: "api://ai-agents/v1/agents",
@@ -247,6 +251,7 @@ function handleArrayType(field, result, context) {
247
251
  function handleSelectType(field, result, context) {
248
252
  const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
249
253
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
254
+ const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
250
255
  const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
251
256
  if (typeof optionsOrGroups === "string") {
252
257
  Object.defineProperty(result, "x-fetch", {
@@ -268,12 +273,33 @@ function handleSelectType(field, result, context) {
268
273
  })) {
269
274
  result.oneOf = (options || []).map((option) => {
270
275
  const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
276
+ const localNestedContainsStrings = Array.isArray(localNested) && localNested.some((item) => typeof item === "string");
271
277
  const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
272
278
  if (localNested) {
273
279
  context.addConditionalFields(
274
280
  field.name,
275
281
  option.value,
276
- typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
282
+ typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : localNestedContainsStrings ? {
283
+ type: "object",
284
+ allOf: localNested.map(
285
+ (item) => typeof item === "string" ? {
286
+ $ref: appendQueryString(item, localDomain, [
287
+ ...context.tail,
288
+ field.name
289
+ ])
290
+ } : toJSONSchemaInternal(
291
+ {
292
+ type: "collection",
293
+ spec: [item]
294
+ },
295
+ {
296
+ ...context,
297
+ domain: localDomain,
298
+ tail: [...context.tail, field.name]
299
+ }
300
+ )
301
+ )
302
+ } : toJSONSchemaInternal(
277
303
  { type: "collection", spec: localNested },
278
304
  {
279
305
  ...context,
@@ -293,7 +319,7 @@ function handleSelectType(field, result, context) {
293
319
  }
294
320
  }
295
321
  if (nested && domain && domain !== context.domain) {
296
- if (typeof nested === "string") {
322
+ if (typeof nested === "string" || nestedContainsStrings) {
297
323
  throw new SchemaConversionError("Dynamic nested fields with domain change are not supported.");
298
324
  }
299
325
  let root = context.roots[domain];
@@ -312,7 +338,19 @@ function handleSelectType(field, result, context) {
312
338
  configurable: true,
313
339
  enumerable: true,
314
340
  writable: true,
315
- value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
341
+ value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
342
+ type: "object",
343
+ allOf: nested.map(
344
+ (item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
345
+ { type: "collection", spec: [item] },
346
+ {
347
+ ...context,
348
+ domain: domain || context.domain,
349
+ tail: [...context.tail, field.name]
350
+ }
351
+ )
352
+ )
353
+ } : toJSONSchemaInternal(
316
354
  { type: "collection", spec: nested },
317
355
  {
318
356
  ...context,
@@ -478,7 +516,7 @@ async function validateFormanValue(value, field, context) {
478
516
  const expectedType = FORMAN_TYPE_MAP2[normalizedField.type];
479
517
  let actualType = typeof value;
480
518
  if (actualType === "object" && Array.isArray(value)) actualType = "array";
481
- if (expectedType && expectedType !== actualType) {
519
+ if (expectedType && expectedType !== actualType && !isPrimitiveIMLExpression(value)) {
482
520
  return {
483
521
  valid: false,
484
522
  errors: [
@@ -729,21 +767,32 @@ async function handleNestedFields(nested, value, field, context) {
729
767
  tail: field.name ? [...context.tail, { name: field.name, value }] : context.tail
730
768
  };
731
769
  if (typeof store === "string") {
732
- try {
733
- store = await context.resolveRemote(store, context);
734
- } catch (error) {
735
- return {
736
- valid: false,
737
- errors: [
738
- ...errors,
739
- {
740
- domain: context.domain,
741
- path: context.path.join("."),
742
- message: `Failed to resolve remote resource ${store}: ${error}`
743
- }
744
- ]
745
- };
770
+ store = [store];
771
+ }
772
+ if (Array.isArray(store) && store.some((item) => typeof item === "string")) {
773
+ const resolvedStore = [];
774
+ for (const item of store) {
775
+ if (typeof item === "string") {
776
+ try {
777
+ resolvedStore.push(...await context.resolveRemote(item, context));
778
+ } catch (error) {
779
+ return {
780
+ valid: false,
781
+ errors: [
782
+ ...errors,
783
+ {
784
+ domain: context.domain,
785
+ path: context.path.join("."),
786
+ message: `Failed to resolve remote resource ${item}: ${error}`
787
+ }
788
+ ]
789
+ };
790
+ }
791
+ } else {
792
+ resolvedStore.push(item);
793
+ }
746
794
  }
795
+ store = resolvedStore;
747
796
  }
748
797
  if (store && domain && domain !== context.domain) {
749
798
  if (!context.roots[domain]) {
package/dist/index.d.cts CHANGED
@@ -138,7 +138,7 @@ type FormanSchemaExtendedOptions = {
138
138
  /**
139
139
  * Nested fields
140
140
  */
141
- type FormanSchemaNested = FormanSchemaField[] | string | FormanSchemaExtendedNested;
141
+ type FormanSchemaNested = (FormanSchemaField | string)[] | string | FormanSchemaExtendedNested;
142
142
  /**
143
143
  * Extended nested fields
144
144
  */
package/dist/index.d.ts CHANGED
@@ -138,7 +138,7 @@ type FormanSchemaExtendedOptions = {
138
138
  /**
139
139
  * Nested fields
140
140
  */
141
- type FormanSchemaNested = FormanSchemaField[] | string | FormanSchemaExtendedNested;
141
+ type FormanSchemaNested = (FormanSchemaField | string)[] | string | FormanSchemaExtendedNested;
142
142
  /**
143
143
  * Extended nested fields
144
144
  */
package/dist/index.js CHANGED
@@ -13,6 +13,10 @@ function containsIMLExpression(value) {
13
13
  if (typeof value !== "string") return false;
14
14
  return value.indexOf("{{") > -1 && value.indexOf("}}") > -1;
15
15
  }
16
+ function isPrimitiveIMLExpression(value) {
17
+ if (typeof value !== "string") return false;
18
+ return value.lastIndexOf("{{") === 0 && value.indexOf("}}") === value.length - 2;
19
+ }
16
20
  var API_ENDPOINTS = {
17
21
  account: "api://connections/{{kind}}",
18
22
  aiagent: "api://ai-agents/v1/agents",
@@ -218,6 +222,7 @@ function handleArrayType(field, result, context) {
218
222
  function handleSelectType(field, result, context) {
219
223
  const optionsOrGroups = isObject(field.options) ? field.options.store : field.options;
220
224
  const nested = isObject(field.options) ? isObject(field.options.nested) ? field.options.nested.store : field.options.nested : void 0;
225
+ const nestedContainsStrings = Array.isArray(nested) && nested.some((item) => typeof item === "string");
221
226
  const domain = isObject(field.options) ? isObject(field.options.nested) && field.options.nested.domain ? field.options.nested.domain : void 0 : void 0;
222
227
  if (typeof optionsOrGroups === "string") {
223
228
  Object.defineProperty(result, "x-fetch", {
@@ -239,12 +244,33 @@ function handleSelectType(field, result, context) {
239
244
  })) {
240
245
  result.oneOf = (options || []).map((option) => {
241
246
  const localNested = (isObject(option.nested) ? option.nested.store : option.nested) || nested;
247
+ const localNestedContainsStrings = Array.isArray(localNested) && localNested.some((item) => typeof item === "string");
242
248
  const localDomain = (isObject(option.nested) && option.nested.domain ? option.nested.domain : domain) || context.domain;
243
249
  if (localNested) {
244
250
  context.addConditionalFields(
245
251
  field.name,
246
252
  option.value,
247
- typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : toJSONSchemaInternal(
253
+ typeof localNested === "string" ? appendQueryString(localNested, localDomain, [...context.tail, field.name]) : localNestedContainsStrings ? {
254
+ type: "object",
255
+ allOf: localNested.map(
256
+ (item) => typeof item === "string" ? {
257
+ $ref: appendQueryString(item, localDomain, [
258
+ ...context.tail,
259
+ field.name
260
+ ])
261
+ } : toJSONSchemaInternal(
262
+ {
263
+ type: "collection",
264
+ spec: [item]
265
+ },
266
+ {
267
+ ...context,
268
+ domain: localDomain,
269
+ tail: [...context.tail, field.name]
270
+ }
271
+ )
272
+ )
273
+ } : toJSONSchemaInternal(
248
274
  { type: "collection", spec: localNested },
249
275
  {
250
276
  ...context,
@@ -264,7 +290,7 @@ function handleSelectType(field, result, context) {
264
290
  }
265
291
  }
266
292
  if (nested && domain && domain !== context.domain) {
267
- if (typeof nested === "string") {
293
+ if (typeof nested === "string" || nestedContainsStrings) {
268
294
  throw new SchemaConversionError("Dynamic nested fields with domain change are not supported.");
269
295
  }
270
296
  let root = context.roots[domain];
@@ -283,7 +309,19 @@ function handleSelectType(field, result, context) {
283
309
  configurable: true,
284
310
  enumerable: true,
285
311
  writable: true,
286
- value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
312
+ value: typeof nested === "string" ? { $ref: appendQueryString(nested, context.domain, [...context.tail, field.name]) } : nestedContainsStrings ? {
313
+ type: "object",
314
+ allOf: nested.map(
315
+ (item) => typeof item === "string" ? { $ref: appendQueryString(item, context.domain, [...context.tail, field.name]) } : toJSONSchemaInternal(
316
+ { type: "collection", spec: [item] },
317
+ {
318
+ ...context,
319
+ domain: domain || context.domain,
320
+ tail: [...context.tail, field.name]
321
+ }
322
+ )
323
+ )
324
+ } : toJSONSchemaInternal(
287
325
  { type: "collection", spec: nested },
288
326
  {
289
327
  ...context,
@@ -449,7 +487,7 @@ async function validateFormanValue(value, field, context) {
449
487
  const expectedType = FORMAN_TYPE_MAP2[normalizedField.type];
450
488
  let actualType = typeof value;
451
489
  if (actualType === "object" && Array.isArray(value)) actualType = "array";
452
- if (expectedType && expectedType !== actualType) {
490
+ if (expectedType && expectedType !== actualType && !isPrimitiveIMLExpression(value)) {
453
491
  return {
454
492
  valid: false,
455
493
  errors: [
@@ -700,21 +738,32 @@ async function handleNestedFields(nested, value, field, context) {
700
738
  tail: field.name ? [...context.tail, { name: field.name, value }] : context.tail
701
739
  };
702
740
  if (typeof store === "string") {
703
- try {
704
- store = await context.resolveRemote(store, context);
705
- } catch (error) {
706
- return {
707
- valid: false,
708
- errors: [
709
- ...errors,
710
- {
711
- domain: context.domain,
712
- path: context.path.join("."),
713
- message: `Failed to resolve remote resource ${store}: ${error}`
714
- }
715
- ]
716
- };
741
+ store = [store];
742
+ }
743
+ if (Array.isArray(store) && store.some((item) => typeof item === "string")) {
744
+ const resolvedStore = [];
745
+ for (const item of store) {
746
+ if (typeof item === "string") {
747
+ try {
748
+ resolvedStore.push(...await context.resolveRemote(item, context));
749
+ } catch (error) {
750
+ return {
751
+ valid: false,
752
+ errors: [
753
+ ...errors,
754
+ {
755
+ domain: context.domain,
756
+ path: context.path.join("."),
757
+ message: `Failed to resolve remote resource ${item}: ${error}`
758
+ }
759
+ ]
760
+ };
761
+ }
762
+ } else {
763
+ resolvedStore.push(item);
764
+ }
717
765
  }
766
+ store = resolvedStore;
718
767
  }
719
768
  if (store && domain && domain !== context.domain) {
720
769
  if (!context.roots[domain]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makehq/forman-schema",
3
- "version": "1.3.4",
3
+ "version": "1.4.1",
4
4
  "description": "Forman Schema Tools",
5
5
  "license": "MIT",
6
6
  "author": "Make",