@fmaplabs/meta-manifest 0.2.0 → 0.4.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 +59 -3
- package/dist/{chunk-PFU5VAO7.js → chunk-AUR2YQCW.js} +2 -2
- package/dist/{chunk-GH5DXHS5.js → chunk-OEJJXMYC.js} +461 -97
- package/dist/chunk-OEJJXMYC.js.map +1 -0
- package/dist/{chunk-3R6VQ3Z3.js → chunk-VSJUGUH7.js} +15 -3
- package/dist/chunk-VSJUGUH7.js.map +1 -0
- package/dist/cli/index.cjs +434 -75
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +35 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +475 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +180 -38
- package/dist/index.d.ts +180 -38
- 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/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> {
|
|
@@ -268,16 +269,21 @@ declare class MetaobjectRefField<R extends boolean> extends GidField<R> {
|
|
|
268
269
|
constructor(target: TypeRef, opts: RefOptions<R>);
|
|
269
270
|
validations(): FieldValidation[];
|
|
270
271
|
}
|
|
272
|
+
declare class MixedRefField<R extends boolean> extends GidField<R> {
|
|
273
|
+
private readonly targets;
|
|
274
|
+
readonly shopifyType = "mixed_reference";
|
|
275
|
+
constructor(targets: readonly TypeRef[], opts: RefOptions<R>);
|
|
276
|
+
validations(): FieldValidation[];
|
|
277
|
+
}
|
|
271
278
|
declare function product<R extends boolean = false>(opts?: RefOptions<R>): SimpleRefField<R>;
|
|
272
279
|
declare function variant<R extends boolean = false>(opts?: RefOptions<R>): SimpleRefField<R>;
|
|
273
280
|
declare function collection<R extends boolean = false>(opts?: RefOptions<R>): SimpleRefField<R>;
|
|
274
281
|
declare function page<R extends boolean = false>(opts?: RefOptions<R>): SimpleRefField<R>;
|
|
275
282
|
declare function file<R extends boolean = false>(opts?: FileOptions<R>): FileField<R>;
|
|
276
283
|
declare function ref<R extends boolean = false>(target: TypeRef, opts?: RefOptions<R>): MetaobjectRefField<R>;
|
|
284
|
+
declare function mixedRef<R extends boolean = false>(targets: readonly TypeRef[], opts?: RefOptions<R>): MixedRefField<R>;
|
|
277
285
|
|
|
278
|
-
interface BaseOptions<R extends boolean = false> {
|
|
279
|
-
name?: string;
|
|
280
|
-
description?: string;
|
|
286
|
+
interface BaseOptions<R extends boolean = false> extends CommonFieldOptions {
|
|
281
287
|
required?: R;
|
|
282
288
|
}
|
|
283
289
|
declare abstract class StringScalarField<R extends boolean> extends Field<string, string, R> {
|
|
@@ -323,9 +329,7 @@ declare function url<R extends boolean = false>(opts?: UrlOptions<R>): UrlField<
|
|
|
323
329
|
declare function color<R extends boolean = false>(opts?: BaseOptions<R>): ColorField<R>;
|
|
324
330
|
declare function json<R extends boolean = false>(opts?: BaseOptions<R>): JsonField<R>;
|
|
325
331
|
|
|
326
|
-
interface TextOptions<R extends boolean = false> {
|
|
327
|
-
name?: string;
|
|
328
|
-
description?: string;
|
|
332
|
+
interface TextOptions<R extends boolean = false> extends CommonFieldOptions {
|
|
329
333
|
required?: R;
|
|
330
334
|
min?: number;
|
|
331
335
|
max?: number;
|
|
@@ -367,6 +371,7 @@ declare const m: {
|
|
|
367
371
|
readonly page: typeof page;
|
|
368
372
|
readonly file: typeof file;
|
|
369
373
|
readonly ref: typeof ref;
|
|
374
|
+
readonly mixedRef: typeof mixedRef;
|
|
370
375
|
readonly list: typeof list;
|
|
371
376
|
};
|
|
372
377
|
|
|
@@ -392,6 +397,11 @@ type InferInput<F extends FieldMap> = Simplify<{
|
|
|
392
397
|
[K in OptionalKeys<F>]?: FieldIn<F[K]>;
|
|
393
398
|
}>;
|
|
394
399
|
|
|
400
|
+
interface FieldCapabilitiesInput {
|
|
401
|
+
adminFilterable?: {
|
|
402
|
+
enabled: boolean;
|
|
403
|
+
};
|
|
404
|
+
}
|
|
395
405
|
interface FieldDefinitionInput {
|
|
396
406
|
key: string;
|
|
397
407
|
name: string;
|
|
@@ -399,19 +409,44 @@ interface FieldDefinitionInput {
|
|
|
399
409
|
required: boolean;
|
|
400
410
|
type: string;
|
|
401
411
|
validations: FieldValidation[];
|
|
412
|
+
capabilities?: FieldCapabilitiesInput;
|
|
413
|
+
}
|
|
414
|
+
interface AccessInput {
|
|
415
|
+
admin?: string;
|
|
416
|
+
storefront?: string;
|
|
417
|
+
customerAccount?: string;
|
|
418
|
+
}
|
|
419
|
+
interface RenderableData {
|
|
420
|
+
metaTitleKey?: string;
|
|
421
|
+
metaDescriptionKey?: string;
|
|
422
|
+
}
|
|
423
|
+
interface OnlineStoreData {
|
|
424
|
+
urlHandle: string;
|
|
425
|
+
createRedirects?: boolean;
|
|
426
|
+
}
|
|
427
|
+
interface CapabilitiesInput {
|
|
428
|
+
publishable?: {
|
|
429
|
+
enabled: boolean;
|
|
430
|
+
};
|
|
431
|
+
translatable?: {
|
|
432
|
+
enabled: boolean;
|
|
433
|
+
};
|
|
434
|
+
renderable?: {
|
|
435
|
+
enabled: boolean;
|
|
436
|
+
data?: RenderableData;
|
|
437
|
+
};
|
|
438
|
+
onlineStore?: {
|
|
439
|
+
enabled: boolean;
|
|
440
|
+
data?: OnlineStoreData;
|
|
441
|
+
};
|
|
402
442
|
}
|
|
403
443
|
interface MetaobjectDefinitionInput {
|
|
404
444
|
type: string;
|
|
405
445
|
name: string;
|
|
406
446
|
description?: string;
|
|
407
447
|
displayNameKey?: string;
|
|
408
|
-
access?:
|
|
409
|
-
|
|
410
|
-
storefront?: string;
|
|
411
|
-
};
|
|
412
|
-
capabilities?: Record<string, {
|
|
413
|
-
enabled: boolean;
|
|
414
|
-
}>;
|
|
448
|
+
access?: AccessInput;
|
|
449
|
+
capabilities?: CapabilitiesInput;
|
|
415
450
|
fieldDefinitions: FieldDefinitionInput[];
|
|
416
451
|
}
|
|
417
452
|
declare function toDefinitionInput<F extends FieldMap>(schema: MetaobjectSchema<F>): MetaobjectDefinitionInput;
|
|
@@ -419,16 +454,30 @@ declare function toDefinitionInput<F extends FieldMap>(schema: MetaobjectSchema<
|
|
|
419
454
|
interface AccessConfig {
|
|
420
455
|
admin?: "merchant_read" | "merchant_read_write";
|
|
421
456
|
storefront?: "public_read" | "none";
|
|
457
|
+
customerAccount?: "none" | "read";
|
|
458
|
+
}
|
|
459
|
+
/** SEO metadata: `true` enables with auto keys; an object maps field keys as title/description. */
|
|
460
|
+
type RenderableConfig = boolean | {
|
|
461
|
+
metaTitleKey?: string;
|
|
462
|
+
metaDescriptionKey?: string;
|
|
463
|
+
};
|
|
464
|
+
/** Publish entries as Online Store web pages. `urlHandle` is required by Shopify. */
|
|
465
|
+
interface OnlineStoreConfig {
|
|
466
|
+
urlHandle: string;
|
|
467
|
+
createRedirects?: boolean;
|
|
422
468
|
}
|
|
423
469
|
interface CapabilitiesConfig {
|
|
424
470
|
publishable?: boolean;
|
|
425
471
|
translatable?: boolean;
|
|
426
|
-
renderable?:
|
|
472
|
+
renderable?: RenderableConfig;
|
|
473
|
+
onlineStore?: OnlineStoreConfig;
|
|
427
474
|
}
|
|
428
475
|
interface MetaobjectConfig<F> {
|
|
429
476
|
name: string;
|
|
430
477
|
description?: string;
|
|
431
478
|
displayName?: keyof F & string;
|
|
479
|
+
/** Override the global config scope for this metaobject. */
|
|
480
|
+
scope?: "app" | "merchant";
|
|
432
481
|
access?: AccessConfig;
|
|
433
482
|
capabilities?: CapabilitiesConfig;
|
|
434
483
|
fields: F;
|
|
@@ -459,17 +508,54 @@ interface MetaobjectSchema<F extends FieldMap> {
|
|
|
459
508
|
}
|
|
460
509
|
declare function defineMetaobject<F extends Record<string, unknown>>(handle: string, config: MetaobjectConfig<F>): F extends FieldMap ? MetaobjectSchema<F> : never;
|
|
461
510
|
|
|
511
|
+
interface RemoteAccess {
|
|
512
|
+
admin?: string;
|
|
513
|
+
storefront?: string;
|
|
514
|
+
customerAccount?: string;
|
|
515
|
+
}
|
|
516
|
+
/** Normalized capability comparison shape. `enabled` is compared for every capability
|
|
517
|
+
* present locally; `data` keys are compared only when locally declared. [design §8] */
|
|
518
|
+
interface RemoteRenderable {
|
|
519
|
+
enabled: boolean;
|
|
520
|
+
data?: {
|
|
521
|
+
metaTitleKey?: string;
|
|
522
|
+
metaDescriptionKey?: string;
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
interface RemoteOnlineStore {
|
|
526
|
+
enabled: boolean;
|
|
527
|
+
data?: {
|
|
528
|
+
urlHandle?: string;
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
interface RemoteCapabilities {
|
|
532
|
+
publishable?: {
|
|
533
|
+
enabled: boolean;
|
|
534
|
+
};
|
|
535
|
+
translatable?: {
|
|
536
|
+
enabled: boolean;
|
|
537
|
+
};
|
|
538
|
+
renderable?: RemoteRenderable;
|
|
539
|
+
onlineStore?: RemoteOnlineStore;
|
|
540
|
+
}
|
|
462
541
|
interface RemoteField {
|
|
463
542
|
key: string;
|
|
464
543
|
type: string;
|
|
465
544
|
required: boolean;
|
|
545
|
+
filterable: boolean;
|
|
466
546
|
validations: FieldValidation[];
|
|
467
547
|
}
|
|
468
548
|
interface RemoteDefinition {
|
|
469
549
|
type: string;
|
|
470
550
|
name?: string;
|
|
551
|
+
description?: string;
|
|
552
|
+
displayNameKey?: string;
|
|
553
|
+
access?: RemoteAccess;
|
|
554
|
+
capabilities?: RemoteCapabilities;
|
|
471
555
|
fields: RemoteField[];
|
|
472
556
|
}
|
|
557
|
+
/** Normalize a (already scope-resolved) definition input to the diff shape. */
|
|
558
|
+
declare function normalizeDefinition(def: MetaobjectDefinitionInput): RemoteDefinition;
|
|
473
559
|
declare function normalizeLocal<F extends FieldMap>(schema: MetaobjectSchema<F>): RemoteDefinition;
|
|
474
560
|
interface PulledFieldDefinition {
|
|
475
561
|
key: string;
|
|
@@ -478,10 +564,45 @@ interface PulledFieldDefinition {
|
|
|
478
564
|
} | string;
|
|
479
565
|
required: boolean;
|
|
480
566
|
validations?: FieldValidation[];
|
|
567
|
+
capabilities?: {
|
|
568
|
+
adminFilterable?: {
|
|
569
|
+
enabled: boolean;
|
|
570
|
+
} | null;
|
|
571
|
+
} | null;
|
|
572
|
+
}
|
|
573
|
+
interface PulledAccess {
|
|
574
|
+
admin?: string | null;
|
|
575
|
+
storefront?: string | null;
|
|
576
|
+
customerAccount?: string | null;
|
|
577
|
+
}
|
|
578
|
+
interface PulledCapabilities {
|
|
579
|
+
publishable?: {
|
|
580
|
+
enabled: boolean;
|
|
581
|
+
} | null;
|
|
582
|
+
translatable?: {
|
|
583
|
+
enabled: boolean;
|
|
584
|
+
} | null;
|
|
585
|
+
renderable?: {
|
|
586
|
+
enabled: boolean;
|
|
587
|
+
data?: {
|
|
588
|
+
metaTitleKey?: string | null;
|
|
589
|
+
metaDescriptionKey?: string | null;
|
|
590
|
+
} | null;
|
|
591
|
+
} | null;
|
|
592
|
+
onlineStore?: {
|
|
593
|
+
enabled: boolean;
|
|
594
|
+
data?: {
|
|
595
|
+
urlHandle?: string | null;
|
|
596
|
+
} | null;
|
|
597
|
+
} | null;
|
|
481
598
|
}
|
|
482
599
|
interface PulledDefinition {
|
|
483
600
|
type: string;
|
|
484
601
|
name?: string;
|
|
602
|
+
description?: string | null;
|
|
603
|
+
displayNameKey?: string | null;
|
|
604
|
+
access?: PulledAccess | null;
|
|
605
|
+
capabilities?: PulledCapabilities | null;
|
|
485
606
|
fieldDefinitions: PulledFieldDefinition[];
|
|
486
607
|
}
|
|
487
608
|
declare function normalizeRemote(def: PulledDefinition): RemoteDefinition;
|
|
@@ -493,10 +614,17 @@ declare function normalizeRemote(def: PulledDefinition): RemoteDefinition;
|
|
|
493
614
|
*/
|
|
494
615
|
declare function generateSchemaSource(defs: RemoteDefinition[]): string;
|
|
495
616
|
|
|
617
|
+
/** Definition-level metadata that `updateDefinition` reconciles. */
|
|
618
|
+
type DefinitionChange = "name" | "description" | "displayNameKey" | "access" | "capabilities";
|
|
496
619
|
type DiffOp = {
|
|
497
620
|
kind: "createDefinition";
|
|
498
621
|
type: string;
|
|
499
622
|
definition: RemoteDefinition;
|
|
623
|
+
} | {
|
|
624
|
+
kind: "updateDefinition";
|
|
625
|
+
type: string;
|
|
626
|
+
changes: DefinitionChange[];
|
|
627
|
+
destructive?: true;
|
|
500
628
|
} | {
|
|
501
629
|
kind: "addField";
|
|
502
630
|
type: string;
|
|
@@ -521,6 +649,18 @@ type DiffOp = {
|
|
|
521
649
|
};
|
|
522
650
|
declare function diff(local: RemoteDefinition[], remote: RemoteDefinition[]): DiffOp[];
|
|
523
651
|
|
|
652
|
+
type AnySchema$1 = MetaobjectSchema<any>;
|
|
653
|
+
/** The subset of config that scope resolution reads. */
|
|
654
|
+
type ScopeConfig = Pick<Config, "scope" | "merchantEditable">;
|
|
655
|
+
type Scope = "app" | "merchant";
|
|
656
|
+
/**
|
|
657
|
+
* Resolve each schema's canonical (`$app:`) definition input into the effective
|
|
658
|
+
* input the sync pipeline pushes: definition `type` and reference targets are
|
|
659
|
+
* rewritten to each metaobject's effective scope, and `access.admin` is resolved.
|
|
660
|
+
* `schema.type` / `m.ref` public values are unchanged. [design §6]
|
|
661
|
+
*/
|
|
662
|
+
declare function resolveDefinitions(schemas: AnySchema$1[], config?: ScopeConfig): MetaobjectDefinitionInput[];
|
|
663
|
+
|
|
524
664
|
/**
|
|
525
665
|
* A definition read back from the store. `type` is the canonical "$app:…" key
|
|
526
666
|
* (the REQUESTED type, re-labeled from the resolved `app--…` form Shopify
|
|
@@ -589,8 +729,10 @@ type UserError = {
|
|
|
589
729
|
* thrown); only transport / top-level GraphQL errors propagate. [design §7, §8]
|
|
590
730
|
*
|
|
591
731
|
* Create ops run in dependency order (a referenced type before its referencer);
|
|
592
|
-
* types in a reference cycle are
|
|
593
|
-
*
|
|
732
|
+
* types entangled in a reference cycle are created two-pass — created with the
|
|
733
|
+
* cycle-breaking ref fields stripped, then updated to add them once every member
|
|
734
|
+
* exists. Results are returned in plan order; client calls happen in execution
|
|
735
|
+
* order. [design §7]
|
|
594
736
|
*/
|
|
595
737
|
declare function push(client: AdminGraphQLClient, plan: DiffOp[], sources: {
|
|
596
738
|
definitions: MetaobjectDefinitionInput[];
|
|
@@ -599,4 +741,4 @@ declare function push(client: AdminGraphQLClient, plan: DiffOp[], sources: {
|
|
|
599
741
|
|
|
600
742
|
type AnySchema = MetaobjectSchema<any>;
|
|
601
743
|
|
|
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 };
|
|
744
|
+
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-
|
|
15
|
+
} from "./chunk-OEJJXMYC.js";
|
|
14
16
|
import {
|
|
15
17
|
DEFAULT_API_VERSION,
|
|
16
18
|
SyncTransportError,
|
|
17
19
|
defineConfig,
|
|
18
20
|
validateConfig
|
|
19
|
-
} from "./chunk-
|
|
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
|
};
|
package/dist/node/client.cjs.map
CHANGED
|
@@ -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;;;
|
|
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":[]}
|
package/dist/node/client.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fmaplabs/meta-manifest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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":[]}
|