@adminiumjs/add-on-contracts 0.2.1

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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * `product-personalizer@1` (24 §5.5, added 2026-08-05 with `personalizer`) —
3
+ * the one contract whose implementation spans three surfaces (shopper, staff,
4
+ * dashboard), which is why it is a contract at all rather than a screen.
5
+ *
6
+ * Landed here with wave 4a even though its implementation ships in 4b: the
7
+ * registry is frozen once, before anything ships, rather than widened after a
8
+ * release (§1.1's sequencing note).
9
+ */
10
+ import { z } from 'zod';
11
+ import type { FileRef } from './common.js';
12
+ export type ZoneKind = 'text-line' | 'text-block' | 'image' | 'colour';
13
+ export type ZoneFinish = 'engraved' | 'raised' | 'printed' | 'painted';
14
+ export interface Zone {
15
+ id: string;
16
+ name: string;
17
+ kind: ZoneKind;
18
+ shape: {
19
+ type: 'rect' | 'ellipse';
20
+ xMm: number;
21
+ yMm: number;
22
+ wMm: number;
23
+ hMm: number;
24
+ };
25
+ constraints: {
26
+ maxChars?: number;
27
+ fonts?: string[];
28
+ minSizeMm?: number;
29
+ maxSizeMm?: number;
30
+ palette?: string[];
31
+ };
32
+ finish: ZoneFinish;
33
+ /** Where the zone lands on each supplied angle; absent ⇒ not visible there. */
34
+ perAngle: Record<string, {
35
+ xPct: number;
36
+ yPct: number;
37
+ wPct: number;
38
+ hPct: number;
39
+ skewDeg?: number;
40
+ }>;
41
+ }
42
+ export interface Template {
43
+ productKey: string;
44
+ angles: {
45
+ id: string;
46
+ label: string;
47
+ fileId: string;
48
+ }[];
49
+ zones: Zone[];
50
+ }
51
+ export interface Personalization {
52
+ templateId: string;
53
+ values: Record<string, string>;
54
+ font?: string;
55
+ sizeMm?: number;
56
+ finish?: ZoneFinish;
57
+ }
58
+ export interface ProductRef {
59
+ productKey: string;
60
+ variant?: string;
61
+ quantity: number;
62
+ }
63
+ export interface PreviewRef {
64
+ fileId: string;
65
+ angle: string;
66
+ widthPx: number;
67
+ /** Content hash — equal values + equal angle ⇒ equal digest (determinism). */
68
+ digest: string;
69
+ }
70
+ /**
71
+ * An overrun carries BOTH remedies with their numbers — the UI renders them as
72
+ * buttons. A bare "doesn't fit" is a contract violation, not a UI choice.
73
+ */
74
+ export type Verdict = {
75
+ zone: string;
76
+ ok: true;
77
+ } | {
78
+ zone: string;
79
+ ok: false;
80
+ reason: string;
81
+ remedies: {
82
+ setSizeMm?: number;
83
+ shortenToChars?: number;
84
+ };
85
+ };
86
+ export interface ProductPersonalizer {
87
+ readonly key: string;
88
+ available(product: ProductRef): {
89
+ ok: true;
90
+ } | {
91
+ ok: false;
92
+ reason: string;
93
+ };
94
+ /** The shopper's surface. Resolves to their choices, or null if they backed out. */
95
+ open(product: ProductRef, initial?: Personalization): Promise<Personalization | null>;
96
+ /** Deterministic: same values + same angle ⇒ same image. */
97
+ render(p: Personalization, opts: {
98
+ angle: string;
99
+ widthPx: number;
100
+ }): Promise<PreviewRef>;
101
+ /** What goes to the machine — outlines, layered. Never drives hardware. */
102
+ productionFile(p: Personalization): Promise<FileRef>;
103
+ validate(p: Personalization, t: Template): Verdict[];
104
+ }
105
+ export declare const zoneSchema: z.ZodObject<{
106
+ id: z.ZodString;
107
+ name: z.ZodString;
108
+ kind: z.ZodEnum<{
109
+ "text-line": "text-line";
110
+ "text-block": "text-block";
111
+ image: "image";
112
+ colour: "colour";
113
+ }>;
114
+ shape: z.ZodObject<{
115
+ type: z.ZodEnum<{
116
+ rect: "rect";
117
+ ellipse: "ellipse";
118
+ }>;
119
+ xMm: z.ZodNumber;
120
+ yMm: z.ZodNumber;
121
+ wMm: z.ZodNumber;
122
+ hMm: z.ZodNumber;
123
+ }, z.core.$strict>;
124
+ constraints: z.ZodObject<{
125
+ maxChars: z.ZodOptional<z.ZodNumber>;
126
+ fonts: z.ZodOptional<z.ZodArray<z.ZodString>>;
127
+ minSizeMm: z.ZodOptional<z.ZodNumber>;
128
+ maxSizeMm: z.ZodOptional<z.ZodNumber>;
129
+ palette: z.ZodOptional<z.ZodArray<z.ZodString>>;
130
+ }, z.core.$strict>;
131
+ finish: z.ZodEnum<{
132
+ engraved: "engraved";
133
+ raised: "raised";
134
+ printed: "printed";
135
+ painted: "painted";
136
+ }>;
137
+ perAngle: z.ZodRecord<z.ZodString, z.ZodObject<{
138
+ xPct: z.ZodNumber;
139
+ yPct: z.ZodNumber;
140
+ wPct: z.ZodNumber;
141
+ hPct: z.ZodNumber;
142
+ skewDeg: z.ZodOptional<z.ZodNumber>;
143
+ }, z.core.$strict>>;
144
+ }, z.core.$strict>;
145
+ export declare const templateSchema: z.ZodObject<{
146
+ productKey: z.ZodString;
147
+ angles: z.ZodArray<z.ZodObject<{
148
+ id: z.ZodString;
149
+ label: z.ZodString;
150
+ fileId: z.ZodString;
151
+ }, z.core.$strict>>;
152
+ zones: z.ZodArray<z.ZodObject<{
153
+ id: z.ZodString;
154
+ name: z.ZodString;
155
+ kind: z.ZodEnum<{
156
+ "text-line": "text-line";
157
+ "text-block": "text-block";
158
+ image: "image";
159
+ colour: "colour";
160
+ }>;
161
+ shape: z.ZodObject<{
162
+ type: z.ZodEnum<{
163
+ rect: "rect";
164
+ ellipse: "ellipse";
165
+ }>;
166
+ xMm: z.ZodNumber;
167
+ yMm: z.ZodNumber;
168
+ wMm: z.ZodNumber;
169
+ hMm: z.ZodNumber;
170
+ }, z.core.$strict>;
171
+ constraints: z.ZodObject<{
172
+ maxChars: z.ZodOptional<z.ZodNumber>;
173
+ fonts: z.ZodOptional<z.ZodArray<z.ZodString>>;
174
+ minSizeMm: z.ZodOptional<z.ZodNumber>;
175
+ maxSizeMm: z.ZodOptional<z.ZodNumber>;
176
+ palette: z.ZodOptional<z.ZodArray<z.ZodString>>;
177
+ }, z.core.$strict>;
178
+ finish: z.ZodEnum<{
179
+ engraved: "engraved";
180
+ raised: "raised";
181
+ printed: "printed";
182
+ painted: "painted";
183
+ }>;
184
+ perAngle: z.ZodRecord<z.ZodString, z.ZodObject<{
185
+ xPct: z.ZodNumber;
186
+ yPct: z.ZodNumber;
187
+ wPct: z.ZodNumber;
188
+ hPct: z.ZodNumber;
189
+ skewDeg: z.ZodOptional<z.ZodNumber>;
190
+ }, z.core.$strict>>;
191
+ }, z.core.$strict>>;
192
+ }, z.core.$strict>;
193
+ export declare const personalizationSchema: z.ZodObject<{
194
+ templateId: z.ZodString;
195
+ values: z.ZodRecord<z.ZodString, z.ZodString>;
196
+ font: z.ZodOptional<z.ZodString>;
197
+ sizeMm: z.ZodOptional<z.ZodNumber>;
198
+ finish: z.ZodOptional<z.ZodEnum<{
199
+ engraved: "engraved";
200
+ raised: "raised";
201
+ printed: "printed";
202
+ painted: "painted";
203
+ }>>;
204
+ }, z.core.$strict>;
205
+ //# sourceMappingURL=product-personalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-personalizer.d.ts","sourceRoot":"","sources":["../src/product-personalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `product-personalizer@1` (24 §5.5, added 2026-08-05 with `personalizer`) —
3
+ * the one contract whose implementation spans three surfaces (shopper, staff,
4
+ * dashboard), which is why it is a contract at all rather than a screen.
5
+ *
6
+ * Landed here with wave 4a even though its implementation ships in 4b: the
7
+ * registry is frozen once, before anything ships, rather than widened after a
8
+ * release (§1.1's sequencing note).
9
+ */
10
+ import { z } from 'zod';
11
+ export const zoneSchema = z
12
+ .object({
13
+ id: z.string().min(1),
14
+ name: z.string().min(1),
15
+ kind: z.enum(['text-line', 'text-block', 'image', 'colour']),
16
+ shape: z
17
+ .object({
18
+ type: z.enum(['rect', 'ellipse']),
19
+ xMm: z.number(),
20
+ yMm: z.number(),
21
+ wMm: z.number().positive(),
22
+ hMm: z.number().positive(),
23
+ })
24
+ .strict(),
25
+ constraints: z
26
+ .object({
27
+ maxChars: z.number().int().positive().optional(),
28
+ fonts: z.array(z.string().min(1)).optional(),
29
+ minSizeMm: z.number().positive().optional(),
30
+ maxSizeMm: z.number().positive().optional(),
31
+ palette: z.array(z.string().min(1)).optional(),
32
+ })
33
+ .strict(),
34
+ finish: z.enum(['engraved', 'raised', 'printed', 'painted']),
35
+ perAngle: z.record(z.string(), z
36
+ .object({
37
+ xPct: z.number(),
38
+ yPct: z.number(),
39
+ wPct: z.number(),
40
+ hPct: z.number(),
41
+ skewDeg: z.number().optional(),
42
+ })
43
+ .strict()),
44
+ })
45
+ .strict();
46
+ export const templateSchema = z
47
+ .object({
48
+ productKey: z.string().min(1),
49
+ angles: z
50
+ .array(z.object({ id: z.string().min(1), label: z.string().min(1), fileId: z.string().min(1) }).strict())
51
+ .min(1),
52
+ zones: z.array(zoneSchema),
53
+ })
54
+ .strict();
55
+ export const personalizationSchema = z
56
+ .object({
57
+ templateId: z.string().min(1),
58
+ values: z.record(z.string(), z.string()),
59
+ font: z.string().min(1).optional(),
60
+ sizeMm: z.number().positive().optional(),
61
+ finish: z.enum(['engraved', 'raised', 'printed', 'painted']).optional(),
62
+ })
63
+ .strict();
64
+ //# sourceMappingURL=product-personalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-personalizer.js","sourceRoot":"","sources":["../src/product-personalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * `shipping-carrier@1` (24 §5.5) — one delivery company, shaped so the second
3
+ * one is a copy.
4
+ *
5
+ * The claim "each shipping company is its own add-on, and the next is this repo
6
+ * with one file replaced" rests entirely on the conformance suite in
7
+ * `@adminium/add-on-contracts/testing`, which every transport runs against
8
+ * itself — the demo one and the real one alike.
9
+ */
10
+ import { z } from 'zod';
11
+ import type { FileRef } from './common.js';
12
+ export interface Parcel {
13
+ weightKg: number;
14
+ lengthCm: number;
15
+ widthCm: number;
16
+ heightCm: number;
17
+ contents: string;
18
+ }
19
+ export interface Address {
20
+ name: string;
21
+ lines: string[];
22
+ city: string;
23
+ postcode: string;
24
+ country: string;
25
+ }
26
+ export interface Rate {
27
+ code: string;
28
+ service: string;
29
+ amount: number;
30
+ currency: string;
31
+ /** ISO date — when the carrier says it arrives. */
32
+ estimatedDelivery: string;
33
+ }
34
+ export interface OrderRef {
35
+ /** The host's own reference, e.g. `MP-4118`. */
36
+ reference: string;
37
+ }
38
+ export interface Shipment {
39
+ id: string;
40
+ tracking: string;
41
+ labelFileId: string;
42
+ /** ISO datetime — the collection window the carrier committed to. */
43
+ collectionFrom: string;
44
+ collectionTo: string;
45
+ rate: Rate;
46
+ }
47
+ export interface TrackEvent {
48
+ at: string;
49
+ place: string;
50
+ status: string;
51
+ description: string;
52
+ }
53
+ /**
54
+ * A carrier refusal is DATA, never a thrown string: the works needs the
55
+ * carrier's own message verbatim to act on it, and a rejected postcode is an
56
+ * ordinary outcome rather than a crash.
57
+ */
58
+ export declare class CarrierError extends Error {
59
+ readonly code: string;
60
+ /** The carrier's own words, rendered in mono and quoted in the UI. */
61
+ readonly carrierMessage: string;
62
+ readonly retryable: boolean;
63
+ constructor(opts: {
64
+ code: string;
65
+ carrierMessage: string;
66
+ retryable?: boolean;
67
+ });
68
+ }
69
+ export interface ShippingCarrier {
70
+ readonly key: string;
71
+ quote(parcel: Parcel, from: Address, to: Address): Promise<Rate[]>;
72
+ book(rate: Rate, order: OrderRef): Promise<Shipment>;
73
+ /** An unknown reference returns an empty list — it never throws. */
74
+ track(tracking: string): Promise<TrackEvent[]>;
75
+ label(shipmentId: string): Promise<FileRef>;
76
+ cancel(shipmentId: string): Promise<void>;
77
+ }
78
+ export declare const parcelSchema: z.ZodObject<{
79
+ weightKg: z.ZodNumber;
80
+ lengthCm: z.ZodNumber;
81
+ widthCm: z.ZodNumber;
82
+ heightCm: z.ZodNumber;
83
+ contents: z.ZodString;
84
+ }, z.core.$strict>;
85
+ export declare const addressSchema: z.ZodObject<{
86
+ name: z.ZodString;
87
+ lines: z.ZodArray<z.ZodString>;
88
+ city: z.ZodString;
89
+ postcode: z.ZodString;
90
+ country: z.ZodString;
91
+ }, z.core.$strict>;
92
+ export declare const rateSchema: z.ZodObject<{
93
+ code: z.ZodString;
94
+ service: z.ZodString;
95
+ amount: z.ZodNumber;
96
+ currency: z.ZodString;
97
+ estimatedDelivery: z.ZodString;
98
+ }, z.core.$strict>;
99
+ export declare const shipmentSchema: z.ZodObject<{
100
+ id: z.ZodString;
101
+ tracking: z.ZodString;
102
+ labelFileId: z.ZodString;
103
+ collectionFrom: z.ZodString;
104
+ collectionTo: z.ZodString;
105
+ rate: z.ZodObject<{
106
+ code: z.ZodString;
107
+ service: z.ZodString;
108
+ amount: z.ZodNumber;
109
+ currency: z.ZodString;
110
+ estimatedDelivery: z.ZodString;
111
+ }, z.core.$strict>;
112
+ }, z.core.$strict>;
113
+ export declare const trackEventSchema: z.ZodObject<{
114
+ at: z.ZodString;
115
+ place: z.ZodString;
116
+ status: z.ZodString;
117
+ description: z.ZodString;
118
+ }, z.core.$strict>;
119
+ //# sourceMappingURL=shipping-carrier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipping-carrier.d.ts","sourceRoot":"","sources":["../src/shipping-carrier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;CAOF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrD,oEAAoE;IACpE,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC/C,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,eAAO,MAAM,YAAY;;;;;;kBAQd,CAAC;AAEZ,eAAO,MAAM,aAAa;;;;;;kBAQf,CAAC;AAEZ,eAAO,MAAM,UAAU;;;;;;kBAQZ,CAAC;AAEZ,eAAO,MAAM,cAAc;;;;;;;;;;;;;kBAShB,CAAC;AAEZ,eAAO,MAAM,gBAAgB;;;;;kBAOlB,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `shipping-carrier@1` (24 §5.5) — one delivery company, shaped so the second
3
+ * one is a copy.
4
+ *
5
+ * The claim "each shipping company is its own add-on, and the next is this repo
6
+ * with one file replaced" rests entirely on the conformance suite in
7
+ * `@adminium/add-on-contracts/testing`, which every transport runs against
8
+ * itself — the demo one and the real one alike.
9
+ */
10
+ import { z } from 'zod';
11
+ /**
12
+ * A carrier refusal is DATA, never a thrown string: the works needs the
13
+ * carrier's own message verbatim to act on it, and a rejected postcode is an
14
+ * ordinary outcome rather than a crash.
15
+ */
16
+ export class CarrierError extends Error {
17
+ code;
18
+ /** The carrier's own words, rendered in mono and quoted in the UI. */
19
+ carrierMessage;
20
+ retryable;
21
+ constructor(opts) {
22
+ super(opts.carrierMessage);
23
+ this.name = 'CarrierError';
24
+ this.code = opts.code;
25
+ this.carrierMessage = opts.carrierMessage;
26
+ this.retryable = opts.retryable ?? true;
27
+ }
28
+ }
29
+ export const parcelSchema = z
30
+ .object({
31
+ weightKg: z.number().positive(),
32
+ lengthCm: z.number().positive(),
33
+ widthCm: z.number().positive(),
34
+ heightCm: z.number().positive(),
35
+ contents: z.string().min(1),
36
+ })
37
+ .strict();
38
+ export const addressSchema = z
39
+ .object({
40
+ name: z.string().min(1),
41
+ lines: z.array(z.string().min(1)).min(1),
42
+ city: z.string().min(1),
43
+ postcode: z.string().min(1),
44
+ country: z.string().min(1),
45
+ })
46
+ .strict();
47
+ export const rateSchema = z
48
+ .object({
49
+ code: z.string().min(1),
50
+ service: z.string().min(1),
51
+ amount: z.number().nonnegative(),
52
+ currency: z.string().length(3),
53
+ estimatedDelivery: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'estimatedDelivery must be an ISO date'),
54
+ })
55
+ .strict();
56
+ export const shipmentSchema = z
57
+ .object({
58
+ id: z.string().min(1),
59
+ tracking: z.string().min(1),
60
+ labelFileId: z.string().min(1),
61
+ collectionFrom: z.string().min(1),
62
+ collectionTo: z.string().min(1),
63
+ rate: rateSchema,
64
+ })
65
+ .strict();
66
+ export const trackEventSchema = z
67
+ .object({
68
+ at: z.string().min(1),
69
+ place: z.string().min(1),
70
+ status: z.string().min(1),
71
+ description: z.string().min(1),
72
+ })
73
+ .strict();
74
+ //# sourceMappingURL=shipping-carrier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipping-carrier.js","sourceRoot":"","sources":["../src/shipping-carrier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Slot registry v1 — CLOSED (24-marketplace-wave-4.md §5.4, eleven slots).
3
+ *
4
+ * A slot is a named place in a host surface, its payload, and its fill rule.
5
+ * The registry is closed for the same reason the widget-id vocabulary is: an
6
+ * open-ended extension point cannot be reviewed, translated, or kept working
7
+ * across host versions. Adding a slot is a spec change with a version bump on
8
+ * this package — never a pull request against an app.
9
+ *
10
+ * Every slot here is filled by something built in wave 4. A slot nobody fills
11
+ * is a guess about a future add-on, which is why an earlier draft's twelfth
12
+ * (`job.timeline.entries`) is absent.
13
+ */
14
+ import { z } from 'zod';
15
+ /** Which persona's surface the slot lives in. */
16
+ export declare const SLOT_SURFACES: readonly ["customer", "staff", "admin", "both"];
17
+ export type SlotSurface = (typeof SLOT_SURFACES)[number];
18
+ /**
19
+ * `multi` renders every enabled fill ordered by `order` then by add-on key, so
20
+ * the order is stable and does not depend on install sequence. `single` takes
21
+ * the lowest `order` and records a SLOT_CONFLICT warning naming the add-on that
22
+ * lost — never a silent override.
23
+ */
24
+ export declare const SLOT_FILLS: readonly ["multi", "single", "per-add-on"];
25
+ export type SlotFill = (typeof SLOT_FILLS)[number];
26
+ export interface SlotDefinition {
27
+ readonly id: string;
28
+ readonly surface: SlotSurface;
29
+ readonly fill: SlotFill;
30
+ /** What the host passes into the fill. */
31
+ readonly payload: string;
32
+ /** What the add-on renders or resolves to. */
33
+ readonly renders: string;
34
+ }
35
+ export declare const SLOT_REGISTRY: readonly [{
36
+ readonly id: "artwork.sources";
37
+ readonly surface: "customer";
38
+ readonly fill: "multi";
39
+ readonly payload: "the configured job: product, trim size mm, bleed mm, sides, quantity";
40
+ readonly renders: "an action tile, and a flow resolving to an ArtworkRef";
41
+ }, {
42
+ readonly id: "checkout.delivery.methods";
43
+ readonly surface: "customer";
44
+ readonly fill: "multi";
45
+ readonly payload: "parcel estimate + destination";
46
+ readonly renders: "selectable rate rows";
47
+ }, {
48
+ readonly id: "order.dispatch.panel";
49
+ readonly surface: "customer";
50
+ readonly fill: "single";
51
+ readonly payload: "the dispatch record";
52
+ readonly renders: "a read-only tracking view";
53
+ }, {
54
+ readonly id: "order.dispatch.actions";
55
+ readonly surface: "staff";
56
+ readonly fill: "multi";
57
+ readonly payload: "the order + its parcel estimate";
58
+ readonly renders: "an action and its result panel";
59
+ }, {
60
+ readonly id: "settings.add-on.panel";
61
+ readonly surface: "admin";
62
+ readonly fill: "per-add-on";
63
+ readonly payload: "the add-on's settings values, a patch handle, and sample records";
64
+ readonly renders: "a settings form, its controls and the sentence under each";
65
+ }, {
66
+ readonly id: "nav.add-on.routes";
67
+ readonly surface: "both";
68
+ readonly fill: "multi";
69
+ readonly payload: "—";
70
+ readonly renders: "full-screen routes under /add-ons/<key>/*";
71
+ }, {
72
+ readonly id: "product.options.personalize";
73
+ readonly surface: "customer";
74
+ readonly fill: "single";
75
+ readonly payload: "the product + its variant and quantity";
76
+ readonly renders: "a personalization surface resolving to a Personalization";
77
+ }, {
78
+ readonly id: "cart.line.preview";
79
+ readonly surface: "customer";
80
+ readonly fill: "multi";
81
+ readonly payload: "one basket / order line";
82
+ readonly renders: "a thumbnail and the values in words";
83
+ }, {
84
+ readonly id: "product.admin.panel";
85
+ readonly surface: "staff";
86
+ readonly fill: "multi";
87
+ readonly payload: "the product record";
88
+ readonly renders: "a setup panel (zones, constraints, sample)";
89
+ }, {
90
+ readonly id: "order.line.actions";
91
+ readonly surface: "staff";
92
+ readonly fill: "multi";
93
+ readonly payload: "one order line";
94
+ readonly renders: "per-line actions and their output";
95
+ }, {
96
+ readonly id: "record.editor.panel";
97
+ readonly surface: "admin";
98
+ readonly fill: "multi";
99
+ readonly payload: "the table name, the record, and write handles";
100
+ readonly renders: "a panel inside the generated dashboard's record editor";
101
+ }];
102
+ export type SlotId = (typeof SLOT_REGISTRY)[number]['id'];
103
+ export declare const SLOT_IDS: readonly SlotId[];
104
+ export declare const slotIdSchema: z.ZodEnum<{
105
+ "artwork.sources": "artwork.sources";
106
+ "checkout.delivery.methods": "checkout.delivery.methods";
107
+ "order.dispatch.panel": "order.dispatch.panel";
108
+ "order.dispatch.actions": "order.dispatch.actions";
109
+ "settings.add-on.panel": "settings.add-on.panel";
110
+ "nav.add-on.routes": "nav.add-on.routes";
111
+ "product.options.personalize": "product.options.personalize";
112
+ "cart.line.preview": "cart.line.preview";
113
+ "product.admin.panel": "product.admin.panel";
114
+ "order.line.actions": "order.line.actions";
115
+ "record.editor.panel": "record.editor.panel";
116
+ }>;
117
+ export declare function isSlotId(v: unknown): v is SlotId;
118
+ export declare function slotDefinition(id: SlotId): SlotDefinition;
119
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../src/slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,eAAO,MAAM,aAAa,iDAAkD,CAAC;AAC7E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,4CAA6C,CAAC;AACrE,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsFoB,CAAC;AAE/C,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAE1D,eAAO,MAAM,QAAQ,EAAqC,SAAS,MAAM,EAAE,CAAC;AAE5E,eAAO,MAAM,YAAY;;;;;;;;;;;;EAAuD,CAAC;AAIjF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAEhD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAKzD"}