@fmaplabs/meta-manifest 0.2.0 → 0.3.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/README.md +45 -1
- package/dist/{chunk-PFU5VAO7.js → chunk-AUR2YQCW.js} +2 -2
- package/dist/{chunk-3R6VQ3Z3.js → chunk-VSJUGUH7.js} +15 -3
- package/dist/chunk-VSJUGUH7.js.map +1 -0
- package/dist/{chunk-GH5DXHS5.js → chunk-ZTWFDJHN.js} +318 -54
- package/dist/chunk-ZTWFDJHN.js.map +1 -0
- package/dist/cli/index.cjs +306 -32
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +35 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +332 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -36
- package/dist/index.d.ts +168 -36
- package/dist/index.js +6 -2
- package/dist/node/client.cjs.map +1 -1
- package/dist/node/client.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-3R6VQ3Z3.js.map +0 -1
- package/dist/chunk-GH5DXHS5.js.map +0 -1
- /package/dist/{chunk-PFU5VAO7.js.map → chunk-AUR2YQCW.js.map} +0 -0
package/dist/cli/index.cjs
CHANGED
|
@@ -41,12 +41,14 @@ var PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {
|
|
|
41
41
|
required
|
|
42
42
|
type { name }
|
|
43
43
|
validations { name value }
|
|
44
|
+
capabilities { adminFilterable { enabled } }
|
|
44
45
|
}
|
|
45
|
-
access { admin storefront }
|
|
46
|
+
access { admin storefront customerAccount }
|
|
46
47
|
capabilities {
|
|
47
48
|
publishable { enabled }
|
|
48
49
|
translatable { enabled }
|
|
49
|
-
renderable { enabled }
|
|
50
|
+
renderable { enabled data { metaTitleKey metaDescriptionKey } }
|
|
51
|
+
onlineStore { enabled data { urlHandle } }
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
}`;
|
|
@@ -56,6 +58,8 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
|
|
|
56
58
|
id
|
|
57
59
|
name
|
|
58
60
|
type
|
|
61
|
+
description
|
|
62
|
+
displayNameKey
|
|
59
63
|
fieldDefinitions {
|
|
60
64
|
key
|
|
61
65
|
name
|
|
@@ -63,6 +67,14 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
|
|
|
63
67
|
required
|
|
64
68
|
type { name }
|
|
65
69
|
validations { name value }
|
|
70
|
+
capabilities { adminFilterable { enabled } }
|
|
71
|
+
}
|
|
72
|
+
access { admin storefront customerAccount }
|
|
73
|
+
capabilities {
|
|
74
|
+
publishable { enabled }
|
|
75
|
+
translatable { enabled }
|
|
76
|
+
renderable { enabled data { metaTitleKey metaDescriptionKey } }
|
|
77
|
+
onlineStore { enabled data { urlHandle } }
|
|
66
78
|
}
|
|
67
79
|
}
|
|
68
80
|
pageInfo { hasNextPage endCursor }
|
|
@@ -205,6 +217,12 @@ async function runInit(opts = {}) {
|
|
|
205
217
|
|
|
206
218
|
// src/codegen.ts
|
|
207
219
|
var APP_PREFIX = "$app:";
|
|
220
|
+
function isMerchant(type) {
|
|
221
|
+
return !type.startsWith(APP_PREFIX);
|
|
222
|
+
}
|
|
223
|
+
function filterableEntry(field) {
|
|
224
|
+
return field.filterable ? ["filterable: true"] : [];
|
|
225
|
+
}
|
|
208
226
|
var SIMPLE = {
|
|
209
227
|
single_line_text_field: "text",
|
|
210
228
|
multi_line_text_field: "multilineText",
|
|
@@ -286,8 +304,8 @@ function scalarEntries(field, warnings, builder) {
|
|
|
286
304
|
jsonArr("file_type_options", "accept");
|
|
287
305
|
return e;
|
|
288
306
|
}
|
|
289
|
-
function scalarCall(builder, field, warnings) {
|
|
290
|
-
const lit = optsLiteral(scalarEntries(field, warnings, builder));
|
|
307
|
+
function scalarCall(builder, field, warnings, extra = []) {
|
|
308
|
+
const lit = optsLiteral([...scalarEntries(field, warnings, builder), ...extra]);
|
|
291
309
|
return lit ? `m.${builder}(${lit})` : `m.${builder}()`;
|
|
292
310
|
}
|
|
293
311
|
function fieldCall(field, typeToIdent, warnings) {
|
|
@@ -297,6 +315,7 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
297
315
|
const max = v(field.validations, "max");
|
|
298
316
|
const e = [`min: ${Number(min ?? 1)}`, `max: ${Number(max ?? 5)}`];
|
|
299
317
|
if (field.required) e.unshift("required: true");
|
|
318
|
+
e.push(...filterableEntry(field));
|
|
300
319
|
if (min === void 0 || max === void 0) warnings.push(`rating field "${field.key}" missing min/max`);
|
|
301
320
|
return `m.rating(${optsLiteral(e)})`;
|
|
302
321
|
}
|
|
@@ -307,7 +326,11 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
307
326
|
warnings.push(`unresolved reference on field "${field.key}"`);
|
|
308
327
|
return `m.json() /* TODO: unmapped reference */`;
|
|
309
328
|
}
|
|
310
|
-
|
|
329
|
+
const refEntries = [];
|
|
330
|
+
if (field.required) refEntries.push("required: true");
|
|
331
|
+
refEntries.push(...filterableEntry(field));
|
|
332
|
+
const opts = optsLiteral(refEntries);
|
|
333
|
+
return opts ? `m.ref(() => ${ident}, ${opts})` : `m.ref(() => ${ident})`;
|
|
311
334
|
}
|
|
312
335
|
if (type.startsWith("list.")) {
|
|
313
336
|
const inner = type.slice("list.".length);
|
|
@@ -317,6 +340,7 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
317
340
|
const max = v(field.validations, "list.max");
|
|
318
341
|
if (min !== void 0) listEntries.push(`min: ${Number(min)}`);
|
|
319
342
|
if (max !== void 0) listEntries.push(`max: ${Number(max)}`);
|
|
343
|
+
listEntries.push(...filterableEntry(field));
|
|
320
344
|
const listOpts = optsLiteral(listEntries);
|
|
321
345
|
let innerCall;
|
|
322
346
|
if (inner === "metaobject_reference") {
|
|
@@ -335,17 +359,53 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
335
359
|
}
|
|
336
360
|
return listOpts ? `m.list(${innerCall}, ${listOpts})` : `m.list(${innerCall})`;
|
|
337
361
|
}
|
|
338
|
-
if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings);
|
|
362
|
+
if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings, filterableEntry(field));
|
|
339
363
|
warnings.push(`unmapped field type "${type}" on field "${field.key}"`);
|
|
340
364
|
return `m.json() /* TODO: unmapped type ${type} */`;
|
|
341
365
|
}
|
|
366
|
+
function accessSource(access) {
|
|
367
|
+
if (!access) return "";
|
|
368
|
+
const parts = [];
|
|
369
|
+
if (access.admin === "MERCHANT_READ_WRITE") parts.push(`admin: "merchant_read_write"`);
|
|
370
|
+
if (access.storefront === "PUBLIC_READ") parts.push(`storefront: "public_read"`);
|
|
371
|
+
if (access.customerAccount === "READ") parts.push(`customerAccount: "read"`);
|
|
372
|
+
return parts.length ? `{ ${parts.join(", ")} }` : "";
|
|
373
|
+
}
|
|
374
|
+
function capabilitiesSource(caps) {
|
|
375
|
+
if (!caps) return "";
|
|
376
|
+
const parts = [];
|
|
377
|
+
if (caps.publishable?.enabled) parts.push(`publishable: true`);
|
|
378
|
+
if (caps.translatable?.enabled) parts.push(`translatable: true`);
|
|
379
|
+
if (caps.renderable?.enabled) {
|
|
380
|
+
const d = caps.renderable.data;
|
|
381
|
+
const dparts = [];
|
|
382
|
+
if (d?.metaTitleKey != null) dparts.push(`metaTitleKey: ${JSON.stringify(d.metaTitleKey)}`);
|
|
383
|
+
if (d?.metaDescriptionKey != null) dparts.push(`metaDescriptionKey: ${JSON.stringify(d.metaDescriptionKey)}`);
|
|
384
|
+
parts.push(dparts.length ? `renderable: { ${dparts.join(", ")} }` : `renderable: true`);
|
|
385
|
+
}
|
|
386
|
+
if (caps.onlineStore?.enabled && caps.onlineStore.data?.urlHandle != null) {
|
|
387
|
+
parts.push(`onlineStore: { urlHandle: ${JSON.stringify(caps.onlineStore.data.urlHandle)} }`);
|
|
388
|
+
}
|
|
389
|
+
return parts.length ? `{ ${parts.join(", ")} }` : "";
|
|
390
|
+
}
|
|
391
|
+
function defConfigLines(def) {
|
|
392
|
+
const lines = [];
|
|
393
|
+
if (isMerchant(def.type)) lines.push(` scope: "merchant",`);
|
|
394
|
+
if (def.name) lines.push(` name: ${JSON.stringify(def.name)},`);
|
|
395
|
+
if (def.description != null) lines.push(` description: ${JSON.stringify(def.description)},`);
|
|
396
|
+
if (def.displayNameKey != null) lines.push(` displayName: ${JSON.stringify(def.displayNameKey)},`);
|
|
397
|
+
const access = accessSource(def.access);
|
|
398
|
+
if (access) lines.push(` access: ${access},`);
|
|
399
|
+
const caps = capabilitiesSource(def.capabilities);
|
|
400
|
+
if (caps) lines.push(` capabilities: ${caps},`);
|
|
401
|
+
return lines.length ? `
|
|
402
|
+
${lines.join("\n")}` : "";
|
|
403
|
+
}
|
|
342
404
|
function defSource(def, typeToIdent, warnings) {
|
|
343
405
|
const ident = typeToIdent.get(def.type);
|
|
344
406
|
const handle = handleOf(def.type);
|
|
345
407
|
const fields = def.fields.map((f) => ` ${f.key}: ${fieldCall(f, typeToIdent, warnings)},`).join("\n");
|
|
346
|
-
const
|
|
347
|
-
name: ${JSON.stringify(def.name)},` : "";
|
|
348
|
-
return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${name}
|
|
408
|
+
return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${defConfigLines(def)}
|
|
349
409
|
fields: {
|
|
350
410
|
${fields}
|
|
351
411
|
},
|
|
@@ -405,6 +465,42 @@ function sameValidations(a, b) {
|
|
|
405
465
|
const norm = (v2) => JSON.stringify([...v2].sort((x, y) => x.name.localeCompare(y.name)));
|
|
406
466
|
return norm(a) === norm(b);
|
|
407
467
|
}
|
|
468
|
+
var CAP_KEYS = ["publishable", "translatable", "renderable", "onlineStore"];
|
|
469
|
+
function accessChanged(local, remote) {
|
|
470
|
+
for (const key of ["admin", "storefront", "customerAccount"]) {
|
|
471
|
+
const lv = local[key];
|
|
472
|
+
if (lv != null && remote?.[key] !== lv) return true;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
function capabilitiesChanged(local, remote) {
|
|
477
|
+
for (const key of CAP_KEYS) {
|
|
478
|
+
const lc = local[key];
|
|
479
|
+
if (!lc) continue;
|
|
480
|
+
const rc = remote?.[key];
|
|
481
|
+
if ((rc?.enabled ?? false) !== lc.enabled) return true;
|
|
482
|
+
const ldata = lc.data;
|
|
483
|
+
if (ldata) {
|
|
484
|
+
const rdata = rc?.data;
|
|
485
|
+
for (const [k, v2] of Object.entries(ldata)) {
|
|
486
|
+
if (v2 != null && rdata?.[k] !== v2) return true;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
function disablesOnlineStore(local, remote) {
|
|
493
|
+
return local.onlineStore?.enabled === false && remote?.onlineStore?.enabled === true;
|
|
494
|
+
}
|
|
495
|
+
function definitionChanges(local, remote) {
|
|
496
|
+
const changes = [];
|
|
497
|
+
if (local.name != null && local.name !== remote.name) changes.push("name");
|
|
498
|
+
if (local.description != null && local.description !== remote.description) changes.push("description");
|
|
499
|
+
if (local.displayNameKey != null && local.displayNameKey !== remote.displayNameKey) changes.push("displayNameKey");
|
|
500
|
+
if (local.access && accessChanged(local.access, remote.access)) changes.push("access");
|
|
501
|
+
if (local.capabilities && capabilitiesChanged(local.capabilities, remote.capabilities)) changes.push("capabilities");
|
|
502
|
+
return changes;
|
|
503
|
+
}
|
|
408
504
|
function diff(local, remote) {
|
|
409
505
|
const ops = [];
|
|
410
506
|
const remoteByType = new Map(remote.map((d) => [d.type, d]));
|
|
@@ -428,6 +524,7 @@ function diff(local, remote) {
|
|
|
428
524
|
}
|
|
429
525
|
const changes = {};
|
|
430
526
|
if (rf.required !== lf.required) changes.required = lf.required;
|
|
527
|
+
if (rf.filterable !== lf.filterable) changes.filterable = lf.filterable;
|
|
431
528
|
if (!sameValidations(rf.validations, lf.validations)) changes.validations = lf.validations;
|
|
432
529
|
if (Object.keys(changes).length) {
|
|
433
530
|
ops.push({ kind: "updateField", type: localDef.type, key: lf.key, changes });
|
|
@@ -438,49 +535,176 @@ function diff(local, remote) {
|
|
|
438
535
|
ops.push({ kind: "removeField", type: localDef.type, key: rf.key, destructive: true });
|
|
439
536
|
}
|
|
440
537
|
}
|
|
538
|
+
const metaChanges = definitionChanges(localDef, remoteDef);
|
|
539
|
+
if (metaChanges.length) {
|
|
540
|
+
const destructive = localDef.capabilities ? disablesOnlineStore(localDef.capabilities, remoteDef.capabilities) : false;
|
|
541
|
+
ops.push({
|
|
542
|
+
kind: "updateDefinition",
|
|
543
|
+
type: localDef.type,
|
|
544
|
+
changes: metaChanges,
|
|
545
|
+
...destructive ? { destructive: true } : {}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
441
548
|
}
|
|
442
549
|
return ops;
|
|
443
550
|
}
|
|
444
551
|
|
|
445
552
|
// src/sync/normalize.ts
|
|
446
|
-
function
|
|
447
|
-
const
|
|
448
|
-
|
|
553
|
+
function localCapabilities(caps) {
|
|
554
|
+
const out = {};
|
|
555
|
+
if (caps?.publishable) out.publishable = { enabled: caps.publishable.enabled };
|
|
556
|
+
if (caps?.translatable) out.translatable = { enabled: caps.translatable.enabled };
|
|
557
|
+
if (caps?.renderable) {
|
|
558
|
+
out.renderable = { enabled: caps.renderable.enabled };
|
|
559
|
+
if (caps.renderable.data) out.renderable.data = { ...caps.renderable.data };
|
|
560
|
+
}
|
|
561
|
+
out.onlineStore = caps?.onlineStore ? { enabled: caps.onlineStore.enabled, data: { urlHandle: caps.onlineStore.data?.urlHandle } } : { enabled: false };
|
|
562
|
+
return out;
|
|
563
|
+
}
|
|
564
|
+
function normalizeDefinition(def) {
|
|
565
|
+
const out = {
|
|
449
566
|
type: def.type,
|
|
450
567
|
name: def.name,
|
|
568
|
+
capabilities: localCapabilities(def.capabilities),
|
|
451
569
|
fields: def.fieldDefinitions.map((f) => ({
|
|
452
570
|
key: f.key,
|
|
453
571
|
type: f.type,
|
|
454
572
|
required: f.required,
|
|
573
|
+
filterable: f.capabilities?.adminFilterable?.enabled ?? false,
|
|
455
574
|
validations: f.validations
|
|
456
575
|
}))
|
|
457
576
|
};
|
|
577
|
+
if (def.description != null) out.description = def.description;
|
|
578
|
+
if (def.displayNameKey != null) out.displayNameKey = def.displayNameKey;
|
|
579
|
+
if (def.access) out.access = { ...def.access };
|
|
580
|
+
return out;
|
|
581
|
+
}
|
|
582
|
+
function remoteAccess(access) {
|
|
583
|
+
if (!access) return void 0;
|
|
584
|
+
const out = {};
|
|
585
|
+
if (access.admin != null) out.admin = access.admin;
|
|
586
|
+
if (access.storefront != null) out.storefront = access.storefront;
|
|
587
|
+
if (access.customerAccount != null) out.customerAccount = access.customerAccount;
|
|
588
|
+
return out;
|
|
589
|
+
}
|
|
590
|
+
function remoteCapabilities(caps) {
|
|
591
|
+
const out = {};
|
|
592
|
+
if (!caps) return out;
|
|
593
|
+
if (caps.publishable) out.publishable = { enabled: !!caps.publishable.enabled };
|
|
594
|
+
if (caps.translatable) out.translatable = { enabled: !!caps.translatable.enabled };
|
|
595
|
+
if (caps.renderable) {
|
|
596
|
+
out.renderable = { enabled: !!caps.renderable.enabled };
|
|
597
|
+
const d = caps.renderable.data;
|
|
598
|
+
if (d) {
|
|
599
|
+
const data = {};
|
|
600
|
+
if (d.metaTitleKey != null) data.metaTitleKey = d.metaTitleKey;
|
|
601
|
+
if (d.metaDescriptionKey != null) data.metaDescriptionKey = d.metaDescriptionKey;
|
|
602
|
+
if (Object.keys(data).length) out.renderable.data = data;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (caps.onlineStore) {
|
|
606
|
+
out.onlineStore = { enabled: !!caps.onlineStore.enabled };
|
|
607
|
+
if (caps.onlineStore.data?.urlHandle != null) out.onlineStore.data = { urlHandle: caps.onlineStore.data.urlHandle };
|
|
608
|
+
}
|
|
609
|
+
return out;
|
|
458
610
|
}
|
|
459
611
|
function normalizeRemote(def) {
|
|
460
|
-
|
|
612
|
+
const out = {
|
|
461
613
|
type: def.type,
|
|
462
614
|
name: def.name,
|
|
615
|
+
capabilities: remoteCapabilities(def.capabilities),
|
|
463
616
|
fields: def.fieldDefinitions.map((f) => ({
|
|
464
617
|
key: f.key,
|
|
465
618
|
type: typeof f.type === "string" ? f.type : f.type.name,
|
|
466
619
|
required: f.required,
|
|
620
|
+
filterable: f.capabilities?.adminFilterable?.enabled ?? false,
|
|
467
621
|
validations: f.validations ?? []
|
|
468
622
|
}))
|
|
469
623
|
};
|
|
624
|
+
if (def.description != null) out.description = def.description;
|
|
625
|
+
if (def.displayNameKey != null) out.displayNameKey = def.displayNameKey;
|
|
626
|
+
const access = remoteAccess(def.access);
|
|
627
|
+
if (access) out.access = access;
|
|
628
|
+
return out;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/sync/resolve.ts
|
|
632
|
+
var APP_PREFIX2 = "$app:";
|
|
633
|
+
function effectiveScope(schema, config) {
|
|
634
|
+
return schema.config.scope ?? config.scope ?? "app";
|
|
635
|
+
}
|
|
636
|
+
function effectiveType(handle, scope) {
|
|
637
|
+
return scope === "merchant" ? handle : `${APP_PREFIX2}${handle}`;
|
|
638
|
+
}
|
|
639
|
+
function rewriteReference(v2, effByCanonical) {
|
|
640
|
+
if (v2.name === "metaobject_definition_type") {
|
|
641
|
+
const mapped = effByCanonical.get(v2.value);
|
|
642
|
+
return mapped ? { ...v2, value: mapped } : v2;
|
|
643
|
+
}
|
|
644
|
+
if (v2.name === "metaobject_definition_types") {
|
|
645
|
+
try {
|
|
646
|
+
const parsed = JSON.parse(v2.value);
|
|
647
|
+
if (Array.isArray(parsed)) {
|
|
648
|
+
const rewritten = parsed.map((t) => typeof t === "string" ? effByCanonical.get(t) ?? t : t);
|
|
649
|
+
return { ...v2, value: JSON.stringify(rewritten) };
|
|
650
|
+
}
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return v2;
|
|
655
|
+
}
|
|
656
|
+
function resolveAdmin(out, handle, scope, config) {
|
|
657
|
+
const explicitAdmin = out.access?.admin;
|
|
658
|
+
if (scope === "merchant") {
|
|
659
|
+
if (explicitAdmin != null) {
|
|
660
|
+
throw new Error(
|
|
661
|
+
`"${handle}" is merchant-scoped but sets access.admin; admin access is only valid on app-scoped metaobjects.`
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (explicitAdmin == null) {
|
|
667
|
+
const admin = config.merchantEditable ? "MERCHANT_READ_WRITE" : "MERCHANT_READ";
|
|
668
|
+
out.access = { ...out.access, admin };
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function resolveDefinitions(schemas, config = {}) {
|
|
672
|
+
const effByCanonical = /* @__PURE__ */ new Map();
|
|
673
|
+
for (const s of schemas) {
|
|
674
|
+
effByCanonical.set(`${APP_PREFIX2}${s.handle}`, effectiveType(s.handle, effectiveScope(s, config)));
|
|
675
|
+
}
|
|
676
|
+
return schemas.map((s) => {
|
|
677
|
+
const scope = effectiveScope(s, config);
|
|
678
|
+
const base = s.toDefinitionInput();
|
|
679
|
+
const fieldDefinitions = base.fieldDefinitions.map((f) => ({
|
|
680
|
+
...f,
|
|
681
|
+
validations: f.validations.map((v2) => rewriteReference(v2, effByCanonical))
|
|
682
|
+
}));
|
|
683
|
+
const out = { ...base, type: effectiveType(s.handle, scope), fieldDefinitions };
|
|
684
|
+
resolveAdmin(out, s.handle, scope, config);
|
|
685
|
+
return out;
|
|
686
|
+
});
|
|
470
687
|
}
|
|
471
688
|
|
|
472
689
|
// src/sync/pull.ts
|
|
690
|
+
function toDefinition(type, node) {
|
|
691
|
+
return {
|
|
692
|
+
type,
|
|
693
|
+
name: node.name,
|
|
694
|
+
description: node.description,
|
|
695
|
+
displayNameKey: node.displayNameKey,
|
|
696
|
+
access: node.access,
|
|
697
|
+
capabilities: node.capabilities,
|
|
698
|
+
fieldDefinitions: node.fieldDefinitions
|
|
699
|
+
};
|
|
700
|
+
}
|
|
473
701
|
async function pull(client, types) {
|
|
474
702
|
const out = [];
|
|
475
703
|
for (const type of types) {
|
|
476
704
|
const data = await execute(client, PULL_DEFINITION_QUERY, { type });
|
|
477
705
|
const node = data.metaobjectDefinitionByType;
|
|
478
706
|
if (!node) continue;
|
|
479
|
-
out.push({
|
|
480
|
-
id: node.id,
|
|
481
|
-
type,
|
|
482
|
-
definition: { type, name: node.name, fieldDefinitions: node.fieldDefinitions }
|
|
483
|
-
});
|
|
707
|
+
out.push({ id: node.id, type, definition: toDefinition(type, node) });
|
|
484
708
|
}
|
|
485
709
|
return out;
|
|
486
710
|
}
|
|
@@ -498,7 +722,7 @@ async function pullAll(client, opts = {}) {
|
|
|
498
722
|
const canonical = toCanonicalType(node.type);
|
|
499
723
|
if (appOwnedOnly && !canonical) continue;
|
|
500
724
|
const type = canonical ?? node.type;
|
|
501
|
-
out.push({ id: node.id, type, definition:
|
|
725
|
+
out.push({ id: node.id, type, definition: toDefinition(type, node) });
|
|
502
726
|
}
|
|
503
727
|
after = data.metaobjectDefinitions.pageInfo.hasNextPage ? data.metaobjectDefinitions.pageInfo.endCursor : null;
|
|
504
728
|
} while (after !== null);
|
|
@@ -506,6 +730,25 @@ async function pullAll(client, opts = {}) {
|
|
|
506
730
|
}
|
|
507
731
|
|
|
508
732
|
// src/sync/push.ts
|
|
733
|
+
function definitionUpdateFor(def, changes) {
|
|
734
|
+
const out = {};
|
|
735
|
+
for (const c of changes) {
|
|
736
|
+
if (c === "name") out.name = def.name;
|
|
737
|
+
else if (c === "description") out.description = def.description;
|
|
738
|
+
else if (c === "displayNameKey") out.displayNameKey = def.displayNameKey;
|
|
739
|
+
else if (c === "access") out.access = def.access;
|
|
740
|
+
else if (c === "capabilities") {
|
|
741
|
+
const caps = def.capabilities;
|
|
742
|
+
const payload = {};
|
|
743
|
+
if (caps?.publishable) payload.publishable = caps.publishable;
|
|
744
|
+
if (caps?.translatable) payload.translatable = caps.translatable;
|
|
745
|
+
if (caps?.renderable) payload.renderable = caps.renderable;
|
|
746
|
+
payload.onlineStore = caps?.onlineStore ?? { enabled: false };
|
|
747
|
+
out.capabilities = payload;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return out;
|
|
751
|
+
}
|
|
509
752
|
function fieldInputFor(defByType, type, key) {
|
|
510
753
|
return defByType.get(type)?.fieldDefinitions.find((f) => f.key === key);
|
|
511
754
|
}
|
|
@@ -606,14 +849,22 @@ async function push(client, plan, sources, options) {
|
|
|
606
849
|
if (id2) idByType.set(op.type, id2);
|
|
607
850
|
return { op, status: "applied", id: id2 };
|
|
608
851
|
}
|
|
609
|
-
const destructive =
|
|
852
|
+
const destructive = "destructive" in op && op.destructive === true;
|
|
610
853
|
if (destructive && !allowDestructive) return { op, status: "skipped", reason: "destructive" };
|
|
611
854
|
if (failedTypes.has(op.type)) return { op, status: "blocked", reason: `blocked: definition "${op.type}" was not created` };
|
|
612
855
|
const id = idByType.get(op.type);
|
|
613
856
|
if (id == null) return { op, status: "blocked", reason: `no definition id for "${op.type}"` };
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
857
|
+
let definition;
|
|
858
|
+
if (op.kind === "updateDefinition") {
|
|
859
|
+
const def = defByType.get(op.type);
|
|
860
|
+
if (!def) return { op, status: "blocked", reason: `no definition input for "${op.type}"` };
|
|
861
|
+
definition = definitionUpdateFor(def, op.changes);
|
|
862
|
+
} else {
|
|
863
|
+
const fieldDefinitions = fieldOpsFor(op);
|
|
864
|
+
if (!fieldDefinitions) return { op, status: "blocked", reason: `no field input for "${op.type}"` };
|
|
865
|
+
definition = { fieldDefinitions };
|
|
866
|
+
}
|
|
867
|
+
const data = await execute(client, UPDATE_DEFINITION_MUTATION, { id, definition });
|
|
617
868
|
const payload = data.metaobjectDefinitionUpdate;
|
|
618
869
|
if (payload.userErrors.length) return { op, status: "failed", userErrors: payload.userErrors };
|
|
619
870
|
return { op, status: "applied", id: payload.metaobjectDefinition?.id ?? id };
|
|
@@ -634,6 +885,9 @@ async function push(client, plan, sources, options) {
|
|
|
634
885
|
validations: field.validations
|
|
635
886
|
};
|
|
636
887
|
if (field.description != null) update.description = field.description;
|
|
888
|
+
if ("filterable" in op.changes) {
|
|
889
|
+
update.capabilities = { adminFilterable: { enabled: op.changes.filterable ?? false } };
|
|
890
|
+
}
|
|
637
891
|
return [{ update }];
|
|
638
892
|
}
|
|
639
893
|
case "removeField":
|
|
@@ -654,12 +908,31 @@ async function push(client, plan, sources, options) {
|
|
|
654
908
|
}
|
|
655
909
|
|
|
656
910
|
// src/cli/plan.ts
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const
|
|
911
|
+
var APP_PREFIX3 = "$app:";
|
|
912
|
+
async function detectScopeFlips(client, definitions, plan) {
|
|
913
|
+
const merchant = definitions.filter((d) => !d.type.startsWith(APP_PREFIX3));
|
|
914
|
+
if (merchant.length === 0) return [];
|
|
915
|
+
const created = new Set(plan.filter((op) => op.kind === "createDefinition").map((op) => op.type));
|
|
916
|
+
const shadows = await pull(client, merchant.map((d) => `${APP_PREFIX3}${d.type}`));
|
|
917
|
+
const shadowTypes = new Set(shadows.map((s) => s.type));
|
|
918
|
+
const warnings = [];
|
|
919
|
+
for (const d of merchant) {
|
|
920
|
+
if (created.has(d.type) && shadowTypes.has(`${APP_PREFIX3}${d.type}`)) {
|
|
921
|
+
warnings.push(
|
|
922
|
+
`"${d.type}" is merchant-scoped but an app-owned "${APP_PREFIX3}${d.type}" already exists remotely. Scope changes are not migrated (type is immutable): a new merchant-owned definition will be created and the app-owned one left orphaned. Migrate manually \u2014 see docs/SYNC.md.`
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return warnings;
|
|
927
|
+
}
|
|
928
|
+
async function planFor(client, schemas, config = {}) {
|
|
929
|
+
const definitions = resolveDefinitions(schemas, config);
|
|
930
|
+
const types = definitions.map((d) => d.type);
|
|
931
|
+
const localDefs = definitions.map(normalizeDefinition);
|
|
660
932
|
const remote = await pull(client, types);
|
|
661
933
|
const plan = diff(localDefs, remote.map((r) => normalizeRemote(r.definition)));
|
|
662
|
-
|
|
934
|
+
const warnings = await detectScopeFlips(client, definitions, plan);
|
|
935
|
+
return { plan, remote, definitions, warnings };
|
|
663
936
|
}
|
|
664
937
|
|
|
665
938
|
// src/cli/format.ts
|
|
@@ -690,7 +963,8 @@ function describeResult(r) {
|
|
|
690
963
|
|
|
691
964
|
// src/cli/diff.ts
|
|
692
965
|
async function runDiff(args) {
|
|
693
|
-
const { plan } = await planFor(args.client, args.schemas);
|
|
966
|
+
const { plan, warnings } = await planFor(args.client, args.schemas, args.config);
|
|
967
|
+
for (const w of warnings) console.warn(`Warning: ${w}`);
|
|
694
968
|
if (plan.length === 0) {
|
|
695
969
|
console.log("Everything is in sync \u2014 nothing to apply.");
|
|
696
970
|
} else {
|
|
@@ -702,8 +976,8 @@ async function runDiff(args) {
|
|
|
702
976
|
|
|
703
977
|
// src/cli/push.ts
|
|
704
978
|
async function runPush(args) {
|
|
705
|
-
const { plan, remote } = await planFor(args.client, args.schemas);
|
|
706
|
-
const
|
|
979
|
+
const { plan, remote, definitions, warnings } = await planFor(args.client, args.schemas, args.config);
|
|
980
|
+
for (const w of warnings) console.warn(`Warning: ${w}`);
|
|
707
981
|
const result = await push(args.client, plan, { definitions, remote }, { allowDestructive: args.allowDestructive });
|
|
708
982
|
for (const r of result.results) console.log(` ${describeResult(r)}`);
|
|
709
983
|
console.log(
|
|
@@ -789,11 +1063,11 @@ async function main(argv) {
|
|
|
789
1063
|
}
|
|
790
1064
|
const schemas = await loadSchemas(config.schema);
|
|
791
1065
|
if (args.command === "diff") {
|
|
792
|
-
await runDiff({ client, schemas });
|
|
1066
|
+
await runDiff({ client, schemas, config });
|
|
793
1067
|
return 0;
|
|
794
1068
|
}
|
|
795
1069
|
if (args.command === "push") {
|
|
796
|
-
const result = await runPush({ client, schemas, allowDestructive: args.allowDestructive });
|
|
1070
|
+
const result = await runPush({ client, schemas, config, allowDestructive: args.allowDestructive });
|
|
797
1071
|
return result.ok ? 0 : 2;
|
|
798
1072
|
}
|
|
799
1073
|
console.error(`Unknown command: ${args.command}`);
|