@lamgig/content-schemas 1.0.0 → 1.1.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 +29 -2
- package/dist/{chunk-KBPOR3WL.js → chunk-GUUCVLL3.js} +63 -3
- package/dist/chunk-GUUCVLL3.js.map +1 -0
- package/dist/hash.js +1 -1
- package/dist/index.d.ts +136 -1
- package/dist/index.js +11 -1
- package/fixtures/v1/full.json +17 -0
- package/fixtures/v1/hashes.json +2 -2
- package/package.json +1 -1
- package/dist/chunk-KBPOR3WL.js.map +0 -1
package/README.md
CHANGED
|
@@ -44,8 +44,35 @@ get uniform strictness):
|
|
|
44
44
|
- Consumers MUST use the tolerant schemas and ignore unknown fields. The forward-compat tests
|
|
45
45
|
in [`test/contract.test.ts`](test/contract.test.ts) enforce exactly this: the v1 tolerant
|
|
46
46
|
schema accepts `full.json` plus an injected unknown optional field and an unknown module key.
|
|
47
|
-
-
|
|
48
|
-
valid payloads (`menu.categories: []`), never missing keys.
|
|
47
|
+
- The three original v1 modules (`siteSettings`, `branding`, `menu`) are **required**. Empty
|
|
48
|
+
states are valid payloads (`menu.categories: []`), never missing keys.
|
|
49
|
+
- **`commerce` (added in 1.1.0) is OPTIONAL**, and this is the compatibility rule working
|
|
50
|
+
rather than an inconsistency: every snapshot published before the module existed lacks the
|
|
51
|
+
key, and a rollback can make one of those the live payload at any time. `minimal.json` is
|
|
52
|
+
deliberately kept commerce-free as the pinned example of that shape. A consumer must treat
|
|
53
|
+
`modules.commerce` as possibly absent — `parsed.modules.commerce?.acceptsOnlineOrders`.
|
|
54
|
+
|
|
55
|
+
## The `commerce` module (1.1.0)
|
|
56
|
+
|
|
57
|
+
Online-ordering **configuration**. LamGig does not process orders and stores no provider
|
|
58
|
+
credentials: everything here is a public identifier or a display value, because a published
|
|
59
|
+
payload is world-readable immutable JSON.
|
|
60
|
+
|
|
61
|
+
| Field | Notes |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `provider` | `'none' \| 'other'` only. A published enum is a consumer-visible commitment, and no provider has been evaluated yet — do not switch on a brand name that is not in this union |
|
|
64
|
+
| `orderingUrl` | optional; **https only** |
|
|
65
|
+
| `locationRef`, `menuRef` | optional public provider identifiers |
|
|
66
|
+
| `currency` | ISO 4217 alpha-3, matching `menu` item prices |
|
|
67
|
+
| `acceptsOnlineOrders` | boolean |
|
|
68
|
+
| `prepTimeMinutes` | optional, 0–240 |
|
|
69
|
+
| `hours` | at most one entry per day, ordered `mon → sun`. A **closed** day is simply ABSENT |
|
|
70
|
+
|
|
71
|
+
**Two things a consumer must get right.** Times are `HH:MM` in the site's own
|
|
72
|
+
`siteSettings.timezone` and carry no offset. And `closes < opens` is **legal** — it means the
|
|
73
|
+
business closes after midnight — so a renderer that treats it as an error will break on a
|
|
74
|
+
perfectly valid payload. `fixtures/v1/full.json` pins an overnight Friday for exactly that
|
|
75
|
+
reason.
|
|
49
76
|
|
|
50
77
|
## Canonical serialization
|
|
51
78
|
|
|
@@ -10,6 +10,9 @@ var CURRENCY_PATTERN_SOURCE = "^[A-Z]{3}$";
|
|
|
10
10
|
var CURRENCY_PATTERN = new RegExp(CURRENCY_PATTERN_SOURCE);
|
|
11
11
|
var COUNTRY_PATTERN_SOURCE = "^[A-Z]{2}$";
|
|
12
12
|
var COUNTRY_PATTERN = new RegExp(COUNTRY_PATTERN_SOURCE);
|
|
13
|
+
var COMMERCE_DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
14
|
+
var TIME_24H_PATTERN_SOURCE = "^([01][0-9]|2[0-3]):[0-5][0-9]$";
|
|
15
|
+
var TIME_24H_PATTERN = new RegExp(TIME_24H_PATTERN_SOURCE);
|
|
13
16
|
function isIanaTimeZone(tz) {
|
|
14
17
|
try {
|
|
15
18
|
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
@@ -100,18 +103,70 @@ var makeMenuSchema = (o) => o({
|
|
|
100
103
|
});
|
|
101
104
|
var menuSchemaStrict = makeMenuSchema(strictFactory);
|
|
102
105
|
var menuSchema = makeMenuSchema(tolerantFactory);
|
|
106
|
+
var makeCommerceHoursSchema = (o) => z.array(
|
|
107
|
+
o({
|
|
108
|
+
day: z.enum(COMMERCE_DAYS),
|
|
109
|
+
opens: z.string().regex(TIME_24H_PATTERN),
|
|
110
|
+
closes: z.string().regex(TIME_24H_PATTERN)
|
|
111
|
+
})
|
|
112
|
+
).max(7).superRefine((entries, ctx) => {
|
|
113
|
+
const seen = /* @__PURE__ */ new Set();
|
|
114
|
+
let previous = -1;
|
|
115
|
+
entries.forEach((entry, index) => {
|
|
116
|
+
if (seen.has(entry.day)) {
|
|
117
|
+
ctx.addIssue({
|
|
118
|
+
code: "custom",
|
|
119
|
+
path: [index, "day"],
|
|
120
|
+
message: `duplicate day "${entry.day}"`
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
seen.add(entry.day);
|
|
124
|
+
const position = COMMERCE_DAYS.indexOf(entry.day);
|
|
125
|
+
if (position < previous) {
|
|
126
|
+
ctx.addIssue({
|
|
127
|
+
code: "custom",
|
|
128
|
+
path: [index, "day"],
|
|
129
|
+
message: "hours must be ordered mon \u2192 sun"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
previous = position;
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
var makeCommerceSchema = (o) => o({
|
|
136
|
+
provider: z.enum(["none", "other"]),
|
|
137
|
+
/** Where a customer is sent to order. Absent when `provider` is `none`. */
|
|
138
|
+
orderingUrl: z.url({ protocol: /^https$/ }).max(2048).optional(),
|
|
139
|
+
/** PUBLIC provider identifiers — never a key, token or secret. */
|
|
140
|
+
locationRef: z.string().min(1).max(128).optional(),
|
|
141
|
+
menuRef: z.string().min(1).max(128).optional(),
|
|
142
|
+
/** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */
|
|
143
|
+
currency: z.string().regex(CURRENCY_PATTERN),
|
|
144
|
+
acceptsOnlineOrders: z.boolean(),
|
|
145
|
+
/** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */
|
|
146
|
+
prepTimeMinutes: z.number().int().min(0).max(240).optional(),
|
|
147
|
+
hours: makeCommerceHoursSchema(o)
|
|
148
|
+
});
|
|
149
|
+
var commerceSchemaStrict = makeCommerceSchema(strictFactory);
|
|
150
|
+
var commerceSchema = makeCommerceSchema(tolerantFactory);
|
|
103
151
|
var makeContentFileSchema = (o) => o({
|
|
104
152
|
schemaVersion: z.literal(1),
|
|
105
153
|
site: o({ id: z.uuid(), slug: z.string().min(1) }),
|
|
106
154
|
publishedAt: z.iso.datetime(),
|
|
107
|
-
//
|
|
155
|
+
// The three v1 modules are REQUIRED; empty states are valid module payloads
|
|
108
156
|
// (e.g. menu.categories: []), never missing keys. A new required module is
|
|
109
157
|
// breaking (retained/rollback snapshots won't contain it) — bump
|
|
110
158
|
// SCHEMA_VERSION instead.
|
|
159
|
+
//
|
|
160
|
+
// `commerce` is therefore OPTIONAL (phase-7 D-3), and that is not a
|
|
161
|
+
// stylistic choice: every snapshot published before the module existed
|
|
162
|
+
// lacks the key, and a ROLLBACK can activate one of those at any time. A
|
|
163
|
+
// required `commerce` would retroactively invalidate the whole publish
|
|
164
|
+
// history. See ARCHITECTURE.md § 5 and docs/seams/commerce.md § 1.
|
|
111
165
|
modules: o({
|
|
112
166
|
siteSettings: makeSiteSettingsSchema(o),
|
|
113
167
|
branding: makeBrandingSchema(o),
|
|
114
|
-
menu: makeMenuSchema(o)
|
|
168
|
+
menu: makeMenuSchema(o),
|
|
169
|
+
commerce: makeCommerceSchema(o).optional()
|
|
115
170
|
})
|
|
116
171
|
});
|
|
117
172
|
var contentFileSchemaStrict = makeContentFileSchema(strictFactory);
|
|
@@ -172,6 +227,9 @@ export {
|
|
|
172
227
|
CURRENCY_PATTERN,
|
|
173
228
|
COUNTRY_PATTERN_SOURCE,
|
|
174
229
|
COUNTRY_PATTERN,
|
|
230
|
+
COMMERCE_DAYS,
|
|
231
|
+
TIME_24H_PATTERN_SOURCE,
|
|
232
|
+
TIME_24H_PATTERN,
|
|
175
233
|
isIanaTimeZone,
|
|
176
234
|
siteSettingsSchemaStrict,
|
|
177
235
|
siteSettingsSchema,
|
|
@@ -179,8 +237,10 @@ export {
|
|
|
179
237
|
brandingSchema,
|
|
180
238
|
menuSchemaStrict,
|
|
181
239
|
menuSchema,
|
|
240
|
+
commerceSchemaStrict,
|
|
241
|
+
commerceSchema,
|
|
182
242
|
contentFileSchemaStrict,
|
|
183
243
|
contentFileSchema,
|
|
184
244
|
canonicalize
|
|
185
245
|
};
|
|
186
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-GUUCVLL3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Version of the published-JSON contract. Additive changes (new OPTIONAL fields\n * and new OPTIONAL module keys) keep this number; any required addition is\n * breaking and bumps it. The full compatibility rule lives in this package's\n * README. See PLAN.md § Published JSON delivery and phase-2.md T1.5.\n */\nexport const SCHEMA_VERSION = 1;\n\n/** Publisher-enforced ceiling on the canonical payload size (1 MiB). */\nexport const MAX_CONTENT_BYTES = 1_048_576;\n\n// ---------------------------------------------------------------------------\n// Shared value patterns — single source of truth.\n//\n// LOCALE_PATTERN_SOURCE is the EXACT string used by the `site_settings.locale`\n// DB CHECK (supabase/migrations/*_content_site_settings.sql). Contract and\n// database must never disagree (phase-2.md T1.1): v1 supports the named BCP-47\n// subset `ll` or `ll-CC` only (e.g. `en`, `en-US`); script/numeric-region forms\n// (`zh-Hant`, `es-419`) are out of scope — widening later is additive.\n// HEX_COLOR / CURRENCY are likewise mirrored by DB CHECKs on `branding.colors`\n// and `menu_items.price_currency`.\n// ---------------------------------------------------------------------------\n\nexport const LOCALE_PATTERN_SOURCE = '^[a-z]{2}(-[A-Z]{2})?$';\nexport const LOCALE_PATTERN = new RegExp(LOCALE_PATTERN_SOURCE);\n\nexport const HEX_COLOR_PATTERN_SOURCE = '^#[0-9A-Fa-f]{6}$';\nexport const HEX_COLOR_PATTERN = new RegExp(HEX_COLOR_PATTERN_SOURCE);\n\n/** ISO 4217 alpha code shape (mirrored by the `price_currency` DB CHECK). */\nexport const CURRENCY_PATTERN_SOURCE = '^[A-Z]{3}$';\nexport const CURRENCY_PATTERN = new RegExp(CURRENCY_PATTERN_SOURCE);\n\n/** ISO 3166-1 alpha-2 country code shape. */\nexport const COUNTRY_PATTERN_SOURCE = '^[A-Z]{2}$';\nexport const COUNTRY_PATTERN = new RegExp(COUNTRY_PATTERN_SOURCE);\n\n/**\n * Days of the week, in the canonical order `commerce.hours` must be sorted by.\n * The order is part of the contract: a consumer rendering opening hours reads\n * the array straight through, so a payload listing Sunday before Monday would\n * render wrong without being malformed.\n */\nexport const COMMERCE_DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] as const;\n\n/**\n * 24-hour `HH:MM`. UNLIKE the four patterns above this one is deliberately NOT\n * mirrored by a DB CHECK: `commerce_settings.hours` is validated at the DB only\n * as \"an array of at most 7\" (see the migration header), and its element shape,\n * time syntax, day uniqueness and ordering are publish-gated here instead. A\n * schema-invalid draft may therefore exist in the table; it cannot be published.\n */\nexport const TIME_24H_PATTERN_SOURCE = '^([01][0-9]|2[0-3]):[0-5][0-9]$';\nexport const TIME_24H_PATTERN = new RegExp(TIME_24H_PATTERN_SOURCE);\n\n/** IANA time zone check via the runtime's own tz database. */\nexport function isIanaTimeZone(tz: string): boolean {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Two parse modes from ONE schema definition (phase-2.md T1):\n//\n// - producer-strict — `.strict()` everywhere; the dashboard validates its own\n// output against this before publishing. Unknown keys are defects.\n// - consumer-tolerant — unknown object keys are STRIPPED and unknown module\n// keys ignored, so a shipped consumer accepts a v1 payload that later gained\n// an optional field or a new module key (the additive changes the\n// compatibility rule promises).\n//\n// zod v4's `.strip()`/`.passthrough()` do NOT recurse into nested objects, so a\n// post-hoc transform of one tree silently leaves nested objects in the original\n// mode. Instead every object in the tree is built through an injected object\n// factory: `z.strictObject` for the strict pair, `z.object` (strips unknown\n// keys) for the tolerant pair. One definition, two instantiations.\n// ---------------------------------------------------------------------------\n\ntype ObjectFactory = <T extends z.ZodRawShape>(shape: T) => z.ZodObject<T>;\n\nconst strictFactory = z.strictObject as ObjectFactory;\nconst tolerantFactory = z.object as ObjectFactory;\n\n// --- siteSettings ----------------------------------------------------------\n\nconst makeSiteSettingsSchema = (o: ObjectFactory) =>\n o({\n siteName: z.string().min(1).max(120),\n tagline: z.string().max(200).optional(),\n contact: o({\n email: z.email().optional(),\n phone: z.string().min(1).optional(),\n address: o({\n line1: z.string().min(1),\n line2: z.string().min(1).optional(),\n city: z.string().min(1),\n region: z.string().min(1),\n postalCode: z.string().min(1),\n country: z.string().regex(COUNTRY_PATTERN),\n }).optional(),\n }),\n // Explicit known keys (NOT a record): strict rejects unknown platforms,\n // tolerant strips them — adding a platform later is additive.\n socialLinks: o({\n facebook: z.url().optional(),\n instagram: z.url().optional(),\n tiktok: z.url().optional(),\n x: z.url().optional(),\n youtube: z.url().optional(),\n yelp: z.url().optional(),\n google: z.url().optional(),\n }).optional(),\n locale: z.string().regex(LOCALE_PATTERN).default('en-US'),\n timezone: z\n .string()\n .min(1)\n .refine(isIanaTimeZone, 'must be an IANA time zone name')\n .default('America/Los_Angeles'),\n });\n\nexport const siteSettingsSchemaStrict = makeSiteSettingsSchema(strictFactory);\nexport const siteSettingsSchema = makeSiteSettingsSchema(tolerantFactory);\n\n// --- branding --------------------------------------------------------------\n\nconst makeBrandingSchema = (o: ObjectFactory) =>\n o({\n // Absolute URLs produced by the dashboard's storagePublicUrl builder\n // (apps/dashboard/src/lib/content-urls.ts). The DB stores storage PATHS;\n // the publish-time compose maps path -> URL. This package never imports\n // from the app (dependency direction, phase-2.md T1.7).\n logoUrl: z.url().optional(),\n faviconUrl: z.url().optional(),\n colors: o({\n primary: z.string().regex(HEX_COLOR_PATTERN),\n secondary: z.string().regex(HEX_COLOR_PATTERN).optional(),\n accent: z.string().regex(HEX_COLOR_PATTERN).optional(),\n background: z.string().regex(HEX_COLOR_PATTERN).optional(),\n text: z.string().regex(HEX_COLOR_PATTERN).optional(),\n }),\n fonts: o({\n heading: z.string().min(1).optional(),\n body: z.string().min(1).optional(),\n }).optional(),\n });\n\nexport const brandingSchemaStrict = makeBrandingSchema(strictFactory);\nexport const brandingSchema = makeBrandingSchema(tolerantFactory);\n\n// --- menu ------------------------------------------------------------------\n\nconst makeMenuItemSchema = (o: ObjectFactory) =>\n o({\n /** Stable DB UUID — identical across publishes (phase-2.md T1.3). */\n id: z.uuid(),\n name: z.string().min(1).max(200),\n description: z.string().max(2000).optional(),\n price: o({\n /** Integer minor units (e.g. cents). */\n amount: z.number().int().min(0),\n currency: z.string().regex(CURRENCY_PATTERN),\n }),\n imageUrl: z.url().optional(),\n tags: z.array(z.string().min(1)).max(20),\n sortOrder: z.number().int().min(0).max(100_000),\n isAvailable: z.boolean(),\n });\n\nconst makeMenuCategorySchema = (o: ObjectFactory) =>\n o({\n id: z.uuid(),\n name: z.string().min(1).max(200),\n description: z.string().max(2000).optional(),\n sortOrder: z.number().int().min(0).max(100_000),\n items: z.array(makeMenuItemSchema(o)),\n });\n\nconst makeMenuSchema = (o: ObjectFactory) =>\n o({\n /** Archived/deleted entities are simply ABSENT; empty menu = `[]`, never a missing key. */\n categories: z.array(makeMenuCategorySchema(o)),\n });\n\nexport const menuSchemaStrict = makeMenuSchema(strictFactory);\nexport const menuSchema = makeMenuSchema(tolerantFactory);\n\n// --- commerce --------------------------------------------------------------\n//\n// Online-ordering CONFIGURATION, never orders and never provider secrets\n// (docs/seams/commerce.md § \"What does NOT go in the module\"): a published\n// payload is world-readable immutable JSON, so a credential written here would\n// be permanent and public. Everything below is a public identifier or a display\n// value.\n//\n// The `provider` enum is `none | other` ONLY (phase-7 D-9). No provider has\n// been selected, and a published enum is a consumer-visible commitment: a\n// consumer that switches on `\"square\"` would be relying on a name the seam says\n// requires an evaluation and a decision record first. Adding a member later is\n// additive for a TOLERANT consumer and breaking for a strict one, which is the\n// right way round.\n//\n// TIMES ARE LOCAL to `siteSettings.timezone` and carry no offset — the site's\n// own clock is what a customer reads on the door. `closes < opens` is LEGAL and\n// means \"closes after midnight\" (docs/seams/commerce.md § 1); a consumer must\n// not treat it as an error.\n\nconst makeCommerceHoursSchema = (o: ObjectFactory) =>\n z\n .array(\n o({\n day: z.enum(COMMERCE_DAYS),\n opens: z.string().regex(TIME_24H_PATTERN),\n closes: z.string().regex(TIME_24H_PATTERN),\n }),\n )\n .max(7)\n // At most one entry per day, listed in COMMERCE_DAYS order. The DB CHECK\n // stops at \"an array of at most 7\" (deliberately — see the migration\n // header), so THIS is the layer that makes `hours` meaningful, and it runs\n // at publish time rather than at write time.\n .superRefine((entries, ctx) => {\n const seen = new Set<string>();\n let previous = -1;\n entries.forEach((entry, index) => {\n if (seen.has(entry.day)) {\n ctx.addIssue({\n code: 'custom',\n path: [index, 'day'],\n message: `duplicate day \"${entry.day}\"`,\n });\n }\n seen.add(entry.day);\n const position = COMMERCE_DAYS.indexOf(entry.day);\n if (position < previous) {\n ctx.addIssue({\n code: 'custom',\n path: [index, 'day'],\n message: 'hours must be ordered mon → sun',\n });\n }\n previous = position;\n });\n });\n\nconst makeCommerceSchema = (o: ObjectFactory) =>\n o({\n provider: z.enum(['none', 'other']),\n /** Where a customer is sent to order. Absent when `provider` is `none`. */\n orderingUrl: z.url({ protocol: /^https$/ }).max(2048).optional(),\n /** PUBLIC provider identifiers — never a key, token or secret. */\n locationRef: z.string().min(1).max(128).optional(),\n menuRef: z.string().min(1).max(128).optional(),\n /** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */\n currency: z.string().regex(CURRENCY_PATTERN),\n acceptsOnlineOrders: z.boolean(),\n /** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */\n prepTimeMinutes: z.number().int().min(0).max(240).optional(),\n hours: makeCommerceHoursSchema(o),\n });\n\nexport const commerceSchemaStrict = makeCommerceSchema(strictFactory);\nexport const commerceSchema = makeCommerceSchema(tolerantFactory);\n\n// --- content file ----------------------------------------------------------\n\nconst makeContentFileSchema = (o: ObjectFactory) =>\n o({\n schemaVersion: z.literal(1),\n site: o({ id: z.uuid(), slug: z.string().min(1) }),\n publishedAt: z.iso.datetime(),\n // The three v1 modules are REQUIRED; empty states are valid module payloads\n // (e.g. menu.categories: []), never missing keys. A new required module is\n // breaking (retained/rollback snapshots won't contain it) — bump\n // SCHEMA_VERSION instead.\n //\n // `commerce` is therefore OPTIONAL (phase-7 D-3), and that is not a\n // stylistic choice: every snapshot published before the module existed\n // lacks the key, and a ROLLBACK can activate one of those at any time. A\n // required `commerce` would retroactively invalidate the whole publish\n // history. See ARCHITECTURE.md § 5 and docs/seams/commerce.md § 1.\n modules: o({\n siteSettings: makeSiteSettingsSchema(o),\n branding: makeBrandingSchema(o),\n menu: makeMenuSchema(o),\n commerce: makeCommerceSchema(o).optional(),\n }),\n });\n\n/** Producer gate: the dashboard validates its own output against this. */\nexport const contentFileSchemaStrict = makeContentFileSchema(strictFactory);\n/** Public consumer schema: tolerant of additive v1 changes. */\nexport const contentFileSchema = makeContentFileSchema(tolerantFactory);\n\nexport type SiteSettings = z.infer<typeof siteSettingsSchemaStrict>;\nexport type Branding = z.infer<typeof brandingSchemaStrict>;\nexport type Menu = z.infer<typeof menuSchemaStrict>;\nexport type MenuCategory = Menu['categories'][number];\nexport type MenuItem = MenuCategory['items'][number];\nexport type Commerce = z.infer<typeof commerceSchemaStrict>;\nexport type CommerceDay = (typeof COMMERCE_DAYS)[number];\nexport type CommerceHours = Commerce['hours'];\nexport type ContentFile = z.infer<typeof contentFileSchemaStrict>;\n\n// ---------------------------------------------------------------------------\n// Canonical serialization (phase-2.md T1.6).\n//\n// Recursively key-sorted JSON with no whitespace, produced by a hand-rolled\n// serializer rather than JSON.stringify over a re-built object — rebuilding\n// depends on JS insertion-order semantics and silently reorders integer-like\n// keys. Determinism here is what makes `hashContent` a byte-equality contract\n// shared by the publisher and every consumer test.\n//\n// Rules (documented in the README):\n// - arrays keep their order; object keys are sorted with Array.prototype.sort\n// (code-unit order);\n// - object properties whose value is `undefined` are omitted (JSON semantics);\n// - `undefined` anywhere else, non-finite numbers, bigint, functions and\n// symbols THROW — never silently coerced the way JSON.stringify would.\n// ---------------------------------------------------------------------------\n\nexport function canonicalize(payload: unknown): string {\n const out = serialize(payload);\n if (out === undefined) {\n throw new TypeError('canonicalize: payload itself serializes to nothing (undefined)');\n }\n return out;\n}\n\nfunction serialize(value: unknown): string | undefined {\n if (value === undefined) return undefined;\n if (value === null) return 'null';\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return JSON.stringify(value);\n case 'number':\n if (!Number.isFinite(value)) {\n throw new TypeError(`canonicalize: non-finite number ${String(value)}`);\n }\n return JSON.stringify(value);\n case 'object':\n break;\n default:\n throw new TypeError(`canonicalize: unsupported value of type ${typeof value}`);\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => {\n const s = serialize(entry);\n if (s === undefined) {\n throw new TypeError('canonicalize: undefined array element');\n }\n return s;\n });\n return `[${parts.join(',')}]`;\n }\n const record = value as Record<string, unknown>;\n const keys = Object.keys(record).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const s = serialize(record[key]);\n if (s === undefined) continue; // undefined property -> omitted key\n parts.push(`${JSON.stringify(key)}:${s}`);\n }\n return `{${parts.join(',')}}`;\n}\n"],"mappings":"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","names":["parts"]}
|
package/dist/hash.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,22 @@ declare const CURRENCY_PATTERN: RegExp;
|
|
|
19
19
|
/** ISO 3166-1 alpha-2 country code shape. */
|
|
20
20
|
declare const COUNTRY_PATTERN_SOURCE = "^[A-Z]{2}$";
|
|
21
21
|
declare const COUNTRY_PATTERN: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* Days of the week, in the canonical order `commerce.hours` must be sorted by.
|
|
24
|
+
* The order is part of the contract: a consumer rendering opening hours reads
|
|
25
|
+
* the array straight through, so a payload listing Sunday before Monday would
|
|
26
|
+
* render wrong without being malformed.
|
|
27
|
+
*/
|
|
28
|
+
declare const COMMERCE_DAYS: readonly ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
29
|
+
/**
|
|
30
|
+
* 24-hour `HH:MM`. UNLIKE the four patterns above this one is deliberately NOT
|
|
31
|
+
* mirrored by a DB CHECK: `commerce_settings.hours` is validated at the DB only
|
|
32
|
+
* as "an array of at most 7" (see the migration header), and its element shape,
|
|
33
|
+
* time syntax, day uniqueness and ordering are publish-gated here instead. A
|
|
34
|
+
* schema-invalid draft may therefore exist in the table; it cannot be published.
|
|
35
|
+
*/
|
|
36
|
+
declare const TIME_24H_PATTERN_SOURCE = "^([01][0-9]|2[0-3]):[0-5][0-9]$";
|
|
37
|
+
declare const TIME_24H_PATTERN: RegExp;
|
|
22
38
|
/** IANA time zone check via the runtime's own tz database. */
|
|
23
39
|
declare function isIanaTimeZone(tz: string): boolean;
|
|
24
40
|
declare const siteSettingsSchemaStrict: z.ZodObject<{
|
|
@@ -153,6 +169,64 @@ declare const menuSchema: z.ZodObject<{
|
|
|
153
169
|
}, z.core.$strip>>;
|
|
154
170
|
}, z.core.$strip>>;
|
|
155
171
|
}, z.core.$strip>;
|
|
172
|
+
declare const commerceSchemaStrict: z.ZodObject<{
|
|
173
|
+
provider: z.ZodEnum<{
|
|
174
|
+
none: "none";
|
|
175
|
+
other: "other";
|
|
176
|
+
}>;
|
|
177
|
+
/** Where a customer is sent to order. Absent when `provider` is `none`. */
|
|
178
|
+
orderingUrl: z.ZodOptional<z.ZodURL>;
|
|
179
|
+
/** PUBLIC provider identifiers — never a key, token or secret. */
|
|
180
|
+
locationRef: z.ZodOptional<z.ZodString>;
|
|
181
|
+
menuRef: z.ZodOptional<z.ZodString>;
|
|
182
|
+
/** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */
|
|
183
|
+
currency: z.ZodString;
|
|
184
|
+
acceptsOnlineOrders: z.ZodBoolean;
|
|
185
|
+
/** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */
|
|
186
|
+
prepTimeMinutes: z.ZodOptional<z.ZodNumber>;
|
|
187
|
+
hours: z.ZodArray<z.ZodObject<{
|
|
188
|
+
day: z.ZodEnum<{
|
|
189
|
+
mon: "mon";
|
|
190
|
+
tue: "tue";
|
|
191
|
+
wed: "wed";
|
|
192
|
+
thu: "thu";
|
|
193
|
+
fri: "fri";
|
|
194
|
+
sat: "sat";
|
|
195
|
+
sun: "sun";
|
|
196
|
+
}>;
|
|
197
|
+
opens: z.ZodString;
|
|
198
|
+
closes: z.ZodString;
|
|
199
|
+
}, z.core.$strip>>;
|
|
200
|
+
}, z.core.$strip>;
|
|
201
|
+
declare const commerceSchema: z.ZodObject<{
|
|
202
|
+
provider: z.ZodEnum<{
|
|
203
|
+
none: "none";
|
|
204
|
+
other: "other";
|
|
205
|
+
}>;
|
|
206
|
+
/** Where a customer is sent to order. Absent when `provider` is `none`. */
|
|
207
|
+
orderingUrl: z.ZodOptional<z.ZodURL>;
|
|
208
|
+
/** PUBLIC provider identifiers — never a key, token or secret. */
|
|
209
|
+
locationRef: z.ZodOptional<z.ZodString>;
|
|
210
|
+
menuRef: z.ZodOptional<z.ZodString>;
|
|
211
|
+
/** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */
|
|
212
|
+
currency: z.ZodString;
|
|
213
|
+
acceptsOnlineOrders: z.ZodBoolean;
|
|
214
|
+
/** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */
|
|
215
|
+
prepTimeMinutes: z.ZodOptional<z.ZodNumber>;
|
|
216
|
+
hours: z.ZodArray<z.ZodObject<{
|
|
217
|
+
day: z.ZodEnum<{
|
|
218
|
+
mon: "mon";
|
|
219
|
+
tue: "tue";
|
|
220
|
+
wed: "wed";
|
|
221
|
+
thu: "thu";
|
|
222
|
+
fri: "fri";
|
|
223
|
+
sat: "sat";
|
|
224
|
+
sun: "sun";
|
|
225
|
+
}>;
|
|
226
|
+
opens: z.ZodString;
|
|
227
|
+
closes: z.ZodString;
|
|
228
|
+
}, z.core.$strip>>;
|
|
229
|
+
}, z.core.$strip>;
|
|
156
230
|
/** Producer gate: the dashboard validates its own output against this. */
|
|
157
231
|
declare const contentFileSchemaStrict: z.ZodObject<{
|
|
158
232
|
schemaVersion: z.ZodLiteral<1>;
|
|
@@ -228,6 +302,35 @@ declare const contentFileSchemaStrict: z.ZodObject<{
|
|
|
228
302
|
}, z.core.$strip>>;
|
|
229
303
|
}, z.core.$strip>>;
|
|
230
304
|
}, z.core.$strip>;
|
|
305
|
+
commerce: z.ZodOptional<z.ZodObject<{
|
|
306
|
+
provider: z.ZodEnum<{
|
|
307
|
+
none: "none";
|
|
308
|
+
other: "other";
|
|
309
|
+
}>;
|
|
310
|
+
/** Where a customer is sent to order. Absent when `provider` is `none`. */
|
|
311
|
+
orderingUrl: z.ZodOptional<z.ZodURL>;
|
|
312
|
+
/** PUBLIC provider identifiers — never a key, token or secret. */
|
|
313
|
+
locationRef: z.ZodOptional<z.ZodString>;
|
|
314
|
+
menuRef: z.ZodOptional<z.ZodString>;
|
|
315
|
+
/** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */
|
|
316
|
+
currency: z.ZodString;
|
|
317
|
+
acceptsOnlineOrders: z.ZodBoolean;
|
|
318
|
+
/** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */
|
|
319
|
+
prepTimeMinutes: z.ZodOptional<z.ZodNumber>;
|
|
320
|
+
hours: z.ZodArray<z.ZodObject<{
|
|
321
|
+
day: z.ZodEnum<{
|
|
322
|
+
mon: "mon";
|
|
323
|
+
tue: "tue";
|
|
324
|
+
wed: "wed";
|
|
325
|
+
thu: "thu";
|
|
326
|
+
fri: "fri";
|
|
327
|
+
sat: "sat";
|
|
328
|
+
sun: "sun";
|
|
329
|
+
}>;
|
|
330
|
+
opens: z.ZodString;
|
|
331
|
+
closes: z.ZodString;
|
|
332
|
+
}, z.core.$strip>>;
|
|
333
|
+
}, z.core.$strip>>;
|
|
231
334
|
}, z.core.$strip>;
|
|
232
335
|
}, z.core.$strip>;
|
|
233
336
|
/** Public consumer schema: tolerant of additive v1 changes. */
|
|
@@ -305,6 +408,35 @@ declare const contentFileSchema: z.ZodObject<{
|
|
|
305
408
|
}, z.core.$strip>>;
|
|
306
409
|
}, z.core.$strip>>;
|
|
307
410
|
}, z.core.$strip>;
|
|
411
|
+
commerce: z.ZodOptional<z.ZodObject<{
|
|
412
|
+
provider: z.ZodEnum<{
|
|
413
|
+
none: "none";
|
|
414
|
+
other: "other";
|
|
415
|
+
}>;
|
|
416
|
+
/** Where a customer is sent to order. Absent when `provider` is `none`. */
|
|
417
|
+
orderingUrl: z.ZodOptional<z.ZodURL>;
|
|
418
|
+
/** PUBLIC provider identifiers — never a key, token or secret. */
|
|
419
|
+
locationRef: z.ZodOptional<z.ZodString>;
|
|
420
|
+
menuRef: z.ZodOptional<z.ZodString>;
|
|
421
|
+
/** Matches `menu` item prices; the DB CHECK mirrors CURRENCY_PATTERN_SOURCE. */
|
|
422
|
+
currency: z.ZodString;
|
|
423
|
+
acceptsOnlineOrders: z.ZodBoolean;
|
|
424
|
+
/** Quoted preparation time. Meaningless — and normally absent — when ordering is off. */
|
|
425
|
+
prepTimeMinutes: z.ZodOptional<z.ZodNumber>;
|
|
426
|
+
hours: z.ZodArray<z.ZodObject<{
|
|
427
|
+
day: z.ZodEnum<{
|
|
428
|
+
mon: "mon";
|
|
429
|
+
tue: "tue";
|
|
430
|
+
wed: "wed";
|
|
431
|
+
thu: "thu";
|
|
432
|
+
fri: "fri";
|
|
433
|
+
sat: "sat";
|
|
434
|
+
sun: "sun";
|
|
435
|
+
}>;
|
|
436
|
+
opens: z.ZodString;
|
|
437
|
+
closes: z.ZodString;
|
|
438
|
+
}, z.core.$strip>>;
|
|
439
|
+
}, z.core.$strip>>;
|
|
308
440
|
}, z.core.$strip>;
|
|
309
441
|
}, z.core.$strip>;
|
|
310
442
|
type SiteSettings = z.infer<typeof siteSettingsSchemaStrict>;
|
|
@@ -312,7 +444,10 @@ type Branding = z.infer<typeof brandingSchemaStrict>;
|
|
|
312
444
|
type Menu = z.infer<typeof menuSchemaStrict>;
|
|
313
445
|
type MenuCategory = Menu['categories'][number];
|
|
314
446
|
type MenuItem = MenuCategory['items'][number];
|
|
447
|
+
type Commerce = z.infer<typeof commerceSchemaStrict>;
|
|
448
|
+
type CommerceDay = (typeof COMMERCE_DAYS)[number];
|
|
449
|
+
type CommerceHours = Commerce['hours'];
|
|
315
450
|
type ContentFile = z.infer<typeof contentFileSchemaStrict>;
|
|
316
451
|
declare function canonicalize(payload: unknown): string;
|
|
317
452
|
|
|
318
|
-
export { type Branding, COUNTRY_PATTERN, COUNTRY_PATTERN_SOURCE, CURRENCY_PATTERN, CURRENCY_PATTERN_SOURCE, type ContentFile, HEX_COLOR_PATTERN, HEX_COLOR_PATTERN_SOURCE, LOCALE_PATTERN, LOCALE_PATTERN_SOURCE, MAX_CONTENT_BYTES, type Menu, type MenuCategory, type MenuItem, SCHEMA_VERSION, type SiteSettings, brandingSchema, brandingSchemaStrict, canonicalize, contentFileSchema, contentFileSchemaStrict, isIanaTimeZone, menuSchema, menuSchemaStrict, siteSettingsSchema, siteSettingsSchemaStrict };
|
|
453
|
+
export { type Branding, COMMERCE_DAYS, COUNTRY_PATTERN, COUNTRY_PATTERN_SOURCE, CURRENCY_PATTERN, CURRENCY_PATTERN_SOURCE, type Commerce, type CommerceDay, type CommerceHours, type ContentFile, HEX_COLOR_PATTERN, HEX_COLOR_PATTERN_SOURCE, LOCALE_PATTERN, LOCALE_PATTERN_SOURCE, MAX_CONTENT_BYTES, type Menu, type MenuCategory, type MenuItem, SCHEMA_VERSION, type SiteSettings, TIME_24H_PATTERN, TIME_24H_PATTERN_SOURCE, brandingSchema, brandingSchemaStrict, canonicalize, commerceSchema, commerceSchemaStrict, contentFileSchema, contentFileSchemaStrict, isIanaTimeZone, menuSchema, menuSchemaStrict, siteSettingsSchema, siteSettingsSchemaStrict };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
COMMERCE_DAYS,
|
|
2
3
|
COUNTRY_PATTERN,
|
|
3
4
|
COUNTRY_PATTERN_SOURCE,
|
|
4
5
|
CURRENCY_PATTERN,
|
|
@@ -9,9 +10,13 @@ import {
|
|
|
9
10
|
LOCALE_PATTERN_SOURCE,
|
|
10
11
|
MAX_CONTENT_BYTES,
|
|
11
12
|
SCHEMA_VERSION,
|
|
13
|
+
TIME_24H_PATTERN,
|
|
14
|
+
TIME_24H_PATTERN_SOURCE,
|
|
12
15
|
brandingSchema,
|
|
13
16
|
brandingSchemaStrict,
|
|
14
17
|
canonicalize,
|
|
18
|
+
commerceSchema,
|
|
19
|
+
commerceSchemaStrict,
|
|
15
20
|
contentFileSchema,
|
|
16
21
|
contentFileSchemaStrict,
|
|
17
22
|
isIanaTimeZone,
|
|
@@ -19,8 +24,9 @@ import {
|
|
|
19
24
|
menuSchemaStrict,
|
|
20
25
|
siteSettingsSchema,
|
|
21
26
|
siteSettingsSchemaStrict
|
|
22
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GUUCVLL3.js";
|
|
23
28
|
export {
|
|
29
|
+
COMMERCE_DAYS,
|
|
24
30
|
COUNTRY_PATTERN,
|
|
25
31
|
COUNTRY_PATTERN_SOURCE,
|
|
26
32
|
CURRENCY_PATTERN,
|
|
@@ -31,9 +37,13 @@ export {
|
|
|
31
37
|
LOCALE_PATTERN_SOURCE,
|
|
32
38
|
MAX_CONTENT_BYTES,
|
|
33
39
|
SCHEMA_VERSION,
|
|
40
|
+
TIME_24H_PATTERN,
|
|
41
|
+
TIME_24H_PATTERN_SOURCE,
|
|
34
42
|
brandingSchema,
|
|
35
43
|
brandingSchemaStrict,
|
|
36
44
|
canonicalize,
|
|
45
|
+
commerceSchema,
|
|
46
|
+
commerceSchemaStrict,
|
|
37
47
|
contentFileSchema,
|
|
38
48
|
contentFileSchemaStrict,
|
|
39
49
|
isIanaTimeZone,
|
package/fixtures/v1/full.json
CHANGED
|
@@ -126,6 +126,23 @@
|
|
|
126
126
|
]
|
|
127
127
|
}
|
|
128
128
|
]
|
|
129
|
+
},
|
|
130
|
+
"commerce": {
|
|
131
|
+
"provider": "other",
|
|
132
|
+
"orderingUrl": "https://order.example.com/pho-palace",
|
|
133
|
+
"locationRef": "loc-charlotte-tryon",
|
|
134
|
+
"menuRef": "menu-dinner-2026",
|
|
135
|
+
"currency": "USD",
|
|
136
|
+
"acceptsOnlineOrders": true,
|
|
137
|
+
"prepTimeMinutes": 25,
|
|
138
|
+
"hours": [
|
|
139
|
+
{ "day": "mon", "opens": "11:00", "closes": "21:00" },
|
|
140
|
+
{ "day": "tue", "opens": "11:00", "closes": "21:00" },
|
|
141
|
+
{ "day": "wed", "opens": "11:00", "closes": "21:00" },
|
|
142
|
+
{ "day": "thu", "opens": "11:00", "closes": "21:00" },
|
|
143
|
+
{ "day": "fri", "opens": "11:00", "closes": "01:30" },
|
|
144
|
+
{ "day": "sat", "opens": "10:00", "closes": "01:30" }
|
|
145
|
+
]
|
|
129
146
|
}
|
|
130
147
|
}
|
|
131
148
|
}
|
package/fixtures/v1/hashes.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Checked-in hashContent() snapshots of the v1 fixtures — the serialization-determinism contract. If a test fails against these, the canonical serialization or a fixture changed; BOTH are locked decisions (phase-2.md T1.6/T1.8). Regenerate only with founder sign-off.",
|
|
3
|
-
"full.json": "sha256:
|
|
2
|
+
"_comment": "Checked-in hashContent() snapshots of the v1 fixtures — the serialization-determinism contract. If a test fails against these, the canonical serialization or a fixture changed; BOTH are locked decisions (phase-2.md T1.6/T1.8). Regenerate only with founder sign-off. REGENERATED ONCE, 2026-08-25: full.json gained the optional `commerce` module (phase-7 Slice 2), signed off in the proposal's Slice 2 line — 'full.json fixture ... publish fixtures and hash updates'. minimal.json is UNCHANGED and must stay so: commerce is optional, so the minimal payload never carries it.",
|
|
3
|
+
"full.json": "sha256:30bd220fda88f5dd511cd0c05fe888e9abad4f5e5946b398aade99ba8d3c8a08",
|
|
4
4
|
"minimal.json": "sha256:7b4ebc88bf2c17e1182a7e89fe994c3a38f70e0e638b37a0caa662f0c7e8db22"
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lamgig/content-schemas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "The LamGig published-JSON contract: Zod schemas, canonical serialization, and fixtures for consuming content published by the LamGig platform.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lamgig",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Version of the published-JSON contract. Additive changes (new OPTIONAL fields\n * and new OPTIONAL module keys) keep this number; any required addition is\n * breaking and bumps it. The full compatibility rule lives in this package's\n * README. See PLAN.md § Published JSON delivery and phase-2.md T1.5.\n */\nexport const SCHEMA_VERSION = 1;\n\n/** Publisher-enforced ceiling on the canonical payload size (1 MiB). */\nexport const MAX_CONTENT_BYTES = 1_048_576;\n\n// ---------------------------------------------------------------------------\n// Shared value patterns — single source of truth.\n//\n// LOCALE_PATTERN_SOURCE is the EXACT string used by the `site_settings.locale`\n// DB CHECK (supabase/migrations/*_content_site_settings.sql). Contract and\n// database must never disagree (phase-2.md T1.1): v1 supports the named BCP-47\n// subset `ll` or `ll-CC` only (e.g. `en`, `en-US`); script/numeric-region forms\n// (`zh-Hant`, `es-419`) are out of scope — widening later is additive.\n// HEX_COLOR / CURRENCY are likewise mirrored by DB CHECKs on `branding.colors`\n// and `menu_items.price_currency`.\n// ---------------------------------------------------------------------------\n\nexport const LOCALE_PATTERN_SOURCE = '^[a-z]{2}(-[A-Z]{2})?$';\nexport const LOCALE_PATTERN = new RegExp(LOCALE_PATTERN_SOURCE);\n\nexport const HEX_COLOR_PATTERN_SOURCE = '^#[0-9A-Fa-f]{6}$';\nexport const HEX_COLOR_PATTERN = new RegExp(HEX_COLOR_PATTERN_SOURCE);\n\n/** ISO 4217 alpha code shape (mirrored by the `price_currency` DB CHECK). */\nexport const CURRENCY_PATTERN_SOURCE = '^[A-Z]{3}$';\nexport const CURRENCY_PATTERN = new RegExp(CURRENCY_PATTERN_SOURCE);\n\n/** ISO 3166-1 alpha-2 country code shape. */\nexport const COUNTRY_PATTERN_SOURCE = '^[A-Z]{2}$';\nexport const COUNTRY_PATTERN = new RegExp(COUNTRY_PATTERN_SOURCE);\n\n/** IANA time zone check via the runtime's own tz database. */\nexport function isIanaTimeZone(tz: string): boolean {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Two parse modes from ONE schema definition (phase-2.md T1):\n//\n// - producer-strict — `.strict()` everywhere; the dashboard validates its own\n// output against this before publishing. Unknown keys are defects.\n// - consumer-tolerant — unknown object keys are STRIPPED and unknown module\n// keys ignored, so a shipped consumer accepts a v1 payload that later gained\n// an optional field or a new module key (the additive changes the\n// compatibility rule promises).\n//\n// zod v4's `.strip()`/`.passthrough()` do NOT recurse into nested objects, so a\n// post-hoc transform of one tree silently leaves nested objects in the original\n// mode. Instead every object in the tree is built through an injected object\n// factory: `z.strictObject` for the strict pair, `z.object` (strips unknown\n// keys) for the tolerant pair. One definition, two instantiations.\n// ---------------------------------------------------------------------------\n\ntype ObjectFactory = <T extends z.ZodRawShape>(shape: T) => z.ZodObject<T>;\n\nconst strictFactory = z.strictObject as ObjectFactory;\nconst tolerantFactory = z.object as ObjectFactory;\n\n// --- siteSettings ----------------------------------------------------------\n\nconst makeSiteSettingsSchema = (o: ObjectFactory) =>\n o({\n siteName: z.string().min(1).max(120),\n tagline: z.string().max(200).optional(),\n contact: o({\n email: z.email().optional(),\n phone: z.string().min(1).optional(),\n address: o({\n line1: z.string().min(1),\n line2: z.string().min(1).optional(),\n city: z.string().min(1),\n region: z.string().min(1),\n postalCode: z.string().min(1),\n country: z.string().regex(COUNTRY_PATTERN),\n }).optional(),\n }),\n // Explicit known keys (NOT a record): strict rejects unknown platforms,\n // tolerant strips them — adding a platform later is additive.\n socialLinks: o({\n facebook: z.url().optional(),\n instagram: z.url().optional(),\n tiktok: z.url().optional(),\n x: z.url().optional(),\n youtube: z.url().optional(),\n yelp: z.url().optional(),\n google: z.url().optional(),\n }).optional(),\n locale: z.string().regex(LOCALE_PATTERN).default('en-US'),\n timezone: z\n .string()\n .min(1)\n .refine(isIanaTimeZone, 'must be an IANA time zone name')\n .default('America/Los_Angeles'),\n });\n\nexport const siteSettingsSchemaStrict = makeSiteSettingsSchema(strictFactory);\nexport const siteSettingsSchema = makeSiteSettingsSchema(tolerantFactory);\n\n// --- branding --------------------------------------------------------------\n\nconst makeBrandingSchema = (o: ObjectFactory) =>\n o({\n // Absolute URLs produced by the dashboard's storagePublicUrl builder\n // (apps/dashboard/src/lib/content-urls.ts). The DB stores storage PATHS;\n // the publish-time compose maps path -> URL. This package never imports\n // from the app (dependency direction, phase-2.md T1.7).\n logoUrl: z.url().optional(),\n faviconUrl: z.url().optional(),\n colors: o({\n primary: z.string().regex(HEX_COLOR_PATTERN),\n secondary: z.string().regex(HEX_COLOR_PATTERN).optional(),\n accent: z.string().regex(HEX_COLOR_PATTERN).optional(),\n background: z.string().regex(HEX_COLOR_PATTERN).optional(),\n text: z.string().regex(HEX_COLOR_PATTERN).optional(),\n }),\n fonts: o({\n heading: z.string().min(1).optional(),\n body: z.string().min(1).optional(),\n }).optional(),\n });\n\nexport const brandingSchemaStrict = makeBrandingSchema(strictFactory);\nexport const brandingSchema = makeBrandingSchema(tolerantFactory);\n\n// --- menu ------------------------------------------------------------------\n\nconst makeMenuItemSchema = (o: ObjectFactory) =>\n o({\n /** Stable DB UUID — identical across publishes (phase-2.md T1.3). */\n id: z.uuid(),\n name: z.string().min(1).max(200),\n description: z.string().max(2000).optional(),\n price: o({\n /** Integer minor units (e.g. cents). */\n amount: z.number().int().min(0),\n currency: z.string().regex(CURRENCY_PATTERN),\n }),\n imageUrl: z.url().optional(),\n tags: z.array(z.string().min(1)).max(20),\n sortOrder: z.number().int().min(0).max(100_000),\n isAvailable: z.boolean(),\n });\n\nconst makeMenuCategorySchema = (o: ObjectFactory) =>\n o({\n id: z.uuid(),\n name: z.string().min(1).max(200),\n description: z.string().max(2000).optional(),\n sortOrder: z.number().int().min(0).max(100_000),\n items: z.array(makeMenuItemSchema(o)),\n });\n\nconst makeMenuSchema = (o: ObjectFactory) =>\n o({\n /** Archived/deleted entities are simply ABSENT; empty menu = `[]`, never a missing key. */\n categories: z.array(makeMenuCategorySchema(o)),\n });\n\nexport const menuSchemaStrict = makeMenuSchema(strictFactory);\nexport const menuSchema = makeMenuSchema(tolerantFactory);\n\n// --- content file ----------------------------------------------------------\n\nconst makeContentFileSchema = (o: ObjectFactory) =>\n o({\n schemaVersion: z.literal(1),\n site: o({ id: z.uuid(), slug: z.string().min(1) }),\n publishedAt: z.iso.datetime(),\n // All three modules REQUIRED; empty states are valid module payloads\n // (e.g. menu.categories: []), never missing keys. A new required module is\n // breaking (retained/rollback snapshots won't contain it) — bump\n // SCHEMA_VERSION instead.\n modules: o({\n siteSettings: makeSiteSettingsSchema(o),\n branding: makeBrandingSchema(o),\n menu: makeMenuSchema(o),\n }),\n });\n\n/** Producer gate: the dashboard validates its own output against this. */\nexport const contentFileSchemaStrict = makeContentFileSchema(strictFactory);\n/** Public consumer schema: tolerant of additive v1 changes. */\nexport const contentFileSchema = makeContentFileSchema(tolerantFactory);\n\nexport type SiteSettings = z.infer<typeof siteSettingsSchemaStrict>;\nexport type Branding = z.infer<typeof brandingSchemaStrict>;\nexport type Menu = z.infer<typeof menuSchemaStrict>;\nexport type MenuCategory = Menu['categories'][number];\nexport type MenuItem = MenuCategory['items'][number];\nexport type ContentFile = z.infer<typeof contentFileSchemaStrict>;\n\n// ---------------------------------------------------------------------------\n// Canonical serialization (phase-2.md T1.6).\n//\n// Recursively key-sorted JSON with no whitespace, produced by a hand-rolled\n// serializer rather than JSON.stringify over a re-built object — rebuilding\n// depends on JS insertion-order semantics and silently reorders integer-like\n// keys. Determinism here is what makes `hashContent` a byte-equality contract\n// shared by the publisher and every consumer test.\n//\n// Rules (documented in the README):\n// - arrays keep their order; object keys are sorted with Array.prototype.sort\n// (code-unit order);\n// - object properties whose value is `undefined` are omitted (JSON semantics);\n// - `undefined` anywhere else, non-finite numbers, bigint, functions and\n// symbols THROW — never silently coerced the way JSON.stringify would.\n// ---------------------------------------------------------------------------\n\nexport function canonicalize(payload: unknown): string {\n const out = serialize(payload);\n if (out === undefined) {\n throw new TypeError('canonicalize: payload itself serializes to nothing (undefined)');\n }\n return out;\n}\n\nfunction serialize(value: unknown): string | undefined {\n if (value === undefined) return undefined;\n if (value === null) return 'null';\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return JSON.stringify(value);\n case 'number':\n if (!Number.isFinite(value)) {\n throw new TypeError(`canonicalize: non-finite number ${String(value)}`);\n }\n return JSON.stringify(value);\n case 'object':\n break;\n default:\n throw new TypeError(`canonicalize: unsupported value of type ${typeof value}`);\n }\n if (Array.isArray(value)) {\n const parts = value.map((entry) => {\n const s = serialize(entry);\n if (s === undefined) {\n throw new TypeError('canonicalize: undefined array element');\n }\n return s;\n });\n return `[${parts.join(',')}]`;\n }\n const record = value as Record<string, unknown>;\n const keys = Object.keys(record).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const s = serialize(record[key]);\n if (s === undefined) continue; // undefined property -> omitted key\n parts.push(`${JSON.stringify(key)}:${s}`);\n }\n return `{${parts.join(',')}}`;\n}\n"],"mappings":"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","names":["parts"]}
|