@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/dist/index.cjs CHANGED
@@ -28,11 +28,13 @@ __export(src_exports, {
28
28
  diff: () => diff,
29
29
  generateSchemaSource: () => generateSchemaSource,
30
30
  m: () => m,
31
+ normalizeDefinition: () => normalizeDefinition,
31
32
  normalizeLocal: () => normalizeLocal,
32
33
  normalizeRemote: () => normalizeRemote,
33
34
  pull: () => pull,
34
35
  pullAll: () => pullAll,
35
36
  push: () => push,
37
+ resolveDefinitions: () => resolveDefinitions,
36
38
  toDefinitionInput: () => toDefinitionInput,
37
39
  validateConfig: () => validateConfig
38
40
  });
@@ -60,6 +62,14 @@ var Field = class {
60
62
  required = false;
61
63
  name;
62
64
  description;
65
+ filterable = false;
66
+ /** Apply the shared field options; call from each concrete constructor. */
67
+ applyCommon(opts) {
68
+ this.required = opts.required ?? false;
69
+ this.name = opts.name;
70
+ this.description = opts.description;
71
+ this.filterable = opts.filterable ?? false;
72
+ }
63
73
  /** Constraint validation on an already-typed value. */
64
74
  check(_value) {
65
75
  return [];
@@ -116,9 +126,7 @@ var BooleanField = class extends Field {
116
126
  shopifyType = "boolean";
117
127
  constructor(opts) {
118
128
  super();
119
- this.required = opts.required ?? false;
120
- this.name = opts.name;
121
- this.description = opts.description;
129
+ this.applyCommon(opts);
122
130
  }
123
131
  validations() {
124
132
  return [];
@@ -144,9 +152,7 @@ var ListField = class extends Field {
144
152
  this.inner = inner;
145
153
  this.o = o;
146
154
  this.shopifyType = `list.${inner.shopifyType}`;
147
- this.required = o.required ?? false;
148
- this.name = o.name;
149
- this.description = o.description;
155
+ this.applyCommon(o);
150
156
  }
151
157
  inner;
152
158
  o;
@@ -182,9 +188,7 @@ var MeasurementField = class extends Field {
182
188
  constructor(opts, shopifyType) {
183
189
  super();
184
190
  this.shopifyType = shopifyType;
185
- this.required = opts.required ?? false;
186
- this.name = opts.name;
187
- this.description = opts.description;
191
+ this.applyCommon(opts);
188
192
  }
189
193
  shopifyType;
190
194
  wireIsJson = true;
@@ -220,9 +224,7 @@ var MoneyField = class extends Field {
220
224
  wireIsJson = true;
221
225
  constructor(opts) {
222
226
  super();
223
- this.required = opts.required ?? false;
224
- this.name = opts.name;
225
- this.description = opts.description;
227
+ this.applyCommon(opts);
226
228
  }
227
229
  validations() {
228
230
  return [];
@@ -248,9 +250,7 @@ var NumericField = class extends Field {
248
250
  constructor(opts) {
249
251
  super();
250
252
  this.opts = opts;
251
- this.required = opts.required ?? false;
252
- this.name = opts.name;
253
- this.description = opts.description;
253
+ this.applyCommon(opts);
254
254
  }
255
255
  opts;
256
256
  toJson(value) {
@@ -315,9 +315,7 @@ var RatingField = class extends Field {
315
315
  constructor(o) {
316
316
  super();
317
317
  this.o = o;
318
- this.required = o.required ?? false;
319
- this.name = o.name;
320
- this.description = o.description;
318
+ this.applyCommon(o);
321
319
  }
322
320
  o;
323
321
  shopifyType = "rating";
@@ -372,9 +370,7 @@ function rating(opts) {
372
370
  var GidField = class extends Field {
373
371
  constructor(opts) {
374
372
  super();
375
- this.required = opts.required ?? false;
376
- this.name = opts.name;
377
- this.description = opts.description;
373
+ this.applyCommon(opts);
378
374
  }
379
375
  toJson(value) {
380
376
  return value;
@@ -442,9 +438,7 @@ function ref(target, opts = {}) {
442
438
  var StringScalarField = class extends Field {
443
439
  constructor(opts) {
444
440
  super();
445
- this.required = opts.required ?? false;
446
- this.name = opts.name;
447
- this.description = opts.description;
441
+ this.applyCommon(opts);
448
442
  }
449
443
  toJson(value) {
450
444
  return value;
@@ -494,9 +488,7 @@ var JsonField = class extends Field {
494
488
  wireIsJson = true;
495
489
  constructor(opts) {
496
490
  super();
497
- this.required = opts.required ?? false;
498
- this.name = opts.name;
499
- this.description = opts.description;
491
+ this.applyCommon(opts);
500
492
  }
501
493
  validations() {
502
494
  return [];
@@ -530,9 +522,7 @@ var TextField = class extends Field {
530
522
  super();
531
523
  this.opts = opts;
532
524
  this.shopifyType = multiline ? "multi_line_text_field" : "single_line_text_field";
533
- this.required = opts.required ?? false;
534
- this.name = opts.name;
535
- this.description = opts.description;
525
+ this.applyCommon(opts);
536
526
  }
537
527
  opts;
538
528
  shopifyType;
@@ -600,16 +590,44 @@ function mapAccess(access) {
600
590
  const out = {};
601
591
  if (access.admin) out.admin = access.admin.toUpperCase();
602
592
  if (access.storefront) out.storefront = access.storefront.toUpperCase();
593
+ if (access.customerAccount) out.customerAccount = access.customerAccount.toUpperCase();
603
594
  return out;
604
595
  }
605
596
  function mapCapabilities(caps) {
606
597
  if (!caps) return void 0;
607
598
  const out = {};
608
- for (const [k, v2] of Object.entries(caps)) if (v2 != null) out[k] = { enabled: v2 };
599
+ if (caps.publishable != null) out.publishable = { enabled: caps.publishable };
600
+ if (caps.translatable != null) out.translatable = { enabled: caps.translatable };
601
+ if (caps.renderable != null) {
602
+ if (typeof caps.renderable === "boolean") {
603
+ out.renderable = { enabled: caps.renderable };
604
+ } else {
605
+ const data = {};
606
+ if (caps.renderable.metaTitleKey != null) data.metaTitleKey = caps.renderable.metaTitleKey;
607
+ if (caps.renderable.metaDescriptionKey != null) data.metaDescriptionKey = caps.renderable.metaDescriptionKey;
608
+ out.renderable = Object.keys(data).length ? { enabled: true, data } : { enabled: true };
609
+ }
610
+ }
611
+ if (caps.onlineStore != null) {
612
+ const data = { urlHandle: caps.onlineStore.urlHandle };
613
+ if (caps.onlineStore.createRedirects != null) data.createRedirects = caps.onlineStore.createRedirects;
614
+ out.onlineStore = { enabled: true, data };
615
+ }
609
616
  return Object.keys(out).length ? out : void 0;
610
617
  }
618
+ function validateSeoKeys(caps, fieldKeys, name) {
619
+ const r = caps?.renderable;
620
+ if (!r || typeof r === "boolean") return;
621
+ for (const prop of ["metaTitleKey", "metaDescriptionKey"]) {
622
+ const key = r[prop];
623
+ if (key != null && !fieldKeys.has(key)) {
624
+ throw new Error(`renderable.${prop} "${key}" is not a declared field key on "${name}".`);
625
+ }
626
+ }
627
+ }
611
628
  function toDefinitionInput(schema) {
612
629
  const { config } = schema;
630
+ validateSeoKeys(config.capabilities, new Set(Object.keys(schema.fields)), config.name);
613
631
  const fieldDefinitions = Object.entries(schema.fields).map(([key, field]) => {
614
632
  const def = {
615
633
  key,
@@ -619,6 +637,7 @@ function toDefinitionInput(schema) {
619
637
  validations: field.validations()
620
638
  };
621
639
  if (field.description != null) def.description = field.description;
640
+ if (field.filterable) def.capabilities = { adminFilterable: { enabled: true } };
622
641
  return def;
623
642
  });
624
643
  const out = {
@@ -691,6 +710,12 @@ function defineMetaobject(handle, config) {
691
710
 
692
711
  // src/codegen.ts
693
712
  var APP_PREFIX = "$app:";
713
+ function isMerchant(type) {
714
+ return !type.startsWith(APP_PREFIX);
715
+ }
716
+ function filterableEntry(field) {
717
+ return field.filterable ? ["filterable: true"] : [];
718
+ }
694
719
  var SIMPLE = {
695
720
  single_line_text_field: "text",
696
721
  multi_line_text_field: "multilineText",
@@ -772,8 +797,8 @@ function scalarEntries(field, warnings, builder) {
772
797
  jsonArr("file_type_options", "accept");
773
798
  return e;
774
799
  }
775
- function scalarCall(builder, field, warnings) {
776
- const lit = optsLiteral(scalarEntries(field, warnings, builder));
800
+ function scalarCall(builder, field, warnings, extra = []) {
801
+ const lit = optsLiteral([...scalarEntries(field, warnings, builder), ...extra]);
777
802
  return lit ? `m.${builder}(${lit})` : `m.${builder}()`;
778
803
  }
779
804
  function fieldCall(field, typeToIdent, warnings) {
@@ -783,6 +808,7 @@ function fieldCall(field, typeToIdent, warnings) {
783
808
  const max = v(field.validations, "max");
784
809
  const e = [`min: ${Number(min ?? 1)}`, `max: ${Number(max ?? 5)}`];
785
810
  if (field.required) e.unshift("required: true");
811
+ e.push(...filterableEntry(field));
786
812
  if (min === void 0 || max === void 0) warnings.push(`rating field "${field.key}" missing min/max`);
787
813
  return `m.rating(${optsLiteral(e)})`;
788
814
  }
@@ -793,7 +819,11 @@ function fieldCall(field, typeToIdent, warnings) {
793
819
  warnings.push(`unresolved reference on field "${field.key}"`);
794
820
  return `m.json() /* TODO: unmapped reference */`;
795
821
  }
796
- return field.required ? `m.ref(() => ${ident}, { required: true })` : `m.ref(() => ${ident})`;
822
+ const refEntries = [];
823
+ if (field.required) refEntries.push("required: true");
824
+ refEntries.push(...filterableEntry(field));
825
+ const opts = optsLiteral(refEntries);
826
+ return opts ? `m.ref(() => ${ident}, ${opts})` : `m.ref(() => ${ident})`;
797
827
  }
798
828
  if (type.startsWith("list.")) {
799
829
  const inner = type.slice("list.".length);
@@ -803,6 +833,7 @@ function fieldCall(field, typeToIdent, warnings) {
803
833
  const max = v(field.validations, "list.max");
804
834
  if (min !== void 0) listEntries.push(`min: ${Number(min)}`);
805
835
  if (max !== void 0) listEntries.push(`max: ${Number(max)}`);
836
+ listEntries.push(...filterableEntry(field));
806
837
  const listOpts = optsLiteral(listEntries);
807
838
  let innerCall;
808
839
  if (inner === "metaobject_reference") {
@@ -821,17 +852,53 @@ function fieldCall(field, typeToIdent, warnings) {
821
852
  }
822
853
  return listOpts ? `m.list(${innerCall}, ${listOpts})` : `m.list(${innerCall})`;
823
854
  }
824
- if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings);
855
+ if (SIMPLE[type]) return scalarCall(SIMPLE[type], field, warnings, filterableEntry(field));
825
856
  warnings.push(`unmapped field type "${type}" on field "${field.key}"`);
826
857
  return `m.json() /* TODO: unmapped type ${type} */`;
827
858
  }
859
+ function accessSource(access) {
860
+ if (!access) return "";
861
+ const parts = [];
862
+ if (access.admin === "MERCHANT_READ_WRITE") parts.push(`admin: "merchant_read_write"`);
863
+ if (access.storefront === "PUBLIC_READ") parts.push(`storefront: "public_read"`);
864
+ if (access.customerAccount === "READ") parts.push(`customerAccount: "read"`);
865
+ return parts.length ? `{ ${parts.join(", ")} }` : "";
866
+ }
867
+ function capabilitiesSource(caps) {
868
+ if (!caps) return "";
869
+ const parts = [];
870
+ if (caps.publishable?.enabled) parts.push(`publishable: true`);
871
+ if (caps.translatable?.enabled) parts.push(`translatable: true`);
872
+ if (caps.renderable?.enabled) {
873
+ const d = caps.renderable.data;
874
+ const dparts = [];
875
+ if (d?.metaTitleKey != null) dparts.push(`metaTitleKey: ${JSON.stringify(d.metaTitleKey)}`);
876
+ if (d?.metaDescriptionKey != null) dparts.push(`metaDescriptionKey: ${JSON.stringify(d.metaDescriptionKey)}`);
877
+ parts.push(dparts.length ? `renderable: { ${dparts.join(", ")} }` : `renderable: true`);
878
+ }
879
+ if (caps.onlineStore?.enabled && caps.onlineStore.data?.urlHandle != null) {
880
+ parts.push(`onlineStore: { urlHandle: ${JSON.stringify(caps.onlineStore.data.urlHandle)} }`);
881
+ }
882
+ return parts.length ? `{ ${parts.join(", ")} }` : "";
883
+ }
884
+ function defConfigLines(def) {
885
+ const lines = [];
886
+ if (isMerchant(def.type)) lines.push(` scope: "merchant",`);
887
+ if (def.name) lines.push(` name: ${JSON.stringify(def.name)},`);
888
+ if (def.description != null) lines.push(` description: ${JSON.stringify(def.description)},`);
889
+ if (def.displayNameKey != null) lines.push(` displayName: ${JSON.stringify(def.displayNameKey)},`);
890
+ const access = accessSource(def.access);
891
+ if (access) lines.push(` access: ${access},`);
892
+ const caps = capabilitiesSource(def.capabilities);
893
+ if (caps) lines.push(` capabilities: ${caps},`);
894
+ return lines.length ? `
895
+ ${lines.join("\n")}` : "";
896
+ }
828
897
  function defSource(def, typeToIdent, warnings) {
829
898
  const ident = typeToIdent.get(def.type);
830
899
  const handle = handleOf(def.type);
831
900
  const fields = def.fields.map((f) => ` ${f.key}: ${fieldCall(f, typeToIdent, warnings)},`).join("\n");
832
- const name = def.name ? `
833
- name: ${JSON.stringify(def.name)},` : "";
834
- return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${name}
901
+ return `export const ${ident} = defineMetaobject(${JSON.stringify(handle)}, {${defConfigLines(def)}
835
902
  fields: {
836
903
  ${fields}
837
904
  },
@@ -891,6 +958,42 @@ function sameValidations(a, b) {
891
958
  const norm = (v2) => JSON.stringify([...v2].sort((x, y) => x.name.localeCompare(y.name)));
892
959
  return norm(a) === norm(b);
893
960
  }
961
+ var CAP_KEYS = ["publishable", "translatable", "renderable", "onlineStore"];
962
+ function accessChanged(local, remote) {
963
+ for (const key of ["admin", "storefront", "customerAccount"]) {
964
+ const lv = local[key];
965
+ if (lv != null && remote?.[key] !== lv) return true;
966
+ }
967
+ return false;
968
+ }
969
+ function capabilitiesChanged(local, remote) {
970
+ for (const key of CAP_KEYS) {
971
+ const lc = local[key];
972
+ if (!lc) continue;
973
+ const rc = remote?.[key];
974
+ if ((rc?.enabled ?? false) !== lc.enabled) return true;
975
+ const ldata = lc.data;
976
+ if (ldata) {
977
+ const rdata = rc?.data;
978
+ for (const [k, v2] of Object.entries(ldata)) {
979
+ if (v2 != null && rdata?.[k] !== v2) return true;
980
+ }
981
+ }
982
+ }
983
+ return false;
984
+ }
985
+ function disablesOnlineStore(local, remote) {
986
+ return local.onlineStore?.enabled === false && remote?.onlineStore?.enabled === true;
987
+ }
988
+ function definitionChanges(local, remote) {
989
+ const changes = [];
990
+ if (local.name != null && local.name !== remote.name) changes.push("name");
991
+ if (local.description != null && local.description !== remote.description) changes.push("description");
992
+ if (local.displayNameKey != null && local.displayNameKey !== remote.displayNameKey) changes.push("displayNameKey");
993
+ if (local.access && accessChanged(local.access, remote.access)) changes.push("access");
994
+ if (local.capabilities && capabilitiesChanged(local.capabilities, remote.capabilities)) changes.push("capabilities");
995
+ return changes;
996
+ }
894
997
  function diff(local, remote) {
895
998
  const ops = [];
896
999
  const remoteByType = new Map(remote.map((d) => [d.type, d]));
@@ -914,6 +1017,7 @@ function diff(local, remote) {
914
1017
  }
915
1018
  const changes = {};
916
1019
  if (rf.required !== lf.required) changes.required = lf.required;
1020
+ if (rf.filterable !== lf.filterable) changes.filterable = lf.filterable;
917
1021
  if (!sameValidations(rf.validations, lf.validations)) changes.validations = lf.validations;
918
1022
  if (Object.keys(changes).length) {
919
1023
  ops.push({ kind: "updateField", type: localDef.type, key: lf.key, changes });
@@ -924,35 +1028,158 @@ function diff(local, remote) {
924
1028
  ops.push({ kind: "removeField", type: localDef.type, key: rf.key, destructive: true });
925
1029
  }
926
1030
  }
1031
+ const metaChanges = definitionChanges(localDef, remoteDef);
1032
+ if (metaChanges.length) {
1033
+ const destructive = localDef.capabilities ? disablesOnlineStore(localDef.capabilities, remoteDef.capabilities) : false;
1034
+ ops.push({
1035
+ kind: "updateDefinition",
1036
+ type: localDef.type,
1037
+ changes: metaChanges,
1038
+ ...destructive ? { destructive: true } : {}
1039
+ });
1040
+ }
927
1041
  }
928
1042
  return ops;
929
1043
  }
930
1044
 
931
1045
  // src/sync/normalize.ts
932
- function normalizeLocal(schema) {
933
- const def = schema.toDefinitionInput();
934
- return {
1046
+ function localCapabilities(caps) {
1047
+ const out = {};
1048
+ if (caps?.publishable) out.publishable = { enabled: caps.publishable.enabled };
1049
+ if (caps?.translatable) out.translatable = { enabled: caps.translatable.enabled };
1050
+ if (caps?.renderable) {
1051
+ out.renderable = { enabled: caps.renderable.enabled };
1052
+ if (caps.renderable.data) out.renderable.data = { ...caps.renderable.data };
1053
+ }
1054
+ out.onlineStore = caps?.onlineStore ? { enabled: caps.onlineStore.enabled, data: { urlHandle: caps.onlineStore.data?.urlHandle } } : { enabled: false };
1055
+ return out;
1056
+ }
1057
+ function normalizeDefinition(def) {
1058
+ const out = {
935
1059
  type: def.type,
936
1060
  name: def.name,
1061
+ capabilities: localCapabilities(def.capabilities),
937
1062
  fields: def.fieldDefinitions.map((f) => ({
938
1063
  key: f.key,
939
1064
  type: f.type,
940
1065
  required: f.required,
1066
+ filterable: f.capabilities?.adminFilterable?.enabled ?? false,
941
1067
  validations: f.validations
942
1068
  }))
943
1069
  };
1070
+ if (def.description != null) out.description = def.description;
1071
+ if (def.displayNameKey != null) out.displayNameKey = def.displayNameKey;
1072
+ if (def.access) out.access = { ...def.access };
1073
+ return out;
1074
+ }
1075
+ function normalizeLocal(schema) {
1076
+ return normalizeDefinition(schema.toDefinitionInput());
1077
+ }
1078
+ function remoteAccess(access) {
1079
+ if (!access) return void 0;
1080
+ const out = {};
1081
+ if (access.admin != null) out.admin = access.admin;
1082
+ if (access.storefront != null) out.storefront = access.storefront;
1083
+ if (access.customerAccount != null) out.customerAccount = access.customerAccount;
1084
+ return out;
1085
+ }
1086
+ function remoteCapabilities(caps) {
1087
+ const out = {};
1088
+ if (!caps) return out;
1089
+ if (caps.publishable) out.publishable = { enabled: !!caps.publishable.enabled };
1090
+ if (caps.translatable) out.translatable = { enabled: !!caps.translatable.enabled };
1091
+ if (caps.renderable) {
1092
+ out.renderable = { enabled: !!caps.renderable.enabled };
1093
+ const d = caps.renderable.data;
1094
+ if (d) {
1095
+ const data = {};
1096
+ if (d.metaTitleKey != null) data.metaTitleKey = d.metaTitleKey;
1097
+ if (d.metaDescriptionKey != null) data.metaDescriptionKey = d.metaDescriptionKey;
1098
+ if (Object.keys(data).length) out.renderable.data = data;
1099
+ }
1100
+ }
1101
+ if (caps.onlineStore) {
1102
+ out.onlineStore = { enabled: !!caps.onlineStore.enabled };
1103
+ if (caps.onlineStore.data?.urlHandle != null) out.onlineStore.data = { urlHandle: caps.onlineStore.data.urlHandle };
1104
+ }
1105
+ return out;
944
1106
  }
945
1107
  function normalizeRemote(def) {
946
- return {
1108
+ const out = {
947
1109
  type: def.type,
948
1110
  name: def.name,
1111
+ capabilities: remoteCapabilities(def.capabilities),
949
1112
  fields: def.fieldDefinitions.map((f) => ({
950
1113
  key: f.key,
951
1114
  type: typeof f.type === "string" ? f.type : f.type.name,
952
1115
  required: f.required,
1116
+ filterable: f.capabilities?.adminFilterable?.enabled ?? false,
953
1117
  validations: f.validations ?? []
954
1118
  }))
955
1119
  };
1120
+ if (def.description != null) out.description = def.description;
1121
+ if (def.displayNameKey != null) out.displayNameKey = def.displayNameKey;
1122
+ const access = remoteAccess(def.access);
1123
+ if (access) out.access = access;
1124
+ return out;
1125
+ }
1126
+
1127
+ // src/sync/resolve.ts
1128
+ var APP_PREFIX2 = "$app:";
1129
+ function effectiveScope(schema, config) {
1130
+ return schema.config.scope ?? config.scope ?? "app";
1131
+ }
1132
+ function effectiveType(handle, scope) {
1133
+ return scope === "merchant" ? handle : `${APP_PREFIX2}${handle}`;
1134
+ }
1135
+ function rewriteReference(v2, effByCanonical) {
1136
+ if (v2.name === "metaobject_definition_type") {
1137
+ const mapped = effByCanonical.get(v2.value);
1138
+ return mapped ? { ...v2, value: mapped } : v2;
1139
+ }
1140
+ if (v2.name === "metaobject_definition_types") {
1141
+ try {
1142
+ const parsed = JSON.parse(v2.value);
1143
+ if (Array.isArray(parsed)) {
1144
+ const rewritten = parsed.map((t) => typeof t === "string" ? effByCanonical.get(t) ?? t : t);
1145
+ return { ...v2, value: JSON.stringify(rewritten) };
1146
+ }
1147
+ } catch {
1148
+ }
1149
+ }
1150
+ return v2;
1151
+ }
1152
+ function resolveAdmin(out, handle, scope, config) {
1153
+ const explicitAdmin = out.access?.admin;
1154
+ if (scope === "merchant") {
1155
+ if (explicitAdmin != null) {
1156
+ throw new Error(
1157
+ `"${handle}" is merchant-scoped but sets access.admin; admin access is only valid on app-scoped metaobjects.`
1158
+ );
1159
+ }
1160
+ return;
1161
+ }
1162
+ if (explicitAdmin == null) {
1163
+ const admin = config.merchantEditable ? "MERCHANT_READ_WRITE" : "MERCHANT_READ";
1164
+ out.access = { ...out.access, admin };
1165
+ }
1166
+ }
1167
+ function resolveDefinitions(schemas, config = {}) {
1168
+ const effByCanonical = /* @__PURE__ */ new Map();
1169
+ for (const s of schemas) {
1170
+ effByCanonical.set(`${APP_PREFIX2}${s.handle}`, effectiveType(s.handle, effectiveScope(s, config)));
1171
+ }
1172
+ return schemas.map((s) => {
1173
+ const scope = effectiveScope(s, config);
1174
+ const base = s.toDefinitionInput();
1175
+ const fieldDefinitions = base.fieldDefinitions.map((f) => ({
1176
+ ...f,
1177
+ validations: f.validations.map((v2) => rewriteReference(v2, effByCanonical))
1178
+ }));
1179
+ const out = { ...base, type: effectiveType(s.handle, scope), fieldDefinitions };
1180
+ resolveAdmin(out, s.handle, scope, config);
1181
+ return out;
1182
+ });
956
1183
  }
957
1184
 
958
1185
  // src/sync/client.ts
@@ -970,12 +1197,14 @@ var PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {
970
1197
  required
971
1198
  type { name }
972
1199
  validations { name value }
1200
+ capabilities { adminFilterable { enabled } }
973
1201
  }
974
- access { admin storefront }
1202
+ access { admin storefront customerAccount }
975
1203
  capabilities {
976
1204
  publishable { enabled }
977
1205
  translatable { enabled }
978
- renderable { enabled }
1206
+ renderable { enabled data { metaTitleKey metaDescriptionKey } }
1207
+ onlineStore { enabled data { urlHandle } }
979
1208
  }
980
1209
  }
981
1210
  }`;
@@ -985,6 +1214,8 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
985
1214
  id
986
1215
  name
987
1216
  type
1217
+ description
1218
+ displayNameKey
988
1219
  fieldDefinitions {
989
1220
  key
990
1221
  name
@@ -992,6 +1223,14 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
992
1223
  required
993
1224
  type { name }
994
1225
  validations { name value }
1226
+ capabilities { adminFilterable { enabled } }
1227
+ }
1228
+ access { admin storefront customerAccount }
1229
+ capabilities {
1230
+ publishable { enabled }
1231
+ translatable { enabled }
1232
+ renderable { enabled data { metaTitleKey metaDescriptionKey } }
1233
+ onlineStore { enabled data { urlHandle } }
995
1234
  }
996
1235
  }
997
1236
  pageInfo { hasNextPage endCursor }
@@ -1026,17 +1265,24 @@ async function execute(client, query, variables) {
1026
1265
  }
1027
1266
 
1028
1267
  // src/sync/pull.ts
1268
+ function toDefinition(type, node) {
1269
+ return {
1270
+ type,
1271
+ name: node.name,
1272
+ description: node.description,
1273
+ displayNameKey: node.displayNameKey,
1274
+ access: node.access,
1275
+ capabilities: node.capabilities,
1276
+ fieldDefinitions: node.fieldDefinitions
1277
+ };
1278
+ }
1029
1279
  async function pull(client, types) {
1030
1280
  const out = [];
1031
1281
  for (const type of types) {
1032
1282
  const data = await execute(client, PULL_DEFINITION_QUERY, { type });
1033
1283
  const node = data.metaobjectDefinitionByType;
1034
1284
  if (!node) continue;
1035
- out.push({
1036
- id: node.id,
1037
- type,
1038
- definition: { type, name: node.name, fieldDefinitions: node.fieldDefinitions }
1039
- });
1285
+ out.push({ id: node.id, type, definition: toDefinition(type, node) });
1040
1286
  }
1041
1287
  return out;
1042
1288
  }
@@ -1054,7 +1300,7 @@ async function pullAll(client, opts = {}) {
1054
1300
  const canonical = toCanonicalType(node.type);
1055
1301
  if (appOwnedOnly && !canonical) continue;
1056
1302
  const type = canonical ?? node.type;
1057
- out.push({ id: node.id, type, definition: { type, name: node.name, fieldDefinitions: node.fieldDefinitions } });
1303
+ out.push({ id: node.id, type, definition: toDefinition(type, node) });
1058
1304
  }
1059
1305
  after = data.metaobjectDefinitions.pageInfo.hasNextPage ? data.metaobjectDefinitions.pageInfo.endCursor : null;
1060
1306
  } while (after !== null);
@@ -1062,6 +1308,25 @@ async function pullAll(client, opts = {}) {
1062
1308
  }
1063
1309
 
1064
1310
  // src/sync/push.ts
1311
+ function definitionUpdateFor(def, changes) {
1312
+ const out = {};
1313
+ for (const c of changes) {
1314
+ if (c === "name") out.name = def.name;
1315
+ else if (c === "description") out.description = def.description;
1316
+ else if (c === "displayNameKey") out.displayNameKey = def.displayNameKey;
1317
+ else if (c === "access") out.access = def.access;
1318
+ else if (c === "capabilities") {
1319
+ const caps = def.capabilities;
1320
+ const payload = {};
1321
+ if (caps?.publishable) payload.publishable = caps.publishable;
1322
+ if (caps?.translatable) payload.translatable = caps.translatable;
1323
+ if (caps?.renderable) payload.renderable = caps.renderable;
1324
+ payload.onlineStore = caps?.onlineStore ?? { enabled: false };
1325
+ out.capabilities = payload;
1326
+ }
1327
+ }
1328
+ return out;
1329
+ }
1065
1330
  function fieldInputFor(defByType, type, key) {
1066
1331
  return defByType.get(type)?.fieldDefinitions.find((f) => f.key === key);
1067
1332
  }
@@ -1162,14 +1427,22 @@ async function push(client, plan, sources, options) {
1162
1427
  if (id2) idByType.set(op.type, id2);
1163
1428
  return { op, status: "applied", id: id2 };
1164
1429
  }
1165
- const destructive = op.kind === "removeField" || op.kind === "changeFieldType";
1430
+ const destructive = "destructive" in op && op.destructive === true;
1166
1431
  if (destructive && !allowDestructive) return { op, status: "skipped", reason: "destructive" };
1167
1432
  if (failedTypes.has(op.type)) return { op, status: "blocked", reason: `blocked: definition "${op.type}" was not created` };
1168
1433
  const id = idByType.get(op.type);
1169
1434
  if (id == null) return { op, status: "blocked", reason: `no definition id for "${op.type}"` };
1170
- const fieldDefinitions = fieldOpsFor(op);
1171
- if (!fieldDefinitions) return { op, status: "blocked", reason: `no field input for "${op.type}"` };
1172
- const data = await execute(client, UPDATE_DEFINITION_MUTATION, { id, definition: { fieldDefinitions } });
1435
+ let definition;
1436
+ if (op.kind === "updateDefinition") {
1437
+ const def = defByType.get(op.type);
1438
+ if (!def) return { op, status: "blocked", reason: `no definition input for "${op.type}"` };
1439
+ definition = definitionUpdateFor(def, op.changes);
1440
+ } else {
1441
+ const fieldDefinitions = fieldOpsFor(op);
1442
+ if (!fieldDefinitions) return { op, status: "blocked", reason: `no field input for "${op.type}"` };
1443
+ definition = { fieldDefinitions };
1444
+ }
1445
+ const data = await execute(client, UPDATE_DEFINITION_MUTATION, { id, definition });
1173
1446
  const payload = data.metaobjectDefinitionUpdate;
1174
1447
  if (payload.userErrors.length) return { op, status: "failed", userErrors: payload.userErrors };
1175
1448
  return { op, status: "applied", id: payload.metaobjectDefinition?.id ?? id };
@@ -1190,6 +1463,9 @@ async function push(client, plan, sources, options) {
1190
1463
  validations: field.validations
1191
1464
  };
1192
1465
  if (field.description != null) update.description = field.description;
1466
+ if ("filterable" in op.changes) {
1467
+ update.capabilities = { adminFilterable: { enabled: op.changes.filterable ?? false } };
1468
+ }
1193
1469
  return [{ update }];
1194
1470
  }
1195
1471
  case "removeField":
@@ -1218,11 +1494,13 @@ async function push(client, plan, sources, options) {
1218
1494
  diff,
1219
1495
  generateSchemaSource,
1220
1496
  m,
1497
+ normalizeDefinition,
1221
1498
  normalizeLocal,
1222
1499
  normalizeRemote,
1223
1500
  pull,
1224
1501
  pullAll,
1225
1502
  push,
1503
+ resolveDefinitions,
1226
1504
  toDefinitionInput,
1227
1505
  validateConfig
1228
1506
  });