@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/dist/index.d.ts CHANGED
@@ -11,6 +11,10 @@ interface Config {
11
11
  apiVersion?: string;
12
12
  /** Path to the schema module whose `schemas` export drives diff/push, and pull writes. */
13
13
  schema: string;
14
+ /** Scope for all metaobjects, unless overridden per-metaobject. Defaults to "app". */
15
+ scope?: "app" | "merchant";
16
+ /** Default admin access for app-scoped metaobjects: false → merchant_read, true → merchant_read_write. Defaults to false. */
17
+ merchantEditable?: boolean;
14
18
  }
15
19
  /** Identity helper for type inference in `meta-manifest.config.ts`. */
16
20
  declare function defineConfig(config: Config): Config;
@@ -65,6 +69,14 @@ type DecodeResult<T> = {
65
69
  value?: undefined;
66
70
  issues: Issue[];
67
71
  };
72
+ /** Options every field builder accepts, regardless of type. */
73
+ interface CommonFieldOptions {
74
+ name?: string;
75
+ description?: string;
76
+ required?: boolean;
77
+ /** Expose this field as a filter in the admin (→ `capabilities.adminFilterable`). */
78
+ filterable?: boolean;
79
+ }
68
80
  declare abstract class Field<TOut, TIn = TOut, Req extends boolean = false> {
69
81
  abstract readonly shopifyType: string;
70
82
  /** True when Shopify stores this type as a JSON string (objects, lists). */
@@ -72,6 +84,9 @@ declare abstract class Field<TOut, TIn = TOut, Req extends boolean = false> {
72
84
  required: boolean;
73
85
  name?: string;
74
86
  description?: string;
87
+ filterable: boolean;
88
+ /** Apply the shared field options; call from each concrete constructor. */
89
+ protected applyCommon(opts: CommonFieldOptions): void;
75
90
  readonly _out: TOut;
76
91
  readonly _in: TIn;
77
92
  readonly _req: Req;
@@ -96,9 +111,7 @@ declare abstract class Field<TOut, TIn = TOut, Req extends boolean = false> {
96
111
  get ["~standard"](): StandardSchemaV1.Props<TIn, TOut>;
97
112
  }
98
113
 
99
- interface BooleanOptions<R extends boolean = false> {
100
- name?: string;
101
- description?: string;
114
+ interface BooleanOptions<R extends boolean = false> extends CommonFieldOptions {
102
115
  required?: R;
103
116
  }
104
117
  declare class BooleanField<R extends boolean> extends Field<boolean, boolean, R> {
@@ -112,9 +125,7 @@ declare function boolean<R extends boolean = false>(opts?: BooleanOptions<R>): B
112
125
 
113
126
  type InnerOut<E> = E extends Field<infer O, any, any> ? O : never;
114
127
  type InnerIn<E> = E extends Field<any, infer I, any> ? I : never;
115
- interface ListOptions<R extends boolean = false> {
116
- name?: string;
117
- description?: string;
128
+ interface ListOptions<R extends boolean = false> extends CommonFieldOptions {
118
129
  required?: R;
119
130
  min?: number;
120
131
  max?: number;
@@ -135,9 +146,7 @@ interface Measure {
135
146
  value: number;
136
147
  unit: string;
137
148
  }
138
- interface MeasureOptions<R extends boolean = false> {
139
- name?: string;
140
- description?: string;
149
+ interface MeasureOptions<R extends boolean = false> extends CommonFieldOptions {
141
150
  required?: R;
142
151
  }
143
152
  declare class MeasurementField<R extends boolean> extends Field<Measure, Measure, R> {
@@ -156,9 +165,7 @@ interface Money {
156
165
  amount: string;
157
166
  currencyCode: string;
158
167
  }
159
- interface MoneyOptions<R extends boolean = false> {
160
- name?: string;
161
- description?: string;
168
+ interface MoneyOptions<R extends boolean = false> extends CommonFieldOptions {
162
169
  required?: R;
163
170
  }
164
171
  declare class MoneyField<R extends boolean> extends Field<Money, Money, R> {
@@ -171,9 +178,7 @@ declare class MoneyField<R extends boolean> extends Field<Money, Money, R> {
171
178
  }
172
179
  declare function money<R extends boolean = false>(opts?: MoneyOptions<R>): MoneyField<R>;
173
180
 
174
- interface NumberOptions<R extends boolean = false> {
175
- name?: string;
176
- description?: string;
181
+ interface NumberOptions<R extends boolean = false> extends CommonFieldOptions {
177
182
  required?: R;
178
183
  min?: number;
179
184
  max?: number;
@@ -212,9 +217,7 @@ interface Rating {
212
217
  interface RatingInput {
213
218
  value: number;
214
219
  }
215
- interface RatingOptions<R extends boolean = false> {
216
- name?: string;
217
- description?: string;
220
+ interface RatingOptions<R extends boolean = false> extends CommonFieldOptions {
218
221
  required?: R;
219
222
  min: number;
220
223
  max: number;
@@ -232,9 +235,7 @@ declare class RatingField<R extends boolean> extends Field<Rating, RatingInput,
232
235
  }
233
236
  declare function rating<R extends boolean = false>(opts: RatingOptions<R>): RatingField<R>;
234
237
 
235
- interface RefOptions<R extends boolean = false> {
236
- name?: string;
237
- description?: string;
238
+ interface RefOptions<R extends boolean = false> extends CommonFieldOptions {
238
239
  required?: R;
239
240
  }
240
241
  declare abstract class GidField<R extends boolean> extends Field<string, string, R> {
@@ -275,9 +276,7 @@ declare function page<R extends boolean = false>(opts?: RefOptions<R>): SimpleRe
275
276
  declare function file<R extends boolean = false>(opts?: FileOptions<R>): FileField<R>;
276
277
  declare function ref<R extends boolean = false>(target: TypeRef, opts?: RefOptions<R>): MetaobjectRefField<R>;
277
278
 
278
- interface BaseOptions<R extends boolean = false> {
279
- name?: string;
280
- description?: string;
279
+ interface BaseOptions<R extends boolean = false> extends CommonFieldOptions {
281
280
  required?: R;
282
281
  }
283
282
  declare abstract class StringScalarField<R extends boolean> extends Field<string, string, R> {
@@ -323,9 +322,7 @@ declare function url<R extends boolean = false>(opts?: UrlOptions<R>): UrlField<
323
322
  declare function color<R extends boolean = false>(opts?: BaseOptions<R>): ColorField<R>;
324
323
  declare function json<R extends boolean = false>(opts?: BaseOptions<R>): JsonField<R>;
325
324
 
326
- interface TextOptions<R extends boolean = false> {
327
- name?: string;
328
- description?: string;
325
+ interface TextOptions<R extends boolean = false> extends CommonFieldOptions {
329
326
  required?: R;
330
327
  min?: number;
331
328
  max?: number;
@@ -392,6 +389,11 @@ type InferInput<F extends FieldMap> = Simplify<{
392
389
  [K in OptionalKeys<F>]?: FieldIn<F[K]>;
393
390
  }>;
394
391
 
392
+ interface FieldCapabilitiesInput {
393
+ adminFilterable?: {
394
+ enabled: boolean;
395
+ };
396
+ }
395
397
  interface FieldDefinitionInput {
396
398
  key: string;
397
399
  name: string;
@@ -399,19 +401,44 @@ interface FieldDefinitionInput {
399
401
  required: boolean;
400
402
  type: string;
401
403
  validations: FieldValidation[];
404
+ capabilities?: FieldCapabilitiesInput;
405
+ }
406
+ interface AccessInput {
407
+ admin?: string;
408
+ storefront?: string;
409
+ customerAccount?: string;
410
+ }
411
+ interface RenderableData {
412
+ metaTitleKey?: string;
413
+ metaDescriptionKey?: string;
414
+ }
415
+ interface OnlineStoreData {
416
+ urlHandle: string;
417
+ createRedirects?: boolean;
418
+ }
419
+ interface CapabilitiesInput {
420
+ publishable?: {
421
+ enabled: boolean;
422
+ };
423
+ translatable?: {
424
+ enabled: boolean;
425
+ };
426
+ renderable?: {
427
+ enabled: boolean;
428
+ data?: RenderableData;
429
+ };
430
+ onlineStore?: {
431
+ enabled: boolean;
432
+ data?: OnlineStoreData;
433
+ };
402
434
  }
403
435
  interface MetaobjectDefinitionInput {
404
436
  type: string;
405
437
  name: string;
406
438
  description?: string;
407
439
  displayNameKey?: string;
408
- access?: {
409
- admin?: string;
410
- storefront?: string;
411
- };
412
- capabilities?: Record<string, {
413
- enabled: boolean;
414
- }>;
440
+ access?: AccessInput;
441
+ capabilities?: CapabilitiesInput;
415
442
  fieldDefinitions: FieldDefinitionInput[];
416
443
  }
417
444
  declare function toDefinitionInput<F extends FieldMap>(schema: MetaobjectSchema<F>): MetaobjectDefinitionInput;
@@ -419,16 +446,30 @@ declare function toDefinitionInput<F extends FieldMap>(schema: MetaobjectSchema<
419
446
  interface AccessConfig {
420
447
  admin?: "merchant_read" | "merchant_read_write";
421
448
  storefront?: "public_read" | "none";
449
+ customerAccount?: "none" | "read";
450
+ }
451
+ /** SEO metadata: `true` enables with auto keys; an object maps field keys as title/description. */
452
+ type RenderableConfig = boolean | {
453
+ metaTitleKey?: string;
454
+ metaDescriptionKey?: string;
455
+ };
456
+ /** Publish entries as Online Store web pages. `urlHandle` is required by Shopify. */
457
+ interface OnlineStoreConfig {
458
+ urlHandle: string;
459
+ createRedirects?: boolean;
422
460
  }
423
461
  interface CapabilitiesConfig {
424
462
  publishable?: boolean;
425
463
  translatable?: boolean;
426
- renderable?: boolean;
464
+ renderable?: RenderableConfig;
465
+ onlineStore?: OnlineStoreConfig;
427
466
  }
428
467
  interface MetaobjectConfig<F> {
429
468
  name: string;
430
469
  description?: string;
431
470
  displayName?: keyof F & string;
471
+ /** Override the global config scope for this metaobject. */
472
+ scope?: "app" | "merchant";
432
473
  access?: AccessConfig;
433
474
  capabilities?: CapabilitiesConfig;
434
475
  fields: F;
@@ -459,17 +500,54 @@ interface MetaobjectSchema<F extends FieldMap> {
459
500
  }
460
501
  declare function defineMetaobject<F extends Record<string, unknown>>(handle: string, config: MetaobjectConfig<F>): F extends FieldMap ? MetaobjectSchema<F> : never;
461
502
 
503
+ interface RemoteAccess {
504
+ admin?: string;
505
+ storefront?: string;
506
+ customerAccount?: string;
507
+ }
508
+ /** Normalized capability comparison shape. `enabled` is compared for every capability
509
+ * present locally; `data` keys are compared only when locally declared. [design §8] */
510
+ interface RemoteRenderable {
511
+ enabled: boolean;
512
+ data?: {
513
+ metaTitleKey?: string;
514
+ metaDescriptionKey?: string;
515
+ };
516
+ }
517
+ interface RemoteOnlineStore {
518
+ enabled: boolean;
519
+ data?: {
520
+ urlHandle?: string;
521
+ };
522
+ }
523
+ interface RemoteCapabilities {
524
+ publishable?: {
525
+ enabled: boolean;
526
+ };
527
+ translatable?: {
528
+ enabled: boolean;
529
+ };
530
+ renderable?: RemoteRenderable;
531
+ onlineStore?: RemoteOnlineStore;
532
+ }
462
533
  interface RemoteField {
463
534
  key: string;
464
535
  type: string;
465
536
  required: boolean;
537
+ filterable: boolean;
466
538
  validations: FieldValidation[];
467
539
  }
468
540
  interface RemoteDefinition {
469
541
  type: string;
470
542
  name?: string;
543
+ description?: string;
544
+ displayNameKey?: string;
545
+ access?: RemoteAccess;
546
+ capabilities?: RemoteCapabilities;
471
547
  fields: RemoteField[];
472
548
  }
549
+ /** Normalize a (already scope-resolved) definition input to the diff shape. */
550
+ declare function normalizeDefinition(def: MetaobjectDefinitionInput): RemoteDefinition;
473
551
  declare function normalizeLocal<F extends FieldMap>(schema: MetaobjectSchema<F>): RemoteDefinition;
474
552
  interface PulledFieldDefinition {
475
553
  key: string;
@@ -478,10 +556,45 @@ interface PulledFieldDefinition {
478
556
  } | string;
479
557
  required: boolean;
480
558
  validations?: FieldValidation[];
559
+ capabilities?: {
560
+ adminFilterable?: {
561
+ enabled: boolean;
562
+ } | null;
563
+ } | null;
564
+ }
565
+ interface PulledAccess {
566
+ admin?: string | null;
567
+ storefront?: string | null;
568
+ customerAccount?: string | null;
569
+ }
570
+ interface PulledCapabilities {
571
+ publishable?: {
572
+ enabled: boolean;
573
+ } | null;
574
+ translatable?: {
575
+ enabled: boolean;
576
+ } | null;
577
+ renderable?: {
578
+ enabled: boolean;
579
+ data?: {
580
+ metaTitleKey?: string | null;
581
+ metaDescriptionKey?: string | null;
582
+ } | null;
583
+ } | null;
584
+ onlineStore?: {
585
+ enabled: boolean;
586
+ data?: {
587
+ urlHandle?: string | null;
588
+ } | null;
589
+ } | null;
481
590
  }
482
591
  interface PulledDefinition {
483
592
  type: string;
484
593
  name?: string;
594
+ description?: string | null;
595
+ displayNameKey?: string | null;
596
+ access?: PulledAccess | null;
597
+ capabilities?: PulledCapabilities | null;
485
598
  fieldDefinitions: PulledFieldDefinition[];
486
599
  }
487
600
  declare function normalizeRemote(def: PulledDefinition): RemoteDefinition;
@@ -493,10 +606,17 @@ declare function normalizeRemote(def: PulledDefinition): RemoteDefinition;
493
606
  */
494
607
  declare function generateSchemaSource(defs: RemoteDefinition[]): string;
495
608
 
609
+ /** Definition-level metadata that `updateDefinition` reconciles. */
610
+ type DefinitionChange = "name" | "description" | "displayNameKey" | "access" | "capabilities";
496
611
  type DiffOp = {
497
612
  kind: "createDefinition";
498
613
  type: string;
499
614
  definition: RemoteDefinition;
615
+ } | {
616
+ kind: "updateDefinition";
617
+ type: string;
618
+ changes: DefinitionChange[];
619
+ destructive?: true;
500
620
  } | {
501
621
  kind: "addField";
502
622
  type: string;
@@ -521,6 +641,18 @@ type DiffOp = {
521
641
  };
522
642
  declare function diff(local: RemoteDefinition[], remote: RemoteDefinition[]): DiffOp[];
523
643
 
644
+ type AnySchema$1 = MetaobjectSchema<any>;
645
+ /** The subset of config that scope resolution reads. */
646
+ type ScopeConfig = Pick<Config, "scope" | "merchantEditable">;
647
+ type Scope = "app" | "merchant";
648
+ /**
649
+ * Resolve each schema's canonical (`$app:`) definition input into the effective
650
+ * input the sync pipeline pushes: definition `type` and reference targets are
651
+ * rewritten to each metaobject's effective scope, and `access.admin` is resolved.
652
+ * `schema.type` / `m.ref` public values are unchanged. [design §6]
653
+ */
654
+ declare function resolveDefinitions(schemas: AnySchema$1[], config?: ScopeConfig): MetaobjectDefinitionInput[];
655
+
524
656
  /**
525
657
  * A definition read back from the store. `type` is the canonical "$app:…" key
526
658
  * (the REQUESTED type, re-labeled from the resolved `app--…` form Shopify
@@ -599,4 +731,4 @@ declare function push(client: AdminGraphQLClient, plan: DiffOp[], sources: {
599
731
 
600
732
  type AnySchema = MetaobjectSchema<any>;
601
733
 
602
- export { type AccessConfig, AdminGraphQLClient, type AnySchema, type CapabilitiesConfig, type Config, DEFAULT_API_VERSION, type DecodeResult, type DiffOp, Field, type FieldDefinitionInput, type FieldValidation, type FileType, type Infer, type InferInput, type Issue, type Measure, type MetaobjectConfig, type MetaobjectDefinitionInput, type MetaobjectSchema, type Money, type ParseInput, type PulledRemote, type PushOpResult, type PushOptions, type PushResult, type Rating, type RatingInput, type RemoteDefinition, type RemoteField, StandardSchemaV1, type TypeRef, defineConfig, defineMetaobject, diff, generateSchemaSource, m, normalizeLocal, normalizeRemote, pull, pullAll, push, toDefinitionInput, validateConfig };
734
+ export { type AccessConfig, AdminGraphQLClient, type AnySchema, type CapabilitiesConfig, type Config, DEFAULT_API_VERSION, type DecodeResult, type DefinitionChange, type DiffOp, Field, type FieldDefinitionInput, type FieldValidation, type FileType, type Infer, type InferInput, type Issue, type Measure, type MetaobjectConfig, type MetaobjectDefinitionInput, type MetaobjectSchema, type Money, type ParseInput, type PulledDefinition, type PulledRemote, type PushOpResult, type PushOptions, type PushResult, type Rating, type RatingInput, type RemoteAccess, type RemoteCapabilities, type RemoteDefinition, type RemoteField, type Scope, type ScopeConfig, StandardSchemaV1, type TypeRef, defineConfig, defineMetaobject, diff, generateSchemaSource, m, normalizeDefinition, normalizeLocal, normalizeRemote, pull, pullAll, push, resolveDefinitions, toDefinitionInput, validateConfig };
package/dist/index.js CHANGED
@@ -4,19 +4,21 @@ import {
4
4
  diff,
5
5
  generateSchemaSource,
6
6
  m,
7
+ normalizeDefinition,
7
8
  normalizeLocal,
8
9
  normalizeRemote,
9
10
  pull,
10
11
  pullAll,
11
12
  push,
13
+ resolveDefinitions,
12
14
  toDefinitionInput
13
- } from "./chunk-GH5DXHS5.js";
15
+ } from "./chunk-ZTWFDJHN.js";
14
16
  import {
15
17
  DEFAULT_API_VERSION,
16
18
  SyncTransportError,
17
19
  defineConfig,
18
20
  validateConfig
19
- } from "./chunk-3R6VQ3Z3.js";
21
+ } from "./chunk-VSJUGUH7.js";
20
22
  export {
21
23
  DEFAULT_API_VERSION,
22
24
  Field,
@@ -26,11 +28,13 @@ export {
26
28
  diff,
27
29
  generateSchemaSource,
28
30
  m,
31
+ normalizeDefinition,
29
32
  normalizeLocal,
30
33
  normalizeRemote,
31
34
  pull,
32
35
  pullAll,
33
36
  push,
37
+ resolveDefinitions,
34
38
  toDefinitionInput,
35
39
  validateConfig
36
40
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/node/client.ts","../../src/sync/client.ts","../../src/config.ts"],"sourcesContent":["import type { AdminGraphQLClient } from \"../sync/client\";\nimport { SyncTransportError } from \"../sync/client\";\nimport { DEFAULT_API_VERSION } from \"../config\";\n\n/**\n * Build an AdminGraphQLClient that talks directly to a store using an Admin API\n * access token — the CLI's standalone equivalent of the app's session-based\n * `admin.graphql` wrapper. [design §2]\n */\nexport function createAdminClient(opts: {\n shop: string;\n accessToken: string;\n apiVersion?: string;\n}): AdminGraphQLClient {\n const version = opts.apiVersion ?? DEFAULT_API_VERSION;\n const endpoint = `https://${opts.shop}/admin/api/${version}/graphql.json`;\n return async (query, options) => {\n let res: Response;\n try {\n res = await fetch(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Shopify-Access-Token\": opts.accessToken },\n body: JSON.stringify({ query, variables: options?.variables }),\n });\n } catch (cause) {\n throw new SyncTransportError(`Request to ${opts.shop} failed`, cause);\n }\n if (!res.ok) {\n throw new SyncTransportError(`Admin API returned HTTP ${res.status}`, await res.text().catch(() => null));\n }\n return res.json() as Promise<{ data?: unknown; errors?: unknown }>;\n };\n}\n","/**\n * The minimal Admin GraphQL transport the sync adapter depends on. The app\n * supplies a concrete implementation at its edge (wrapping `admin.graphql`),\n * keeping `@fmaplabs/meta-manifest` free of any runtime dependency on a GraphQL\n * client. [design §5]\n */\nexport interface AdminGraphQLClient {\n (query: string, options?: { variables?: Record<string, unknown> }): Promise<{ data?: unknown; errors?: unknown }>;\n}\n\n// GraphQL operation strings, copied verbatim from the schema-validated documents\n// in the design spec §3. Re-validated against Admin API 2026-07 during planning.\n// Do not edit by hand — keep equal to the validated documents (drift guard, §12).\n\nexport const PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {\n metaobjectDefinitionByType(type: $type) {\n id\n name\n type\n description\n displayNameKey\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n }\n access { admin storefront }\n capabilities {\n publishable { enabled }\n translatable { enabled }\n renderable { enabled }\n }\n }\n}`;\n\nexport const LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {\n metaobjectDefinitions(first: 50, after: $after) {\n nodes {\n id\n name\n type\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}`;\n\nexport const CREATE_DEFINITION_MUTATION = `mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {\n metaobjectDefinitionCreate(definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\nexport const UPDATE_DEFINITION_MUTATION = `mutation UpdateMetaobjectDefinition($id: ID!, $definition: MetaobjectDefinitionUpdateInput!) {\n metaobjectDefinitionUpdate(id: $id, definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\n/**\n * Thrown when a request fails at the transport or top-level GraphQL layer —\n * distinct from per-op `userErrors`, which `push` reports as `failed` rather\n * than throwing. Carries the offending top-level `errors` payload. [design §5]\n */\nexport class SyncTransportError extends Error {\n constructor(message: string, readonly errors: unknown) {\n super(message);\n this.name = \"SyncTransportError\";\n }\n}\n\n/**\n * Runs an operation through the injected client and returns its `data`.\n * A non-empty top-level `errors` payload becomes a `SyncTransportError`;\n * a rejected transport promise propagates unchanged. [design §5]\n */\nexport async function execute<T>(\n client: AdminGraphQLClient,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> {\n const result = await client(query, variables ? { variables } : undefined);\n if (Array.isArray(result.errors) ? result.errors.length > 0 : result.errors != null) {\n throw new SyncTransportError(\"GraphQL request failed\", result.errors);\n }\n return result.data as T;\n}\n","export const DEFAULT_API_VERSION = \"2026-07\";\n\nexport interface Config {\n /** e.g. \"my-store.myshopify.com\" */\n shop: string;\n /** Admin API access token; reference via process.env in your config file. */\n accessToken: string;\n /** Admin API version. Defaults to DEFAULT_API_VERSION. */\n apiVersion?: string;\n /** Path to the schema module whose `schemas` export drives diff/push, and pull writes. */\n schema: string;\n}\n\n/** Identity helper for type inference in `meta-manifest.config.ts`. */\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n/** Validate a loaded config object, throwing a one-line Error naming the first missing field. */\nexport function validateConfig(raw: unknown): Config {\n const c = raw as Partial<Config> | null | undefined;\n for (const key of [\"shop\", \"accessToken\", \"schema\"] as const) {\n if (!c || typeof c[key] !== \"string\" || c[key] === \"\") {\n throw new Error(`Invalid config: missing or empty \"${key}\".`);\n }\n }\n return c as Config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4EO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAA0B,QAAiB;AACrD,UAAM,OAAO;AADuB;AAEpC,SAAK,OAAO;AAAA,EACd;AAAA,EAHsC;AAIxC;;;ACjFO,IAAM,sBAAsB;;;AFS5B,SAAS,kBAAkB,MAIX;AACrB,QAAM,UAAU,KAAK,cAAc;AACnC,QAAM,WAAW,WAAW,KAAK,IAAI,cAAc,OAAO;AAC1D,SAAO,OAAO,OAAO,YAAY;AAC/B,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,UAAU;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oBAAoB,0BAA0B,KAAK,YAAY;AAAA,QAC1F,MAAM,KAAK,UAAU,EAAE,OAAO,WAAW,SAAS,UAAU,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI,mBAAmB,cAAc,KAAK,IAAI,WAAW,KAAK;AAAA,IACtE;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,mBAAmB,2BAA2B,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IAC1G;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/node/client.ts","../../src/sync/client.ts","../../src/config.ts"],"sourcesContent":["import type { AdminGraphQLClient } from \"../sync/client\";\nimport { SyncTransportError } from \"../sync/client\";\nimport { DEFAULT_API_VERSION } from \"../config\";\n\n/**\n * Build an AdminGraphQLClient that talks directly to a store using an Admin API\n * access token — the CLI's standalone equivalent of the app's session-based\n * `admin.graphql` wrapper. [design §2]\n */\nexport function createAdminClient(opts: {\n shop: string;\n accessToken: string;\n apiVersion?: string;\n}): AdminGraphQLClient {\n const version = opts.apiVersion ?? DEFAULT_API_VERSION;\n const endpoint = `https://${opts.shop}/admin/api/${version}/graphql.json`;\n return async (query, options) => {\n let res: Response;\n try {\n res = await fetch(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"X-Shopify-Access-Token\": opts.accessToken },\n body: JSON.stringify({ query, variables: options?.variables }),\n });\n } catch (cause) {\n throw new SyncTransportError(`Request to ${opts.shop} failed`, cause);\n }\n if (!res.ok) {\n throw new SyncTransportError(`Admin API returned HTTP ${res.status}`, await res.text().catch(() => null));\n }\n return res.json() as Promise<{ data?: unknown; errors?: unknown }>;\n };\n}\n","/**\n * The minimal Admin GraphQL transport the sync adapter depends on. The app\n * supplies a concrete implementation at its edge (wrapping `admin.graphql`),\n * keeping `@fmaplabs/meta-manifest` free of any runtime dependency on a GraphQL\n * client. [design §5]\n */\nexport interface AdminGraphQLClient {\n (query: string, options?: { variables?: Record<string, unknown> }): Promise<{ data?: unknown; errors?: unknown }>;\n}\n\n// GraphQL operation strings, copied verbatim from the schema-validated documents\n// in the design spec §3. Re-validated against Admin API 2026-07 during planning.\n// Do not edit by hand — keep equal to the validated documents (drift guard, §12).\n\nexport const PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {\n metaobjectDefinitionByType(type: $type) {\n id\n name\n type\n description\n displayNameKey\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n capabilities { adminFilterable { enabled } }\n }\n access { admin storefront customerAccount }\n capabilities {\n publishable { enabled }\n translatable { enabled }\n renderable { enabled data { metaTitleKey metaDescriptionKey } }\n onlineStore { enabled data { urlHandle } }\n }\n }\n}`;\n\nexport const LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {\n metaobjectDefinitions(first: 50, after: $after) {\n nodes {\n id\n name\n type\n description\n displayNameKey\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n capabilities { adminFilterable { enabled } }\n }\n access { admin storefront customerAccount }\n capabilities {\n publishable { enabled }\n translatable { enabled }\n renderable { enabled data { metaTitleKey metaDescriptionKey } }\n onlineStore { enabled data { urlHandle } }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}`;\n\nexport const CREATE_DEFINITION_MUTATION = `mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {\n metaobjectDefinitionCreate(definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\nexport const UPDATE_DEFINITION_MUTATION = `mutation UpdateMetaobjectDefinition($id: ID!, $definition: MetaobjectDefinitionUpdateInput!) {\n metaobjectDefinitionUpdate(id: $id, definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\n/**\n * Thrown when a request fails at the transport or top-level GraphQL layer —\n * distinct from per-op `userErrors`, which `push` reports as `failed` rather\n * than throwing. Carries the offending top-level `errors` payload. [design §5]\n */\nexport class SyncTransportError extends Error {\n constructor(message: string, readonly errors: unknown) {\n super(message);\n this.name = \"SyncTransportError\";\n }\n}\n\n/**\n * Runs an operation through the injected client and returns its `data`.\n * A non-empty top-level `errors` payload becomes a `SyncTransportError`;\n * a rejected transport promise propagates unchanged. [design §5]\n */\nexport async function execute<T>(\n client: AdminGraphQLClient,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> {\n const result = await client(query, variables ? { variables } : undefined);\n if (Array.isArray(result.errors) ? result.errors.length > 0 : result.errors != null) {\n throw new SyncTransportError(\"GraphQL request failed\", result.errors);\n }\n return result.data as T;\n}\n","export const DEFAULT_API_VERSION = \"2026-07\";\n\nexport interface Config {\n /** e.g. \"my-store.myshopify.com\" */\n shop: string;\n /** Admin API access token; reference via process.env in your config file. */\n accessToken: string;\n /** Admin API version. Defaults to DEFAULT_API_VERSION. */\n apiVersion?: string;\n /** Path to the schema module whose `schemas` export drives diff/push, and pull writes. */\n schema: string;\n /** Scope for all metaobjects, unless overridden per-metaobject. Defaults to \"app\". */\n scope?: \"app\" | \"merchant\";\n /** Default admin access for app-scoped metaobjects: false → merchant_read, true → merchant_read_write. Defaults to false. */\n merchantEditable?: boolean;\n}\n\n/** Identity helper for type inference in `meta-manifest.config.ts`. */\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n/** Validate a loaded config object, throwing a one-line Error naming the first missing field. */\nexport function validateConfig(raw: unknown): Config {\n const c = raw as Partial<Config> | null | undefined;\n for (const key of [\"shop\", \"accessToken\", \"schema\"] as const) {\n if (!c || typeof c[key] !== \"string\" || c[key] === \"\") {\n throw new Error(`Invalid config: missing or empty \"${key}\".`);\n }\n }\n return c as Config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwFO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAA0B,QAAiB;AACrD,UAAM,OAAO;AADuB;AAEpC,SAAK,OAAO;AAAA,EACd;AAAA,EAHsC;AAIxC;;;AC7FO,IAAM,sBAAsB;;;AFS5B,SAAS,kBAAkB,MAIX;AACrB,QAAM,UAAU,KAAK,cAAc;AACnC,QAAM,WAAW,WAAW,KAAK,IAAI,cAAc,OAAO;AAC1D,SAAO,OAAO,OAAO,YAAY;AAC/B,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,UAAU;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oBAAoB,0BAA0B,KAAK,YAAY;AAAA,QAC1F,MAAM,KAAK,UAAU,EAAE,OAAO,WAAW,SAAS,UAAU,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI,mBAAmB,cAAc,KAAK,IAAI,WAAW,KAAK;AAAA,IACtE;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,mBAAmB,2BAA2B,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,IAC1G;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createAdminClient
3
- } from "../chunk-PFU5VAO7.js";
4
- import "../chunk-3R6VQ3Z3.js";
3
+ } from "../chunk-AUR2YQCW.js";
4
+ import "../chunk-VSJUGUH7.js";
5
5
  export {
6
6
  createAdminClient
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fmaplabs/meta-manifest",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Zero-dependency, zod-style builder for Shopify metaobject definitions, plus a CLI (mm) that syncs them to a store via pull → diff → push.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config.ts","../src/sync/client.ts"],"sourcesContent":["export const DEFAULT_API_VERSION = \"2026-07\";\n\nexport interface Config {\n /** e.g. \"my-store.myshopify.com\" */\n shop: string;\n /** Admin API access token; reference via process.env in your config file. */\n accessToken: string;\n /** Admin API version. Defaults to DEFAULT_API_VERSION. */\n apiVersion?: string;\n /** Path to the schema module whose `schemas` export drives diff/push, and pull writes. */\n schema: string;\n}\n\n/** Identity helper for type inference in `meta-manifest.config.ts`. */\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n/** Validate a loaded config object, throwing a one-line Error naming the first missing field. */\nexport function validateConfig(raw: unknown): Config {\n const c = raw as Partial<Config> | null | undefined;\n for (const key of [\"shop\", \"accessToken\", \"schema\"] as const) {\n if (!c || typeof c[key] !== \"string\" || c[key] === \"\") {\n throw new Error(`Invalid config: missing or empty \"${key}\".`);\n }\n }\n return c as Config;\n}\n","/**\n * The minimal Admin GraphQL transport the sync adapter depends on. The app\n * supplies a concrete implementation at its edge (wrapping `admin.graphql`),\n * keeping `@fmaplabs/meta-manifest` free of any runtime dependency on a GraphQL\n * client. [design §5]\n */\nexport interface AdminGraphQLClient {\n (query: string, options?: { variables?: Record<string, unknown> }): Promise<{ data?: unknown; errors?: unknown }>;\n}\n\n// GraphQL operation strings, copied verbatim from the schema-validated documents\n// in the design spec §3. Re-validated against Admin API 2026-07 during planning.\n// Do not edit by hand — keep equal to the validated documents (drift guard, §12).\n\nexport const PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {\n metaobjectDefinitionByType(type: $type) {\n id\n name\n type\n description\n displayNameKey\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n }\n access { admin storefront }\n capabilities {\n publishable { enabled }\n translatable { enabled }\n renderable { enabled }\n }\n }\n}`;\n\nexport const LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {\n metaobjectDefinitions(first: 50, after: $after) {\n nodes {\n id\n name\n type\n fieldDefinitions {\n key\n name\n description\n required\n type { name }\n validations { name value }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}`;\n\nexport const CREATE_DEFINITION_MUTATION = `mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {\n metaobjectDefinitionCreate(definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\nexport const UPDATE_DEFINITION_MUTATION = `mutation UpdateMetaobjectDefinition($id: ID!, $definition: MetaobjectDefinitionUpdateInput!) {\n metaobjectDefinitionUpdate(id: $id, definition: $definition) {\n metaobjectDefinition { id type }\n userErrors { field message code }\n }\n}`;\n\n/**\n * Thrown when a request fails at the transport or top-level GraphQL layer —\n * distinct from per-op `userErrors`, which `push` reports as `failed` rather\n * than throwing. Carries the offending top-level `errors` payload. [design §5]\n */\nexport class SyncTransportError extends Error {\n constructor(message: string, readonly errors: unknown) {\n super(message);\n this.name = \"SyncTransportError\";\n }\n}\n\n/**\n * Runs an operation through the injected client and returns its `data`.\n * A non-empty top-level `errors` payload becomes a `SyncTransportError`;\n * a rejected transport promise propagates unchanged. [design §5]\n */\nexport async function execute<T>(\n client: AdminGraphQLClient,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> {\n const result = await client(query, variables ? { variables } : undefined);\n if (Array.isArray(result.errors) ? result.errors.length > 0 : result.errors != null) {\n throw new SyncTransportError(\"GraphQL request failed\", result.errors);\n }\n return result.data as T;\n}\n"],"mappings":";AAAO,IAAM,sBAAsB;AAc5B,SAAS,aAAa,QAAwB;AACnD,SAAO;AACT;AAGO,SAAS,eAAe,KAAsB;AACnD,QAAM,IAAI;AACV,aAAW,OAAO,CAAC,QAAQ,eAAe,QAAQ,GAAY;AAC5D,QAAI,CAAC,KAAK,OAAO,EAAE,GAAG,MAAM,YAAY,EAAE,GAAG,MAAM,IAAI;AACrD,YAAM,IAAI,MAAM,qCAAqC,GAAG,IAAI;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;;;ACbO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwB9B,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmB/B,IAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAOnC,IAAM,6BAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAYnC,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAA0B,QAAiB;AACrD,UAAM,OAAO;AADuB;AAEpC,SAAK,OAAO;AAAA,EACd;AAAA,EAHsC;AAIxC;AAOA,eAAsB,QACpB,QACA,OACA,WACY;AACZ,QAAM,SAAS,MAAM,OAAO,OAAO,YAAY,EAAE,UAAU,IAAI,MAAS;AACxE,MAAI,MAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,OAAO,SAAS,IAAI,OAAO,UAAU,MAAM;AACnF,UAAM,IAAI,mBAAmB,0BAA0B,OAAO,MAAM;AAAA,EACtE;AACA,SAAO,OAAO;AAChB;","names":[]}