@fmaplabs/meta-manifest 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,6 +57,48 @@ export const Book = defineMetaobject("book", {
57
57
  });
58
58
  ```
59
59
 
60
+ ### Configuration options
61
+
62
+ Beyond fields, a metaobject definition accepts these options — all optional, all
63
+ reconciled by `diff`/`push` against a live store:
64
+
65
+ ```ts
66
+ export const Author = defineMetaobject("author", {
67
+ name: "Author",
68
+ displayName: "name", // optional; omit → Shopify auto-generates
69
+ scope: "merchant", // optional; overrides config.scope for this metaobject
70
+ access: {
71
+ admin: "merchant_read_write", // "merchant_read" | "merchant_read_write" (app scope only)
72
+ storefront: "public_read", // "none" | "public_read"
73
+ customerAccount: "read", // "none" | "read"
74
+ },
75
+ capabilities: {
76
+ publishable: true, // active/draft status
77
+ translatable: true, // translations
78
+ renderable: { metaTitleKey: "name", metaDescriptionKey: "bio" }, // SEO metadata; also accepts `true`
79
+ onlineStore: { urlHandle: "authors", createRedirects: true }, // publish entries as web pages
80
+ },
81
+ fields: {
82
+ name: m.text({ required: true, filterable: true }), // filterable → "use as filter" in the admin
83
+ bio: m.multilineText(),
84
+ },
85
+ });
86
+ ```
87
+
88
+ | Option | Maps to | Notes |
89
+ | --- | --- | --- |
90
+ | `scope` (config or per-metaobject) | app (`$app:<handle>`) vs merchant (`<handle>`) `type` | default `"app"`; resolved at sync time — `Author.type` stays `"$app:author"` |
91
+ | `merchantEditable` (config) | `access.admin` default | `false` → `merchant_read`, `true` → `merchant_read_write` (app scope only) |
92
+ | `access.admin` | `access.admin` | per-metaobject override; invalid on merchant scope |
93
+ | `access.storefront` | `access.storefront` | Storefront API access |
94
+ | `access.customerAccount` | `access.customerAccount` | Customer Account API access (`none` \| `read`) |
95
+ | `capabilities.publishable` | `capabilities.publishable` | active/draft status |
96
+ | `capabilities.translatable` | `capabilities.translatable` | translations |
97
+ | `capabilities.renderable` | `capabilities.renderable` | `true` or `{ metaTitleKey?, metaDescriptionKey? }` (SEO) |
98
+ | `capabilities.onlineStore` | `capabilities.onlineStore` | publish as web pages; GraphQL-only (not `shopify.app.toml`) |
99
+ | `displayName` | `displayNameKey` | omit to let Shopify auto-generate |
100
+ | field `filterable` | field `capabilities.adminFilterable` | expose the field as an admin filter |
101
+
60
102
  For the full `pull` → `diff` → `push` sync model (how local schema and a live store are
61
103
  reconciled, destructive-change gating, dependency ordering, error handling), see
62
104
  [`docs/SYNC.md`](./docs/SYNC.md).
@@ -79,6 +121,8 @@ export default defineConfig({
79
121
  accessToken: process.env.SHOPIFY_ADMIN_TOKEN!,
80
122
  apiVersion: "2026-07", // optional; defaults to DEFAULT_API_VERSION
81
123
  schema: "./src/schema.ts", // where `pull` writes, `diff`/`push` read
124
+ scope: "app", // optional; "app" (default) | "merchant" — applies to all metaobjects
125
+ merchantEditable: false, // optional; default admin access for app-scoped metaobjects
82
126
  });
83
127
  ```
84
128
 
@@ -120,4 +164,4 @@ config/transport error, and `0` otherwise — including when destructive ops wer
120
164
 
121
165
  v1 covers metaobject **definitions** (schema sync) only. A runtime client for reading/writing
122
166
  metaobject **entries** — the tento-style query API — is not implemented yet and is tracked as a
123
- follow-up, along with codegen of `access`/`capabilities` config.
167
+ follow-up.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DEFAULT_API_VERSION,
3
3
  SyncTransportError
4
- } from "./chunk-3R6VQ3Z3.js";
4
+ } from "./chunk-VSJUGUH7.js";
5
5
 
6
6
  // src/node/client.ts
7
7
  function createAdminClient(opts) {
@@ -28,4 +28,4 @@ function createAdminClient(opts) {
28
28
  export {
29
29
  createAdminClient
30
30
  };
31
- //# sourceMappingURL=chunk-PFU5VAO7.js.map
31
+ //# sourceMappingURL=chunk-AUR2YQCW.js.map
@@ -28,12 +28,14 @@ var PULL_DEFINITION_QUERY = `query PullMetaobjectDefinition($type: String!) {
28
28
  required
29
29
  type { name }
30
30
  validations { name value }
31
+ capabilities { adminFilterable { enabled } }
31
32
  }
32
- access { admin storefront }
33
+ access { admin storefront customerAccount }
33
34
  capabilities {
34
35
  publishable { enabled }
35
36
  translatable { enabled }
36
- renderable { enabled }
37
+ renderable { enabled data { metaTitleKey metaDescriptionKey } }
38
+ onlineStore { enabled data { urlHandle } }
37
39
  }
38
40
  }
39
41
  }`;
@@ -43,6 +45,8 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
43
45
  id
44
46
  name
45
47
  type
48
+ description
49
+ displayNameKey
46
50
  fieldDefinitions {
47
51
  key
48
52
  name
@@ -50,6 +54,14 @@ var LIST_DEFINITIONS_QUERY = `query ListMetaobjectDefinitions($after: String) {
50
54
  required
51
55
  type { name }
52
56
  validations { name value }
57
+ capabilities { adminFilterable { enabled } }
58
+ }
59
+ access { admin storefront customerAccount }
60
+ capabilities {
61
+ publishable { enabled }
62
+ translatable { enabled }
63
+ renderable { enabled data { metaTitleKey metaDescriptionKey } }
64
+ onlineStore { enabled data { urlHandle } }
53
65
  }
54
66
  }
55
67
  pageInfo { hasNextPage endCursor }
@@ -94,4 +106,4 @@ export {
94
106
  SyncTransportError,
95
107
  execute
96
108
  };
97
- //# sourceMappingURL=chunk-3R6VQ3Z3.js.map
109
+ //# sourceMappingURL=chunk-VSJUGUH7.js.map
@@ -0,0 +1 @@
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 /** 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","/**\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"],"mappings":";AAAO,IAAM,sBAAsB;AAkB5B,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;;;ACjBO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0B9B,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6B/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":[]}