@fmaplabs/meta-manifest 0.1.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 +49 -4
- 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 +324 -39
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +49 -17
- 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 }
|
|
@@ -149,9 +161,19 @@ async function loadSchemas(schemaPath) {
|
|
|
149
161
|
return mod.schemas;
|
|
150
162
|
}
|
|
151
163
|
|
|
164
|
+
// src/cli/load-env.ts
|
|
165
|
+
var import_node_path2 = require("path");
|
|
166
|
+
function loadDotEnv(cwd = process.cwd()) {
|
|
167
|
+
try {
|
|
168
|
+
process.loadEnvFile((0, import_node_path2.resolve)(cwd, ".env"));
|
|
169
|
+
} catch (err) {
|
|
170
|
+
if (err.code !== "ENOENT") throw err;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
152
174
|
// src/cli/init.ts
|
|
153
175
|
var import_node_fs = require("fs");
|
|
154
|
-
var
|
|
176
|
+
var import_node_path3 = require("path");
|
|
155
177
|
var CONFIG_TEMPLATE = `import { defineConfig } from "@fmaplabs/meta-manifest";
|
|
156
178
|
|
|
157
179
|
export default defineConfig({
|
|
@@ -176,9 +198,9 @@ async function runInit(opts = {}) {
|
|
|
176
198
|
const cwd = opts.cwd ?? process.cwd();
|
|
177
199
|
const created = [];
|
|
178
200
|
const write = (rel, contents) => {
|
|
179
|
-
const abs = (0,
|
|
201
|
+
const abs = (0, import_node_path3.join)(cwd, rel);
|
|
180
202
|
if ((0, import_node_fs.existsSync)(abs)) return;
|
|
181
|
-
(0, import_node_fs.mkdirSync)((0,
|
|
203
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path3.dirname)(abs), { recursive: true });
|
|
182
204
|
(0, import_node_fs.writeFileSync)(abs, contents);
|
|
183
205
|
created.push(rel);
|
|
184
206
|
};
|
|
@@ -186,7 +208,7 @@ async function runInit(opts = {}) {
|
|
|
186
208
|
write("src/schema.ts", SCHEMA_TEMPLATE);
|
|
187
209
|
if (created.length) {
|
|
188
210
|
console.log(`Created: ${created.join(", ")}`);
|
|
189
|
-
console.log("Next: set SHOPIFY_ADMIN_TOKEN
|
|
211
|
+
console.log("Next: set SHOPIFY_ADMIN_TOKEN (export it or add it to .env), edit meta-manifest.config.ts, then run `mm diff`.");
|
|
190
212
|
} else {
|
|
191
213
|
console.log("Nothing to do \u2014 config and schema already exist.");
|
|
192
214
|
}
|
|
@@ -195,6 +217,12 @@ async function runInit(opts = {}) {
|
|
|
195
217
|
|
|
196
218
|
// src/codegen.ts
|
|
197
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
|
+
}
|
|
198
226
|
var SIMPLE = {
|
|
199
227
|
single_line_text_field: "text",
|
|
200
228
|
multi_line_text_field: "multilineText",
|
|
@@ -276,8 +304,8 @@ function scalarEntries(field, warnings, builder) {
|
|
|
276
304
|
jsonArr("file_type_options", "accept");
|
|
277
305
|
return e;
|
|
278
306
|
}
|
|
279
|
-
function scalarCall(builder, field, warnings) {
|
|
280
|
-
const lit = optsLiteral(scalarEntries(field, warnings, builder));
|
|
307
|
+
function scalarCall(builder, field, warnings, extra = []) {
|
|
308
|
+
const lit = optsLiteral([...scalarEntries(field, warnings, builder), ...extra]);
|
|
281
309
|
return lit ? `m.${builder}(${lit})` : `m.${builder}()`;
|
|
282
310
|
}
|
|
283
311
|
function fieldCall(field, typeToIdent, warnings) {
|
|
@@ -287,6 +315,7 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
287
315
|
const max = v(field.validations, "max");
|
|
288
316
|
const e = [`min: ${Number(min ?? 1)}`, `max: ${Number(max ?? 5)}`];
|
|
289
317
|
if (field.required) e.unshift("required: true");
|
|
318
|
+
e.push(...filterableEntry(field));
|
|
290
319
|
if (min === void 0 || max === void 0) warnings.push(`rating field "${field.key}" missing min/max`);
|
|
291
320
|
return `m.rating(${optsLiteral(e)})`;
|
|
292
321
|
}
|
|
@@ -297,7 +326,11 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
297
326
|
warnings.push(`unresolved reference on field "${field.key}"`);
|
|
298
327
|
return `m.json() /* TODO: unmapped reference */`;
|
|
299
328
|
}
|
|
300
|
-
|
|
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})`;
|
|
301
334
|
}
|
|
302
335
|
if (type.startsWith("list.")) {
|
|
303
336
|
const inner = type.slice("list.".length);
|
|
@@ -307,6 +340,7 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
307
340
|
const max = v(field.validations, "list.max");
|
|
308
341
|
if (min !== void 0) listEntries.push(`min: ${Number(min)}`);
|
|
309
342
|
if (max !== void 0) listEntries.push(`max: ${Number(max)}`);
|
|
343
|
+
listEntries.push(...filterableEntry(field));
|
|
310
344
|
const listOpts = optsLiteral(listEntries);
|
|
311
345
|
let innerCall;
|
|
312
346
|
if (inner === "metaobject_reference") {
|
|
@@ -325,17 +359,53 @@ function fieldCall(field, typeToIdent, warnings) {
|
|
|
325
359
|
}
|
|
326
360
|
return listOpts ? `m.list(${innerCall}, ${listOpts})` : `m.list(${innerCall})`;
|
|
327
361
|
}
|
|
328
|
-
if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings);
|
|
362
|
+
if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings, filterableEntry(field));
|
|
329
363
|
warnings.push(`unmapped field type "${type}" on field "${field.key}"`);
|
|
330
364
|
return `m.json() /* TODO: unmapped type ${type} */`;
|
|
331
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
|
+
}
|
|
332
404
|
function defSource(def, typeToIdent, warnings) {
|
|
333
405
|
const ident = typeToIdent.get(def.type);
|
|
334
406
|
const handle = handleOf(def.type);
|
|
335
407
|
const fields = def.fields.map((f) => ` ${f.key}: ${fieldCall(f, typeToIdent, warnings)},`).join("\n");
|
|
336
|
-
const
|
|
337
|
-
name: ${JSON.stringify(def.name)},` : "";
|
|
338
|
-
return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${name}
|
|
408
|
+
return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${defConfigLines(def)}
|
|
339
409
|
fields: {
|
|
340
410
|
${fields}
|
|
341
411
|
},
|
|
@@ -395,6 +465,42 @@ function sameValidations(a, b) {
|
|
|
395
465
|
const norm = (v2) => JSON.stringify([...v2].sort((x, y) => x.name.localeCompare(y.name)));
|
|
396
466
|
return norm(a) === norm(b);
|
|
397
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
|
+
}
|
|
398
504
|
function diff(local, remote) {
|
|
399
505
|
const ops = [];
|
|
400
506
|
const remoteByType = new Map(remote.map((d) => [d.type, d]));
|
|
@@ -418,6 +524,7 @@ function diff(local, remote) {
|
|
|
418
524
|
}
|
|
419
525
|
const changes = {};
|
|
420
526
|
if (rf.required !== lf.required) changes.required = lf.required;
|
|
527
|
+
if (rf.filterable !== lf.filterable) changes.filterable = lf.filterable;
|
|
421
528
|
if (!sameValidations(rf.validations, lf.validations)) changes.validations = lf.validations;
|
|
422
529
|
if (Object.keys(changes).length) {
|
|
423
530
|
ops.push({ kind: "updateField", type: localDef.type, key: lf.key, changes });
|
|
@@ -428,49 +535,176 @@ function diff(local, remote) {
|
|
|
428
535
|
ops.push({ kind: "removeField", type: localDef.type, key: rf.key, destructive: true });
|
|
429
536
|
}
|
|
430
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
|
+
}
|
|
431
548
|
}
|
|
432
549
|
return ops;
|
|
433
550
|
}
|
|
434
551
|
|
|
435
552
|
// src/sync/normalize.ts
|
|
436
|
-
function
|
|
437
|
-
const
|
|
438
|
-
|
|
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 = {
|
|
439
566
|
type: def.type,
|
|
440
567
|
name: def.name,
|
|
568
|
+
capabilities: localCapabilities(def.capabilities),
|
|
441
569
|
fields: def.fieldDefinitions.map((f) => ({
|
|
442
570
|
key: f.key,
|
|
443
571
|
type: f.type,
|
|
444
572
|
required: f.required,
|
|
573
|
+
filterable: f.capabilities?.adminFilterable?.enabled ?? false,
|
|
445
574
|
validations: f.validations
|
|
446
575
|
}))
|
|
447
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;
|
|
448
610
|
}
|
|
449
611
|
function normalizeRemote(def) {
|
|
450
|
-
|
|
612
|
+
const out = {
|
|
451
613
|
type: def.type,
|
|
452
614
|
name: def.name,
|
|
615
|
+
capabilities: remoteCapabilities(def.capabilities),
|
|
453
616
|
fields: def.fieldDefinitions.map((f) => ({
|
|
454
617
|
key: f.key,
|
|
455
618
|
type: typeof f.type === "string" ? f.type : f.type.name,
|
|
456
619
|
required: f.required,
|
|
620
|
+
filterable: f.capabilities?.adminFilterable?.enabled ?? false,
|
|
457
621
|
validations: f.validations ?? []
|
|
458
622
|
}))
|
|
459
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
|
+
});
|
|
460
687
|
}
|
|
461
688
|
|
|
462
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
|
+
}
|
|
463
701
|
async function pull(client, types) {
|
|
464
702
|
const out = [];
|
|
465
703
|
for (const type of types) {
|
|
466
704
|
const data = await execute(client, PULL_DEFINITION_QUERY, { type });
|
|
467
705
|
const node = data.metaobjectDefinitionByType;
|
|
468
706
|
if (!node) continue;
|
|
469
|
-
out.push({
|
|
470
|
-
id: node.id,
|
|
471
|
-
type,
|
|
472
|
-
definition: { type, name: node.name, fieldDefinitions: node.fieldDefinitions }
|
|
473
|
-
});
|
|
707
|
+
out.push({ id: node.id, type, definition: toDefinition(type, node) });
|
|
474
708
|
}
|
|
475
709
|
return out;
|
|
476
710
|
}
|
|
@@ -488,7 +722,7 @@ async function pullAll(client, opts = {}) {
|
|
|
488
722
|
const canonical = toCanonicalType(node.type);
|
|
489
723
|
if (appOwnedOnly && !canonical) continue;
|
|
490
724
|
const type = canonical ?? node.type;
|
|
491
|
-
out.push({ id: node.id, type, definition:
|
|
725
|
+
out.push({ id: node.id, type, definition: toDefinition(type, node) });
|
|
492
726
|
}
|
|
493
727
|
after = data.metaobjectDefinitions.pageInfo.hasNextPage ? data.metaobjectDefinitions.pageInfo.endCursor : null;
|
|
494
728
|
} while (after !== null);
|
|
@@ -496,6 +730,25 @@ async function pullAll(client, opts = {}) {
|
|
|
496
730
|
}
|
|
497
731
|
|
|
498
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
|
+
}
|
|
499
752
|
function fieldInputFor(defByType, type, key) {
|
|
500
753
|
return defByType.get(type)?.fieldDefinitions.find((f) => f.key === key);
|
|
501
754
|
}
|
|
@@ -596,14 +849,22 @@ async function push(client, plan, sources, options) {
|
|
|
596
849
|
if (id2) idByType.set(op.type, id2);
|
|
597
850
|
return { op, status: "applied", id: id2 };
|
|
598
851
|
}
|
|
599
|
-
const destructive =
|
|
852
|
+
const destructive = "destructive" in op && op.destructive === true;
|
|
600
853
|
if (destructive && !allowDestructive) return { op, status: "skipped", reason: "destructive" };
|
|
601
854
|
if (failedTypes.has(op.type)) return { op, status: "blocked", reason: `blocked: definition "${op.type}" was not created` };
|
|
602
855
|
const id = idByType.get(op.type);
|
|
603
856
|
if (id == null) return { op, status: "blocked", reason: `no definition id for "${op.type}"` };
|
|
604
|
-
|
|
605
|
-
if (
|
|
606
|
-
|
|
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 });
|
|
607
868
|
const payload = data.metaobjectDefinitionUpdate;
|
|
608
869
|
if (payload.userErrors.length) return { op, status: "failed", userErrors: payload.userErrors };
|
|
609
870
|
return { op, status: "applied", id: payload.metaobjectDefinition?.id ?? id };
|
|
@@ -624,6 +885,9 @@ async function push(client, plan, sources, options) {
|
|
|
624
885
|
validations: field.validations
|
|
625
886
|
};
|
|
626
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
|
+
}
|
|
627
891
|
return [{ update }];
|
|
628
892
|
}
|
|
629
893
|
case "removeField":
|
|
@@ -644,12 +908,31 @@ async function push(client, plan, sources, options) {
|
|
|
644
908
|
}
|
|
645
909
|
|
|
646
910
|
// src/cli/plan.ts
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
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);
|
|
650
932
|
const remote = await pull(client, types);
|
|
651
933
|
const plan = diff(localDefs, remote.map((r) => normalizeRemote(r.definition)));
|
|
652
|
-
|
|
934
|
+
const warnings = await detectScopeFlips(client, definitions, plan);
|
|
935
|
+
return { plan, remote, definitions, warnings };
|
|
653
936
|
}
|
|
654
937
|
|
|
655
938
|
// src/cli/format.ts
|
|
@@ -680,7 +963,8 @@ function describeResult(r) {
|
|
|
680
963
|
|
|
681
964
|
// src/cli/diff.ts
|
|
682
965
|
async function runDiff(args) {
|
|
683
|
-
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}`);
|
|
684
968
|
if (plan.length === 0) {
|
|
685
969
|
console.log("Everything is in sync \u2014 nothing to apply.");
|
|
686
970
|
} else {
|
|
@@ -692,8 +976,8 @@ async function runDiff(args) {
|
|
|
692
976
|
|
|
693
977
|
// src/cli/push.ts
|
|
694
978
|
async function runPush(args) {
|
|
695
|
-
const { plan, remote } = await planFor(args.client, args.schemas);
|
|
696
|
-
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}`);
|
|
697
981
|
const result = await push(args.client, plan, { definitions, remote }, { allowDestructive: args.allowDestructive });
|
|
698
982
|
for (const r of result.results) console.log(` ${describeResult(r)}`);
|
|
699
983
|
console.log(
|
|
@@ -707,7 +991,7 @@ async function runPush(args) {
|
|
|
707
991
|
|
|
708
992
|
// src/cli/pull.ts
|
|
709
993
|
var import_node_fs2 = require("fs");
|
|
710
|
-
var
|
|
994
|
+
var import_node_path4 = require("path");
|
|
711
995
|
async function maybeFormat(source) {
|
|
712
996
|
try {
|
|
713
997
|
const spec = "prettier";
|
|
@@ -721,11 +1005,11 @@ async function runPull(args) {
|
|
|
721
1005
|
const remote = await pullAll(args.client);
|
|
722
1006
|
const defs = remote.map((r) => normalizeRemote(r.definition));
|
|
723
1007
|
const source = await maybeFormat(generateSchemaSource(defs));
|
|
724
|
-
const abs = (0,
|
|
1008
|
+
const abs = (0, import_node_path4.resolve)(process.cwd(), args.schemaPath);
|
|
725
1009
|
if ((0, import_node_fs2.existsSync)(abs) && !args.force) {
|
|
726
1010
|
console.warn(`Overwriting existing ${args.schemaPath}.`);
|
|
727
1011
|
}
|
|
728
|
-
(0, import_node_fs2.mkdirSync)((0,
|
|
1012
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path4.dirname)(abs), { recursive: true });
|
|
729
1013
|
(0, import_node_fs2.writeFileSync)(abs, source);
|
|
730
1014
|
console.log(`Wrote ${defs.length} definition${defs.length === 1 ? "" : "s"} to ${args.schemaPath}.`);
|
|
731
1015
|
return { written: abs, count: defs.length };
|
|
@@ -770,6 +1054,7 @@ async function main(argv) {
|
|
|
770
1054
|
await runInit();
|
|
771
1055
|
return 0;
|
|
772
1056
|
}
|
|
1057
|
+
loadDotEnv();
|
|
773
1058
|
const config = await loadConfig(args.config);
|
|
774
1059
|
const client = createAdminClient(config);
|
|
775
1060
|
if (args.command === "pull") {
|
|
@@ -778,11 +1063,11 @@ async function main(argv) {
|
|
|
778
1063
|
}
|
|
779
1064
|
const schemas = await loadSchemas(config.schema);
|
|
780
1065
|
if (args.command === "diff") {
|
|
781
|
-
await runDiff({ client, schemas });
|
|
1066
|
+
await runDiff({ client, schemas, config });
|
|
782
1067
|
return 0;
|
|
783
1068
|
}
|
|
784
1069
|
if (args.command === "push") {
|
|
785
|
-
const result = await runPush({ client, schemas, allowDestructive: args.allowDestructive });
|
|
1070
|
+
const result = await runPush({ client, schemas, config, allowDestructive: args.allowDestructive });
|
|
786
1071
|
return result.ok ? 0 : 2;
|
|
787
1072
|
}
|
|
788
1073
|
console.error(`Unknown command: ${args.command}`);
|