@fmaplabs/meta-manifest 0.3.0 → 0.4.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/index.cjs CHANGED
@@ -415,6 +415,17 @@ var MetaobjectRefField = class extends GidField {
415
415
  return [{ name: "metaobject_definition_type", value: resolveType(this.target) }];
416
416
  }
417
417
  };
418
+ var MixedRefField = class extends GidField {
419
+ constructor(targets, opts) {
420
+ super(opts);
421
+ this.targets = targets;
422
+ }
423
+ targets;
424
+ shopifyType = "mixed_reference";
425
+ validations() {
426
+ return [{ name: "metaobject_definition_types", value: JSON.stringify(this.targets.map(resolveType)) }];
427
+ }
428
+ };
418
429
  function product(opts = {}) {
419
430
  return new SimpleRefField(opts, "product_reference");
420
431
  }
@@ -433,6 +444,9 @@ function file(opts = {}) {
433
444
  function ref(target, opts = {}) {
434
445
  return new MetaobjectRefField(target, opts);
435
446
  }
447
+ function mixedRef(targets, opts = {}) {
448
+ return new MixedRefField(targets, opts);
449
+ }
436
450
 
437
451
  // src/fields/scalar.ts
438
452
  var StringScalarField = class extends Field {
@@ -581,6 +595,7 @@ var m = {
581
595
  page,
582
596
  file,
583
597
  ref,
598
+ mixedRef,
584
599
  list
585
600
  };
586
601
 
@@ -759,6 +774,27 @@ function refTarget(field) {
759
774
  }
760
775
  return void 0;
761
776
  }
777
+ function refTargets(field) {
778
+ const many = v(field.validations, "metaobject_definition_types");
779
+ if (many) {
780
+ try {
781
+ const arr = JSON.parse(many);
782
+ if (Array.isArray(arr)) return arr.map(String);
783
+ } catch {
784
+ }
785
+ }
786
+ const single = v(field.validations, "metaobject_definition_type");
787
+ return single ? [single] : [];
788
+ }
789
+ function mixedTargetsLiteral(field, typeToIdent, warnings) {
790
+ const targets = refTargets(field);
791
+ const idents = targets.map((t) => typeToIdent.get(t));
792
+ if (!targets.length || idents.some((i) => !i)) {
793
+ warnings.push(`unresolved mixed reference on field "${field.key}"`);
794
+ return void 0;
795
+ }
796
+ return `[${idents.map((i) => `() => ${i}`).join(", ")}]`;
797
+ }
762
798
  function optsLiteral(entries) {
763
799
  return entries.length ? `{ ${entries.join(", ")} }` : "";
764
800
  }
@@ -825,6 +861,15 @@ function fieldCall(field, typeToIdent, warnings) {
825
861
  const opts = optsLiteral(refEntries);
826
862
  return opts ? `m.ref(() => ${ident}, ${opts})` : `m.ref(() => ${ident})`;
827
863
  }
864
+ if (type === "mixed_reference") {
865
+ const arr = mixedTargetsLiteral(field, typeToIdent, warnings);
866
+ if (!arr) return `m.json() /* TODO: unmapped mixed reference */`;
867
+ const refEntries = [];
868
+ if (field.required) refEntries.push("required: true");
869
+ refEntries.push(...filterableEntry(field));
870
+ const opts = optsLiteral(refEntries);
871
+ return opts ? `m.mixedRef(${arr}, ${opts})` : `m.mixedRef(${arr})`;
872
+ }
828
873
  if (type.startsWith("list.")) {
829
874
  const inner = type.slice("list.".length);
830
875
  const listEntries = [];
@@ -844,6 +889,10 @@ function fieldCall(field, typeToIdent, warnings) {
844
889
  return `m.json() /* TODO: unmapped list reference */`;
845
890
  }
846
891
  innerCall = `m.ref(() => ${ident})`;
892
+ } else if (inner === "mixed_reference") {
893
+ const arr = mixedTargetsLiteral(field, typeToIdent, warnings);
894
+ if (!arr) return `m.json() /* TODO: unmapped list mixed reference */`;
895
+ innerCall = `m.mixedRef(${arr})`;
847
896
  } else if (SIMPLE[inner]) {
848
897
  innerCall = scalarCall(SIMPLE[inner], { ...field, required: false }, warnings);
849
898
  } else {
@@ -910,6 +959,8 @@ function referencedTypes(def) {
910
959
  if (f.type === "metaobject_reference" || f.type === "list.metaobject_reference") {
911
960
  const t = refTarget(f);
912
961
  if (t) out.add(t);
962
+ } else if (f.type === "mixed_reference" || f.type === "list.mixed_reference") {
963
+ for (const t of refTargets(f)) out.add(t);
913
964
  }
914
965
  }
915
966
  return out;
@@ -1330,25 +1381,35 @@ function definitionUpdateFor(def, changes) {
1330
1381
  function fieldInputFor(defByType, type, key) {
1331
1382
  return defByType.get(type)?.fieldDefinitions.find((f) => f.key === key);
1332
1383
  }
1333
- function referenceEdges(def) {
1384
+ function fieldRefTargets(field) {
1334
1385
  const out = [];
1335
- for (const field of def.fieldDefinitions) {
1336
- for (const v2 of field.validations) {
1337
- if (v2.name === "metaobject_definition_type") {
1338
- out.push(v2.value);
1339
- } else if (v2.name === "metaobject_definition_types") {
1340
- try {
1341
- const parsed = JSON.parse(v2.value);
1342
- if (Array.isArray(parsed)) {
1343
- for (const t of parsed) if (typeof t === "string") out.push(t);
1344
- }
1345
- } catch {
1386
+ for (const v2 of field.validations) {
1387
+ if (v2.name === "metaobject_definition_type") {
1388
+ out.push(v2.value);
1389
+ } else if (v2.name === "metaobject_definition_types") {
1390
+ try {
1391
+ const parsed = JSON.parse(v2.value);
1392
+ if (Array.isArray(parsed)) {
1393
+ for (const t of parsed) if (typeof t === "string") out.push(t);
1346
1394
  }
1395
+ } catch {
1347
1396
  }
1348
1397
  }
1349
1398
  }
1350
1399
  return out;
1351
1400
  }
1401
+ function referenceEdges(def) {
1402
+ return def.fieldDefinitions.flatMap(fieldRefTargets);
1403
+ }
1404
+ function splitCyclicFields(def, cyclicTypes) {
1405
+ const pass1 = [];
1406
+ const deferred = [];
1407
+ for (const f of def.fieldDefinitions) {
1408
+ const breaksCycle = fieldRefTargets(f).some((t) => cyclicTypes.has(t) && t !== def.type);
1409
+ (breaksCycle ? deferred : pass1).push(f);
1410
+ }
1411
+ return { pass1, deferred };
1412
+ }
1352
1413
  function topoSortCreates(types, deps) {
1353
1414
  const remaining = /* @__PURE__ */ new Map();
1354
1415
  const dependents = /* @__PURE__ */ new Map();
@@ -1391,42 +1452,35 @@ async function push(client, plan, sources, options) {
1391
1452
  deps.set(op.type, new Set(targets.filter((t) => createTypes.has(t) && t !== op.type)));
1392
1453
  }
1393
1454
  const { ordered, unordered } = topoSortCreates(createTypes, deps);
1394
- const orderedSet = new Set(ordered);
1395
1455
  const cyclicTypes = new Set(unordered);
1396
1456
  const createByType = new Map(createOps.map((x) => [x.op.type, x]));
1397
- const execOrder = [
1398
- ...ordered.map((t) => createByType.get(t)),
1399
- ...createOps.filter((x) => !orderedSet.has(x.op.type)),
1400
- ...otherOps
1401
- ];
1402
1457
  const failedTypes = /* @__PURE__ */ new Set();
1403
- async function applyOp(op) {
1404
- if (op.kind === "createDefinition") {
1405
- if (cyclicTypes.has(op.type)) {
1406
- failedTypes.add(op.type);
1407
- return { op, status: "blocked", reason: "reference cycle \u2014 two-pass create deferred" };
1408
- }
1409
- for (const dep of deps.get(op.type) ?? []) {
1410
- if (failedTypes.has(dep)) {
1411
- failedTypes.add(op.type);
1412
- return { op, status: "blocked", reason: `blocked: dependency "${dep}" was not created` };
1413
- }
1414
- }
1415
- const def = defByType.get(op.type);
1416
- if (!def) {
1417
- failedTypes.add(op.type);
1418
- return { op, status: "blocked", reason: `no definition input for "${op.type}"` };
1419
- }
1420
- const data2 = await execute(client, CREATE_DEFINITION_MUTATION, { definition: def });
1421
- const payload2 = data2.metaobjectDefinitionCreate;
1422
- if (payload2.userErrors.length) {
1458
+ async function createDefinition(op, definition) {
1459
+ const data = await execute(client, CREATE_DEFINITION_MUTATION, { definition });
1460
+ const payload = data.metaobjectDefinitionCreate;
1461
+ if (payload.userErrors.length) {
1462
+ failedTypes.add(op.type);
1463
+ return { op, status: "failed", userErrors: payload.userErrors };
1464
+ }
1465
+ const id = payload.metaobjectDefinition?.id;
1466
+ if (id) idByType.set(op.type, id);
1467
+ return { op, status: "applied", id };
1468
+ }
1469
+ async function applyAcyclicCreate(op) {
1470
+ for (const dep of deps.get(op.type) ?? []) {
1471
+ if (failedTypes.has(dep)) {
1423
1472
  failedTypes.add(op.type);
1424
- return { op, status: "failed", userErrors: payload2.userErrors };
1473
+ return { op, status: "blocked", reason: `blocked: dependency "${dep}" was not created` };
1425
1474
  }
1426
- const id2 = payload2.metaobjectDefinition?.id;
1427
- if (id2) idByType.set(op.type, id2);
1428
- return { op, status: "applied", id: id2 };
1429
1475
  }
1476
+ const def = defByType.get(op.type);
1477
+ if (!def) {
1478
+ failedTypes.add(op.type);
1479
+ return { op, status: "blocked", reason: `no definition input for "${op.type}"` };
1480
+ }
1481
+ return createDefinition(op, def);
1482
+ }
1483
+ async function applyFieldOp(op) {
1430
1484
  const destructive = "destructive" in op && op.destructive === true;
1431
1485
  if (destructive && !allowDestructive) return { op, status: "skipped", reason: "destructive" };
1432
1486
  if (failedTypes.has(op.type)) return { op, status: "blocked", reason: `blocked: definition "${op.type}" was not created` };
@@ -1479,7 +1533,53 @@ async function push(client, plan, sources, options) {
1479
1533
  }
1480
1534
  }
1481
1535
  const results = new Array(plan.length);
1482
- for (const { op, index } of execOrder) results[index] = await applyOp(op);
1536
+ for (const type of ordered) {
1537
+ const entry = createByType.get(type);
1538
+ results[entry.index] = await applyAcyclicCreate(entry.op);
1539
+ }
1540
+ const cyclicCreates = createOps.filter((x) => cyclicTypes.has(x.op.type));
1541
+ const deferredByType = /* @__PURE__ */ new Map();
1542
+ for (const { op, index } of cyclicCreates) {
1543
+ const def = defByType.get(op.type);
1544
+ if (!def) {
1545
+ failedTypes.add(op.type);
1546
+ results[index] = { op, status: "blocked", reason: `no definition input for "${op.type}"` };
1547
+ continue;
1548
+ }
1549
+ const failedDep = [...deps.get(op.type) ?? []].find((d) => !cyclicTypes.has(d) && failedTypes.has(d));
1550
+ if (failedDep) {
1551
+ failedTypes.add(op.type);
1552
+ results[index] = { op, status: "blocked", reason: `blocked: dependency "${failedDep}" was not created` };
1553
+ continue;
1554
+ }
1555
+ const { pass1, deferred } = splitCyclicFields(def, cyclicTypes);
1556
+ deferredByType.set(op.type, deferred);
1557
+ results[index] = await createDefinition(op, { ...def, fieldDefinitions: pass1 });
1558
+ }
1559
+ for (const { op, index } of cyclicCreates) {
1560
+ if (results[index]?.status !== "applied") continue;
1561
+ const deferred = deferredByType.get(op.type) ?? [];
1562
+ if (!deferred.length) continue;
1563
+ const failedTarget = deferred.flatMap(fieldRefTargets).find((t) => failedTypes.has(t));
1564
+ if (failedTarget) {
1565
+ failedTypes.add(op.type);
1566
+ results[index] = { op, status: "blocked", reason: `blocked: dependency "${failedTarget}" was not created` };
1567
+ continue;
1568
+ }
1569
+ const id = idByType.get(op.type);
1570
+ if (id == null) {
1571
+ results[index] = { op, status: "blocked", reason: `no definition id for "${op.type}"` };
1572
+ continue;
1573
+ }
1574
+ const definition = { fieldDefinitions: deferred.map((f) => ({ create: f })) };
1575
+ const data = await execute(client, UPDATE_DEFINITION_MUTATION, { id, definition });
1576
+ const payload = data.metaobjectDefinitionUpdate;
1577
+ if (payload.userErrors.length) {
1578
+ failedTypes.add(op.type);
1579
+ results[index] = { op, status: "failed", userErrors: payload.userErrors };
1580
+ }
1581
+ }
1582
+ for (const { op, index } of otherOps) results[index] = await applyFieldOp(op);
1483
1583
  const counts = { applied: 0, skipped: 0, blocked: 0, failed: 0 };
1484
1584
  for (const r of results) counts[r.status]++;
1485
1585
  return { results, counts, ok: counts.failed === 0 && counts.blocked === 0 };