@aglyn/plugins-ai 1.0.0-beta.239 → 1.0.0-beta.241

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.
Files changed (99) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-job-links.d.ts +17 -0
  3. package/src/lib/components/ai-job-links.js +63 -0
  4. package/src/lib/components/ai-job-links.js.map +1 -1
  5. package/src/lib/components/ai-job-plan.component.js +8 -1
  6. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  7. package/src/lib/components/ai-job-requests.d.ts +5 -0
  8. package/src/lib/components/ai-job-requests.js +4 -2
  9. package/src/lib/components/ai-job-requests.js.map +1 -1
  10. package/src/lib/components/ai-site-build-page.component.js +7 -1
  11. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  12. package/src/lib/components/ai-store-finish-card.component.d.ts +28 -0
  13. package/src/lib/components/ai-store-finish-card.component.js +116 -0
  14. package/src/lib/components/ai-store-finish-card.component.js.map +1 -0
  15. package/src/lib/components/assist-build-card.component.js +12 -1
  16. package/src/lib/components/assist-build-card.component.js.map +1 -1
  17. package/src/lib/components/assist-panel.component.js +14 -2
  18. package/src/lib/components/assist-panel.component.js.map +1 -1
  19. package/src/lib/jobs/ai-job-build-step.d.ts +45 -1
  20. package/src/lib/jobs/ai-job-build-step.js +283 -7
  21. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  22. package/src/lib/jobs/ai-job-drafts.d.ts +9 -1
  23. package/src/lib/jobs/ai-job-drafts.js +4 -1
  24. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  25. package/src/lib/jobs/ai-job-layout-language.d.ts +3 -0
  26. package/src/lib/jobs/ai-job-layout-language.js +11 -4
  27. package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
  28. package/src/lib/jobs/ai-job-layout-step.js +3 -1
  29. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  30. package/src/lib/jobs/ai-job-page-language.d.ts +2 -1
  31. package/src/lib/jobs/ai-job-page-language.js +26 -9
  32. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  33. package/src/lib/jobs/ai-job-page-sections.d.ts +11 -0
  34. package/src/lib/jobs/ai-job-page-sections.js +53 -5
  35. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  36. package/src/lib/jobs/ai-job-page-step.d.ts +2 -1
  37. package/src/lib/jobs/ai-job-page-step.js +51 -10
  38. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  39. package/src/lib/jobs/ai-job-plan-step.d.ts +12 -0
  40. package/src/lib/jobs/ai-job-plan-step.js +41 -7
  41. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  42. package/src/lib/jobs/ai-job-site-datasets.d.ts +89 -2
  43. package/src/lib/jobs/ai-job-site-datasets.js +239 -15
  44. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  45. package/src/lib/jobs/ai-job-site-step.d.ts +18 -2
  46. package/src/lib/jobs/ai-job-site-step.js +109 -22
  47. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  48. package/src/lib/jobs/ai-job-site-store-pages.d.ts +75 -0
  49. package/src/lib/jobs/ai-job-site-store-pages.js +195 -0
  50. package/src/lib/jobs/ai-job-site-store-pages.js.map +1 -0
  51. package/src/lib/jobs/ai-job-store-links-layout.d.ts +71 -0
  52. package/src/lib/jobs/ai-job-store-links-layout.js +95 -0
  53. package/src/lib/jobs/ai-job-store-links-layout.js.map +1 -0
  54. package/src/lib/jobs/ai-layout-stock-photos.d.ts +69 -3
  55. package/src/lib/jobs/ai-layout-stock-photos.js +603 -42
  56. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  57. package/src/lib/jobs/ai-site-publish.d.ts +18 -0
  58. package/src/lib/jobs/ai-site-publish.js +76 -15
  59. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  60. package/src/lib/jobs/fixtures/ai-stock-photo-ember-oak-hits.d.ts +43 -0
  61. package/src/lib/jobs/fixtures/ai-stock-photo-ember-oak-hits.js +61 -0
  62. package/src/lib/jobs/fixtures/ai-stock-photo-ember-oak-hits.js.map +1 -0
  63. package/src/lib/layout-language/ai-layout-compiler.js +25 -2
  64. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
  65. package/src/lib/layout-language/ai-layout-frame.d.ts +10 -0
  66. package/src/lib/layout-language/ai-layout-frame.js +58 -4
  67. package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
  68. package/src/lib/layout-language/ai-layout-listings.d.ts +6 -0
  69. package/src/lib/layout-language/ai-layout-listings.js +3 -0
  70. package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
  71. package/src/lib/layout-language/ai-layout-pictures.js +12 -4
  72. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  73. package/src/lib/model/ai-build-job.d.ts +2 -0
  74. package/src/lib/model/ai-build-job.js +23 -3
  75. package/src/lib/model/ai-build-job.js.map +1 -1
  76. package/src/lib/model/ai-build-plan.d.ts +7 -0
  77. package/src/lib/model/ai-build-plan.js.map +1 -1
  78. package/src/lib/model/ai-build-progress.js +3 -1
  79. package/src/lib/model/ai-build-progress.js.map +1 -1
  80. package/src/lib/model/ai-build-store-pages.d.ts +133 -0
  81. package/src/lib/model/ai-build-store-pages.js +209 -0
  82. package/src/lib/model/ai-build-store-pages.js.map +1 -0
  83. package/src/lib/model/ai-layout-store-links.d.ts +64 -0
  84. package/src/lib/model/ai-layout-store-links.js +165 -0
  85. package/src/lib/model/ai-layout-store-links.js.map +1 -0
  86. package/src/lib/model/ai-site-inventory.d.ts +5 -0
  87. package/src/lib/model/ai-site-inventory.js.map +1 -1
  88. package/src/lib/model/ai-site-store-pages.d.ts +192 -0
  89. package/src/lib/model/ai-site-store-pages.js +383 -0
  90. package/src/lib/model/ai-site-store-pages.js.map +1 -0
  91. package/src/lib/runtime/ai-doctrine-validators.d.ts +44 -0
  92. package/src/lib/runtime/ai-doctrine-validators.js +177 -0
  93. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  94. package/src/lib/runtime/ai-doctrine.js +3 -2
  95. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  96. package/src/lib/runtime/site-inventory.js +8 -3
  97. package/src/lib/runtime/site-inventory.js.map +1 -1
  98. package/src/lib/testing/stand-in-dataset-index.js +8 -4
  99. package/src/lib/testing/stand-in-dataset-index.js.map +1 -1
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * A STORE'S OWN PAGES (AGL-3676, third round).
19
+ *
20
+ * Zach, 2026-10-10: "We seem to be missing the account pages for the
21
+ * shop/store AI generation, make sure all they would have to do is setup the
22
+ * payment info and update products to finish up their storefront." The
23
+ * Ember & Oak Candle Co start had Home, Shop, Gift sets, About, Shipping &
24
+ * care and Contact, and a product page per product; a shopper had nowhere to
25
+ * sign in, see an order, find what they saved, or read the store's privacy
26
+ * policy and terms.
27
+ *
28
+ * So a paid store start gets the pages every storefront has, written by code
29
+ * from the platform's own elements — never asked of the model:
30
+ *
31
+ * - ACCOUNT at /account: the commerce plugin's Customer account (sign in,
32
+ * create an account, profile, addresses, order history with status and
33
+ * tracking, sign out) and Saved items (the Wishlist the product page's
34
+ * Save fills, kept for a guest in the browser and on the account once
35
+ * they sign in);
36
+ * - CART at /cart: the Cart in full, with its checkout, for a shopper who
37
+ * goes to the address rather than the header's cart drawer;
38
+ * - SHIPPING & RETURNS at /shipping-returns, PRIVACY POLICY at /privacy and
39
+ * TERMS OF SALE at /terms: editable drafts in the Markdown element, every
40
+ * fact only the merchant knows left as a [bracketed placeholder] — never a
41
+ * return window, a price or a promise the brief did not give.
42
+ *
43
+ * The account and the cart are a shopper's own pages, written UNLISTED
44
+ * (noindex, out of the sitemap); the policies are indexable like every other
45
+ * page.
46
+ *
47
+ * An order's own page is the commerce plugin's built-in /order-status, which
48
+ * every order email links with its signed key; the Account page says so.
49
+ *
50
+ * ── Outside the plan's page count ───────────────────────────────────────
51
+ *
52
+ * They are STORE PAGES, not planned pages: the plan's four to eight pages are
53
+ * the content the model designs, and these are added beside them, so they
54
+ * never crowd one out. They still count toward the plan's pages allowance
55
+ * (`screensPerHost`), as every page a site has does; a store is Starter and
56
+ * up, whose allowance holds both.
57
+ *
58
+ * A page the plan already has stands for its store page instead of a second
59
+ * one: a planned "Shipping & care" is the store's shipping page, and a page
60
+ * the plan put at /privacy is its privacy policy. The header and footer link
61
+ * that page.
62
+ *
63
+ * Pure: names, paths, words and node trees. Safe for the console to import.
64
+ */
65
+ /** A store page, by what it is. Persisted in job outputs (`proposal.storePage`); never renamed. */
66
+ export type AiStorePageKey = 'account' | 'cart' | 'shipping' | 'privacy' | 'terms';
67
+ export interface AiStorePageDefinition {
68
+ key: AiStorePageKey;
69
+ /** The page's name in Pages, and its heading. */
70
+ title: string;
71
+ slug: string;
72
+ /** What the footer's link to it says. */
73
+ link: string;
74
+ seo: {
75
+ title: string;
76
+ description: string;
77
+ };
78
+ /**
79
+ * A shopper's own page, not one to find by search: written UNLISTED, the
80
+ * page's Access setting the console offers, so the tenant serves it
81
+ * `noindex` and leaves it out of the sitemap. Only the account and the
82
+ * cart; the policies, like every content and landing page, stay indexable.
83
+ */
84
+ noindex?: true;
85
+ }
86
+ /** Every store page, in the order the job writes them and the footer links them. */
87
+ export declare const AI_STORE_PAGES: readonly AiStorePageDefinition[];
88
+ /** What the job's row for the store pages reads. */
89
+ export declare const AI_SITE_STORE_PAGES_LABEL = "Adding your account, cart and policy pages";
90
+ /** The note the row carries once they are written: what the merchant fills in. */
91
+ export declare const AI_SITE_STORE_PAGES_NOTE = "Your shipping, privacy and terms pages are drafts: fill in the parts in [brackets] with your store\u2019s own details.";
92
+ /** A store page as a start builds or links it. */
93
+ export interface AiStorePagePlan {
94
+ key: AiStorePageKey;
95
+ title: string;
96
+ slug: string;
97
+ /** Where the header and footer link it. */
98
+ href: string;
99
+ /** What the footer's link says. */
100
+ link: string;
101
+ /**
102
+ * The planned page that stands for it, by its title, where the plan has
103
+ * one: nothing is written for this key, and its links go to that page.
104
+ */
105
+ planned?: string;
106
+ }
107
+ /**
108
+ * The store pages a store plan gets: each one written, or — where a planned
109
+ * page already holds its address, or a planned page's words say it is the
110
+ * store's shipping page — that planned page, linked in its place.
111
+ */
112
+ export declare function aiStorePagesOfPlan(screens: ReadonlyArray<{
113
+ slug: string;
114
+ title: string;
115
+ record?: unknown;
116
+ }>): AiStorePagePlan[];
117
+ /** The store pages a start writes itself: those no planned page stands for. */
118
+ export declare function aiStorePagesToWrite(pages: readonly AiStorePagePlan[]): AiStorePagePlan[];
119
+ /** The unit input a site layout is told the store's links under. */
120
+ export declare const AI_SITE_STORE_LINKS_INPUT = "storeLinks";
121
+ /** What a store's header and footer link (AGL-3676): the account beside the cart, and the store's pages under the site's. */
122
+ export interface AiStoreFrameLinks {
123
+ /** The account page's path, which the header links beside the cart. */
124
+ account: string | null;
125
+ /** The footer's store links, in order: the account, then the policies. */
126
+ footer: Array<{
127
+ label: string;
128
+ href: string;
129
+ }>;
130
+ }
131
+ /** The header's and footer's store links for these store pages. The cart is the header's own button. */
132
+ export declare function aiStoreFrameLinks(pages: readonly AiStorePagePlan[]): AiStoreFrameLinks;
133
+ /** The store links a layout unit's inputs carry; none for any other layout. */
134
+ export declare function aiStoreFrameLinksOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiStoreFrameLinks | null;
135
+ /** The longest one Markdown element is given: the element's own text limit. */
136
+ export declare const AI_STORE_MARKDOWN_MAX = 2000;
137
+ /** The commerce plugin's elements a store page places, by the ids it persists (`ai-site-store-pages.spec.ts` holds them). */
138
+ export declare const AI_STORE_ELEMENTS: {
139
+ readonly account: "customer-account";
140
+ readonly wishlist: "wishlist";
141
+ readonly cart: "cart";
142
+ };
143
+ /** What a store page's words may link: the site's contact page and its shop, where the plan has them. */
144
+ export interface AiStorePageFacts {
145
+ /** The contact page's path, or `null` where the plan has none. */
146
+ contactPath: string | null;
147
+ /** The shop's path, `/shop` where the plan has none. */
148
+ shopPath: string;
149
+ /** The shipping page's path, as the terms link it. */
150
+ shippingPath: string;
151
+ }
152
+ interface StoredNode {
153
+ $id: string;
154
+ type: 'node';
155
+ componentId: string;
156
+ pluginId: string;
157
+ parentId: string | null;
158
+ nodes: string[];
159
+ props?: Record<string, unknown>;
160
+ sx?: Record<string, unknown>;
161
+ }
162
+ /** The policy's sections, as markdown, each a `##` heading and its paragraphs. */
163
+ export declare function aiStorePolicySections(key: 'shipping' | 'privacy' | 'terms', facts: AiStorePageFacts): string[];
164
+ /** Sections joined into as few Markdown elements as fit the element's limit, in order. */
165
+ export declare function aiStoreMarkdownChunks(sections: readonly string[], max?: number): string[];
166
+ /**
167
+ * A store page's node tree, root first, as a page's first version stores it:
168
+ * the page's heading over the platform's elements, in one section the
169
+ * site's layout renders inside.
170
+ */
171
+ export declare function aiStorePageNodes(key: AiStorePageKey, facts: AiStorePageFacts): Record<string, StoredNode>;
172
+ /** One thing left before an AI-built store sells, as its done page lists it. */
173
+ export interface AiStoreFinishStep {
174
+ id: 'payments' | 'products' | 'shipping' | 'policies' | 'links';
175
+ title: string;
176
+ text: string;
177
+ /**
178
+ * The record route kind whose list the step opens (`plugin-record-routes`),
179
+ * `pages` for the site's Pages, `layouts` for its Layouts, or `layout` for
180
+ * the layout version a build drafted its store's links into (AGL-3676).
181
+ */
182
+ opens: 'store-settings' | 'product' | 'pages' | 'layouts' | 'layout';
183
+ action: string;
184
+ }
185
+ /**
186
+ * What is left once an AI store is built (Zach, 2026-10-10): connect
187
+ * payments, review the products and their prices, set shipping and tax, and
188
+ * fill in the policies' brackets. Everything else — the storefront, the
189
+ * account, cart and policy pages, the header's cart and account — is done.
190
+ */
191
+ export declare const AI_STORE_FINISH_STEPS: readonly AiStoreFinishStep[];
192
+ export {};
@@ -0,0 +1,383 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * A STORE'S OWN PAGES (AGL-3676, third round).
18
+ *
19
+ * Zach, 2026-10-10: "We seem to be missing the account pages for the
20
+ * shop/store AI generation, make sure all they would have to do is setup the
21
+ * payment info and update products to finish up their storefront." The
22
+ * Ember & Oak Candle Co start had Home, Shop, Gift sets, About, Shipping &
23
+ * care and Contact, and a product page per product; a shopper had nowhere to
24
+ * sign in, see an order, find what they saved, or read the store's privacy
25
+ * policy and terms.
26
+ *
27
+ * So a paid store start gets the pages every storefront has, written by code
28
+ * from the platform's own elements — never asked of the model:
29
+ *
30
+ * - ACCOUNT at /account: the commerce plugin's Customer account (sign in,
31
+ * create an account, profile, addresses, order history with status and
32
+ * tracking, sign out) and Saved items (the Wishlist the product page's
33
+ * Save fills, kept for a guest in the browser and on the account once
34
+ * they sign in);
35
+ * - CART at /cart: the Cart in full, with its checkout, for a shopper who
36
+ * goes to the address rather than the header's cart drawer;
37
+ * - SHIPPING & RETURNS at /shipping-returns, PRIVACY POLICY at /privacy and
38
+ * TERMS OF SALE at /terms: editable drafts in the Markdown element, every
39
+ * fact only the merchant knows left as a [bracketed placeholder] — never a
40
+ * return window, a price or a promise the brief did not give.
41
+ *
42
+ * The account and the cart are a shopper's own pages, written UNLISTED
43
+ * (noindex, out of the sitemap); the policies are indexable like every other
44
+ * page.
45
+ *
46
+ * An order's own page is the commerce plugin's built-in /order-status, which
47
+ * every order email links with its signed key; the Account page says so.
48
+ *
49
+ * ── Outside the plan's page count ───────────────────────────────────────
50
+ *
51
+ * They are STORE PAGES, not planned pages: the plan's four to eight pages are
52
+ * the content the model designs, and these are added beside them, so they
53
+ * never crowd one out. They still count toward the plan's pages allowance
54
+ * (`screensPerHost`), as every page a site has does; a store is Starter and
55
+ * up, whose allowance holds both.
56
+ *
57
+ * A page the plan already has stands for its store page instead of a second
58
+ * one: a planned "Shipping & care" is the store's shipping page, and a page
59
+ * the plan put at /privacy is its privacy policy. The header and footer link
60
+ * that page.
61
+ *
62
+ * Pure: names, paths, words and node trees. Safe for the console to import.
63
+ */ /** A store page, by what it is. Persisted in job outputs (`proposal.storePage`); never renamed. */ import { _ as _extends } from "@swc/helpers/_/_extends";
64
+ /** Every store page, in the order the job writes them and the footer links them. */ export const AI_STORE_PAGES = [
65
+ {
66
+ key: 'account',
67
+ title: 'Your account',
68
+ slug: 'account',
69
+ link: 'Your account',
70
+ noindex: true,
71
+ seo: {
72
+ title: 'Your account',
73
+ description: 'Sign in to see your orders, saved addresses and saved items.'
74
+ }
75
+ },
76
+ {
77
+ key: 'cart',
78
+ title: 'Your cart',
79
+ slug: 'cart',
80
+ link: 'Cart',
81
+ noindex: true,
82
+ seo: {
83
+ title: 'Your cart',
84
+ description: 'Review your cart and check out.'
85
+ }
86
+ },
87
+ {
88
+ key: 'shipping',
89
+ title: 'Shipping & returns',
90
+ slug: 'shipping-returns',
91
+ link: 'Shipping & returns',
92
+ seo: {
93
+ title: 'Shipping & returns',
94
+ description: 'How we ship your order, and how returns and refunds work.'
95
+ }
96
+ },
97
+ {
98
+ key: 'privacy',
99
+ title: 'Privacy policy',
100
+ slug: 'privacy',
101
+ link: 'Privacy policy',
102
+ seo: {
103
+ title: 'Privacy policy',
104
+ description: 'What we collect when you shop with us, and what we do with it.'
105
+ }
106
+ },
107
+ {
108
+ key: 'terms',
109
+ title: 'Terms of sale',
110
+ slug: 'terms',
111
+ link: 'Terms of sale',
112
+ seo: {
113
+ title: 'Terms of sale',
114
+ description: 'The terms that apply when you buy from our store.'
115
+ }
116
+ }
117
+ ];
118
+ /** What the job's row for the store pages reads. */ export const AI_SITE_STORE_PAGES_LABEL = 'Adding your account, cart and policy pages';
119
+ /** The note the row carries once they are written: what the merchant fills in. */ export const AI_SITE_STORE_PAGES_NOTE = 'Your shipping, privacy and terms pages are drafts: fill in the parts in [brackets] with your store’s own details.';
120
+ /** A planned page whose words say it is the store's shipping page: "Shipping & care", "Delivery & returns". */ const SHIPPING_WORDS = /\b(shipping|delivery|returns?|refunds?)\b/i;
121
+ /** The words of a planned page's address and title, spaced. */ const wordsOf = (screen)=>`${screen.slug.replace(/[-/_]+/g, ' ')} ${screen.title}`;
122
+ const firstSegment = (slug)=>slug.trim().replace(/^\/+/, '').split('/')[0].toLowerCase();
123
+ /**
124
+ * The store pages a store plan gets: each one written, or — where a planned
125
+ * page already holds its address, or a planned page's words say it is the
126
+ * store's shipping page — that planned page, linked in its place.
127
+ */ export function aiStorePagesOfPlan(screens) {
128
+ const pages = screens.filter((screen)=>!screen.record && firstSegment(screen.slug) !== '');
129
+ return AI_STORE_PAGES.map((page)=>{
130
+ var _pages_find;
131
+ const holds = (_pages_find = pages.find((screen)=>firstSegment(screen.slug) === page.slug)) != null ? _pages_find : page.key === 'shipping' ? pages.find((screen)=>SHIPPING_WORDS.test(wordsOf(screen))) : undefined;
132
+ if (holds) {
133
+ const slug = firstSegment(holds.slug);
134
+ return {
135
+ key: page.key,
136
+ title: page.title,
137
+ slug,
138
+ href: `/${slug}`,
139
+ link: holds.title.trim() || page.link,
140
+ planned: holds.title
141
+ };
142
+ }
143
+ return {
144
+ key: page.key,
145
+ title: page.title,
146
+ slug: page.slug,
147
+ href: `/${page.slug}`,
148
+ link: page.link
149
+ };
150
+ });
151
+ }
152
+ /** The store pages a start writes itself: those no planned page stands for. */ export function aiStorePagesToWrite(pages) {
153
+ return pages.filter((page)=>!page.planned);
154
+ }
155
+ /** The unit input a site layout is told the store's links under. */ export const AI_SITE_STORE_LINKS_INPUT = 'storeLinks';
156
+ /** The header's and footer's store links for these store pages. The cart is the header's own button. */ export function aiStoreFrameLinks(pages) {
157
+ var _ref;
158
+ const account = pages.find((page)=>page.key === 'account');
159
+ return {
160
+ account: (_ref = account == null ? void 0 : account.href) != null ? _ref : null,
161
+ footer: pages.filter((page)=>page.key !== 'cart').map((page)=>({
162
+ label: page.link,
163
+ href: page.href
164
+ }))
165
+ };
166
+ }
167
+ const PATH = /^\/[a-z0-9-]+$/;
168
+ /** The store links a layout unit's inputs carry; none for any other layout. */ export function aiStoreFrameLinksOf(inputs) {
169
+ const raw = inputs == null ? void 0 : inputs[AI_SITE_STORE_LINKS_INPUT];
170
+ if (!raw || typeof raw !== 'object') return null;
171
+ const { account, footer } = raw;
172
+ const links = Array.isArray(footer) ? footer.flatMap((entry)=>{
173
+ const { label, href } = entry != null ? entry : {};
174
+ return typeof label === 'string' && label.trim() && typeof href === 'string' && PATH.test(href) ? [
175
+ {
176
+ label: label.trim().slice(0, 40),
177
+ href
178
+ }
179
+ ] : [];
180
+ }) : [];
181
+ const accountPath = typeof account === 'string' && PATH.test(account) ? account : null;
182
+ return accountPath || links.length ? {
183
+ account: accountPath,
184
+ footer: links.slice(0, 6)
185
+ } : null;
186
+ }
187
+ // ── The pages, as node trees ──────────────────────────────────────────────
188
+ /** The canvas root every stored page hangs from (`CANVAS_ROOT_ELEMENT_ID`). */ const ROOT = '_@_';
189
+ /** The longest one Markdown element is given: the element's own text limit. */ export const AI_STORE_MARKDOWN_MAX = 2000;
190
+ /** The commerce plugin's elements a store page places, by the ids it persists (`ai-site-store-pages.spec.ts` holds them). */ export const AI_STORE_ELEMENTS = {
191
+ account: 'customer-account',
192
+ wishlist: 'wishlist',
193
+ cart: 'cart'
194
+ };
195
+ /** The host token that reads the site's own name at render (`AI_SITE_NAME_TOKEN`). */ const NAME = '{{host.businessName}}';
196
+ /** How a sentence asks a shopper to get in touch: the contact page, else a reply to their order email. */ function contact(facts, words = 'contact us') {
197
+ return facts.contactPath ? `[${words}](${facts.contactPath})` : `${words} by replying to your order email`;
198
+ }
199
+ /** The policy's sections, as markdown, each a `##` heading and its paragraphs. */ export function aiStorePolicySections(key, facts) {
200
+ if (key === 'shipping') {
201
+ return [
202
+ '## Shipping\n\nWe ship orders from [where you ship from]. Orders are packed within [number] business days, and you will get an email with tracking as soon as yours is on its way.\n\nShipping costs and delivery options are shown at checkout before you pay. [List the countries or regions you ship to, and any free-shipping offer you make.]',
203
+ `## Returns\n\nYou can return an item within [number] days of delivery if it is [unused and in its original packaging]. [Name anything that cannot be returned, such as gift cards or personalized items.]\n\nTo start a return, open the link in your order email and choose Request a return, or ${contact(facts)}. [Say who pays for return shipping.]`,
204
+ '## Refunds\n\nOnce we receive and check your return, we refund it to your original payment method within [number] business days. [Say whether the original shipping cost is refunded.]',
205
+ `## Damaged or wrong items\n\nIf your order arrives damaged or is not what you ordered, ${contact(facts)} within [number] days of delivery with your order number and a photo, and we will put it right.`
206
+ ];
207
+ }
208
+ if (key === 'privacy') {
209
+ return [
210
+ `## Who we are\n\n${NAME} runs this online store. This policy explains what we collect when you visit, shop or create an account, and what we do with it. [Add your business’s legal name and postal address.]`,
211
+ '## What we collect\n\n- **When you order:** your name, email address, shipping and billing address, your phone number if you give one, and what you bought.\n- **When you create an account:** your name, email address, saved addresses, order history and saved items.\n- **When you sign up for emails:** your email address and your choice to hear from us.\n- **Payments:** card payments are processed by Stripe. We never see or store your full card number.',
212
+ '## How we use it\n\nWe use your information to process and deliver your orders, to send order and shipping emails, to answer your questions, and, only if you opt in, to send you news and offers. You can unsubscribe from marketing emails at any time with the link in each one.',
213
+ '## Who we share it with\n\nWe share only what is needed with the services that run the store: our website and store platform, our payment processor (Stripe), and the carriers that deliver your order. [List any other services you use, such as an email or analytics tool.] We do not sell your personal information.',
214
+ '## Cookies\n\nThis site uses cookies to keep your cart and your sign-in working. [Describe any analytics or advertising cookies you use, and how visitors choose.]',
215
+ `## Your choices\n\nYou can ask to see, correct or delete the information we hold about you: ${contact(facts)}. [Say how long you keep order records, and the privacy rights that apply where you sell.]\n\nWe post any change to this policy on this page. Last updated: [date].`
216
+ ];
217
+ }
218
+ return [
219
+ `## About these terms\n\nThese terms apply to purchases from ${NAME} through this website. By placing an order you agree to them. [Add your business’s legal name and where it is registered.]`,
220
+ '## Orders\n\nYour order is an offer to buy. We accept it when we send your order confirmation email. If an item turns out to be unavailable, we will tell you and refund what you paid for it.',
221
+ '## Prices and payment\n\nPrices are shown in [currency] and [include or exclude] [sales tax or VAT]. Shipping and any taxes are shown at checkout before you pay. Payment is taken securely through Stripe when you place your order.',
222
+ `## Shipping, returns and refunds\n\nHow we ship, and how returns and refunds work, is set out on our [Shipping & returns](${facts.shippingPath}) page.`,
223
+ '## Products\n\nWe describe and photograph our products as accurately as we can. [Note anything that naturally varies, such as handmade pieces, colors or sizes.]',
224
+ `## Liability\n\n[State any limit on your liability, as the law where you sell allows.] Nothing in these terms limits your rights under consumer law.\n\n## Contact\n\nQuestions about an order? ${contact(facts, 'Contact us')}. Last updated: [date].`
225
+ ];
226
+ }
227
+ /** Sections joined into as few Markdown elements as fit the element's limit, in order. */ export function aiStoreMarkdownChunks(sections, max = AI_STORE_MARKDOWN_MAX) {
228
+ const chunks = [];
229
+ for (const section of sections){
230
+ const last = chunks[chunks.length - 1];
231
+ if (last !== undefined && last.length + 2 + section.length <= max) chunks[chunks.length - 1] = `${last}\n\n${section}`;
232
+ else chunks.push(section.slice(0, max));
233
+ }
234
+ return chunks;
235
+ }
236
+ /**
237
+ * A store page's node tree, root first, as a page's first version stores it:
238
+ * the page's heading over the platform's elements, in one section the
239
+ * site's layout renders inside.
240
+ */ export function aiStorePageNodes(key, facts) {
241
+ const page = AI_STORE_PAGES.find((one)=>one.key === key);
242
+ const nodes = {};
243
+ const id = (role)=>`store-${key}-${role}`;
244
+ const container = id('container');
245
+ const add = (role, componentId, pluginId, props, sx)=>{
246
+ const nodeId = id(role);
247
+ nodes[nodeId] = _extends({
248
+ $id: nodeId,
249
+ type: 'node',
250
+ componentId,
251
+ pluginId,
252
+ parentId: container,
253
+ nodes: [],
254
+ props
255
+ }, sx ? {
256
+ sx
257
+ } : {});
258
+ return nodeId;
259
+ };
260
+ const children = [
261
+ add('title', 'muiTypography', 'mui', {
262
+ children: page.title,
263
+ variant: 'h1',
264
+ component: 'h1'
265
+ }, {
266
+ mb: 4
267
+ })
268
+ ];
269
+ if (key === 'account') {
270
+ children.push(add('account', AI_STORE_ELEMENTS.account, 'commerce', {
271
+ signedOutHeading: 'Sign in or create an account'
272
+ }), add('guest', 'muiTypography', 'mui', {
273
+ children: 'Checked out as a guest? The link in your order email opens your order’s status and tracking.',
274
+ variant: 'body2',
275
+ component: 'p'
276
+ }, {
277
+ color: 'text.secondary',
278
+ mt: 3
279
+ }), add('saved', AI_STORE_ELEMENTS.wishlist, 'commerce', {
280
+ heading: 'Saved items',
281
+ emptyText: 'Nothing saved yet. Tap Save on any product to keep it here.'
282
+ }, {
283
+ mt: 8
284
+ }));
285
+ } else if (key === 'cart') {
286
+ children.push(add('cart', AI_STORE_ELEMENTS.cart, 'commerce', {
287
+ variant: 'inline',
288
+ showCoupon: true
289
+ }), add('shop', 'muiButton', 'mui', {
290
+ children: 'Continue shopping',
291
+ variant: 'outlined',
292
+ href: facts.shopPath
293
+ }, {
294
+ mt: 4
295
+ }));
296
+ } else {
297
+ aiStoreMarkdownChunks(aiStorePolicySections(key, facts)).forEach((content, index)=>{
298
+ children.push(add(`text${index + 1}`, 'markdown', 'mui', {
299
+ content
300
+ }));
301
+ });
302
+ }
303
+ const section = id('section');
304
+ nodes[ROOT] = {
305
+ $id: ROOT,
306
+ type: 'node',
307
+ componentId: 'div',
308
+ pluginId: 'mui',
309
+ parentId: null,
310
+ nodes: [
311
+ section
312
+ ]
313
+ };
314
+ nodes[section] = {
315
+ $id: section,
316
+ type: 'node',
317
+ componentId: 'section',
318
+ pluginId: 'mui',
319
+ parentId: ROOT,
320
+ nodes: [
321
+ container
322
+ ],
323
+ props: {
324
+ element: 'section',
325
+ ariaLabel: page.title
326
+ }
327
+ };
328
+ nodes[container] = {
329
+ $id: container,
330
+ type: 'node',
331
+ componentId: 'muiContainer',
332
+ pluginId: 'mui',
333
+ parentId: section,
334
+ nodes: children,
335
+ props: {
336
+ maxWidth: key === 'account' || key === 'cart' ? 'lg' : 'md'
337
+ },
338
+ sx: {
339
+ py: {
340
+ xs: 6,
341
+ md: 10
342
+ }
343
+ }
344
+ };
345
+ return nodes;
346
+ }
347
+ /**
348
+ * What is left once an AI store is built (Zach, 2026-10-10): connect
349
+ * payments, review the products and their prices, set shipping and tax, and
350
+ * fill in the policies' brackets. Everything else — the storefront, the
351
+ * account, cart and policy pages, the header's cart and account — is done.
352
+ */ export const AI_STORE_FINISH_STEPS = [
353
+ {
354
+ id: 'payments',
355
+ title: 'Connect payments',
356
+ text: 'Connect Stripe so your store can take payments and pay you out.',
357
+ opens: 'store-settings',
358
+ action: 'Set up payments'
359
+ },
360
+ {
361
+ id: 'products',
362
+ title: 'Review your products and prices',
363
+ text: 'Your first products are listed with a starting price. Set your real prices, photos and stock.',
364
+ opens: 'product',
365
+ action: 'Open products'
366
+ },
367
+ {
368
+ id: 'shipping',
369
+ title: 'Set shipping and tax',
370
+ text: 'Choose your shipping rates and how tax is charged at checkout.',
371
+ opens: 'store-settings',
372
+ action: 'Open store settings'
373
+ },
374
+ {
375
+ id: 'policies',
376
+ title: 'Fill in your policies',
377
+ text: 'Your Shipping & returns, Privacy policy and Terms of sale pages are drafts: replace the parts in [brackets].',
378
+ opens: 'pages',
379
+ action: 'Open pages'
380
+ }
381
+ ];
382
+
383
+ //# sourceMappingURL=ai-site-store-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-store-pages.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A STORE'S OWN PAGES (AGL-3676, third round).\n *\n * Zach, 2026-10-10: \"We seem to be missing the account pages for the\n * shop/store AI generation, make sure all they would have to do is setup the\n * payment info and update products to finish up their storefront.\" The\n * Ember & Oak Candle Co start had Home, Shop, Gift sets, About, Shipping &\n * care and Contact, and a product page per product; a shopper had nowhere to\n * sign in, see an order, find what they saved, or read the store's privacy\n * policy and terms.\n *\n * So a paid store start gets the pages every storefront has, written by code\n * from the platform's own elements — never asked of the model:\n *\n * - ACCOUNT at /account: the commerce plugin's Customer account (sign in,\n * create an account, profile, addresses, order history with status and\n * tracking, sign out) and Saved items (the Wishlist the product page's\n * Save fills, kept for a guest in the browser and on the account once\n * they sign in);\n * - CART at /cart: the Cart in full, with its checkout, for a shopper who\n * goes to the address rather than the header's cart drawer;\n * - SHIPPING & RETURNS at /shipping-returns, PRIVACY POLICY at /privacy and\n * TERMS OF SALE at /terms: editable drafts in the Markdown element, every\n * fact only the merchant knows left as a [bracketed placeholder] — never a\n * return window, a price or a promise the brief did not give.\n *\n * The account and the cart are a shopper's own pages, written UNLISTED\n * (noindex, out of the sitemap); the policies are indexable like every other\n * page.\n *\n * An order's own page is the commerce plugin's built-in /order-status, which\n * every order email links with its signed key; the Account page says so.\n *\n * ── Outside the plan's page count ───────────────────────────────────────\n *\n * They are STORE PAGES, not planned pages: the plan's four to eight pages are\n * the content the model designs, and these are added beside them, so they\n * never crowd one out. They still count toward the plan's pages allowance\n * (`screensPerHost`), as every page a site has does; a store is Starter and\n * up, whose allowance holds both.\n *\n * A page the plan already has stands for its store page instead of a second\n * one: a planned \"Shipping & care\" is the store's shipping page, and a page\n * the plan put at /privacy is its privacy policy. The header and footer link\n * that page.\n *\n * Pure: names, paths, words and node trees. Safe for the console to import.\n */\n\n/** A store page, by what it is. Persisted in job outputs (`proposal.storePage`); never renamed. */\nexport type AiStorePageKey = 'account' | 'cart' | 'shipping' | 'privacy' | 'terms'\n\nexport interface AiStorePageDefinition {\n key: AiStorePageKey\n /** The page's name in Pages, and its heading. */\n title: string\n slug: string\n /** What the footer's link to it says. */\n link: string\n seo: { title: string; description: string }\n /**\n * A shopper's own page, not one to find by search: written UNLISTED, the\n * page's Access setting the console offers, so the tenant serves it\n * `noindex` and leaves it out of the sitemap. Only the account and the\n * cart; the policies, like every content and landing page, stay indexable.\n */\n noindex?: true\n}\n\n/** Every store page, in the order the job writes them and the footer links them. */\nexport const AI_STORE_PAGES: readonly AiStorePageDefinition[] = [\n {\n key: 'account',\n title: 'Your account',\n slug: 'account',\n link: 'Your account',\n noindex: true,\n seo: { title: 'Your account', description: 'Sign in to see your orders, saved addresses and saved items.' },\n },\n {\n key: 'cart',\n title: 'Your cart',\n slug: 'cart',\n link: 'Cart',\n noindex: true,\n seo: { title: 'Your cart', description: 'Review your cart and check out.' },\n },\n {\n key: 'shipping',\n title: 'Shipping & returns',\n slug: 'shipping-returns',\n link: 'Shipping & returns',\n seo: { title: 'Shipping & returns', description: 'How we ship your order, and how returns and refunds work.' },\n },\n {\n key: 'privacy',\n title: 'Privacy policy',\n slug: 'privacy',\n link: 'Privacy policy',\n seo: { title: 'Privacy policy', description: 'What we collect when you shop with us, and what we do with it.' },\n },\n {\n key: 'terms',\n title: 'Terms of sale',\n slug: 'terms',\n link: 'Terms of sale',\n seo: { title: 'Terms of sale', description: 'The terms that apply when you buy from our store.' },\n },\n]\n\n/** What the job's row for the store pages reads. */\nexport const AI_SITE_STORE_PAGES_LABEL = 'Adding your account, cart and policy pages'\n\n/** The note the row carries once they are written: what the merchant fills in. */\nexport const AI_SITE_STORE_PAGES_NOTE =\n 'Your shipping, privacy and terms pages are drafts: fill in the parts in [brackets] with your store’s own details.'\n\n/** A planned page whose words say it is the store's shipping page: \"Shipping & care\", \"Delivery & returns\". */\nconst SHIPPING_WORDS = /\\b(shipping|delivery|returns?|refunds?)\\b/i\n\n/** The words of a planned page's address and title, spaced. */\nconst wordsOf = (screen: { slug: string; title: string }) => `${screen.slug.replace(/[-/_]+/g, ' ')} ${screen.title}`\n\nconst firstSegment = (slug: string) => slug.trim().replace(/^\\/+/, '').split('/')[0].toLowerCase()\n\n/** A store page as a start builds or links it. */\nexport interface AiStorePagePlan {\n key: AiStorePageKey\n title: string\n slug: string\n /** Where the header and footer link it. */\n href: string\n /** What the footer's link says. */\n link: string\n /**\n * The planned page that stands for it, by its title, where the plan has\n * one: nothing is written for this key, and its links go to that page.\n */\n planned?: string\n}\n\n/**\n * The store pages a store plan gets: each one written, or — where a planned\n * page already holds its address, or a planned page's words say it is the\n * store's shipping page — that planned page, linked in its place.\n */\nexport function aiStorePagesOfPlan(screens: ReadonlyArray<{ slug: string; title: string; record?: unknown }>): AiStorePagePlan[] {\n const pages = screens.filter((screen) => !screen.record && firstSegment(screen.slug) !== '')\n return AI_STORE_PAGES.map((page) => {\n const holds =\n pages.find((screen) => firstSegment(screen.slug) === page.slug) ??\n (page.key === 'shipping' ? pages.find((screen) => SHIPPING_WORDS.test(wordsOf(screen))) : undefined)\n if (holds) {\n const slug = firstSegment(holds.slug)\n return { key: page.key, title: page.title, slug, href: `/${slug}`, link: holds.title.trim() || page.link, planned: holds.title }\n }\n return { key: page.key, title: page.title, slug: page.slug, href: `/${page.slug}`, link: page.link }\n })\n}\n\n/** The store pages a start writes itself: those no planned page stands for. */\nexport function aiStorePagesToWrite(pages: readonly AiStorePagePlan[]): AiStorePagePlan[] {\n return pages.filter((page) => !page.planned)\n}\n\n/** The unit input a site layout is told the store's links under. */\nexport const AI_SITE_STORE_LINKS_INPUT = 'storeLinks'\n\n/** What a store's header and footer link (AGL-3676): the account beside the cart, and the store's pages under the site's. */\nexport interface AiStoreFrameLinks {\n /** The account page's path, which the header links beside the cart. */\n account: string | null\n /** The footer's store links, in order: the account, then the policies. */\n footer: Array<{ label: string; href: string }>\n}\n\n/** The header's and footer's store links for these store pages. The cart is the header's own button. */\nexport function aiStoreFrameLinks(pages: readonly AiStorePagePlan[]): AiStoreFrameLinks {\n const account = pages.find((page) => page.key === 'account')\n return {\n account: account?.href ?? null,\n footer: pages.filter((page) => page.key !== 'cart').map((page) => ({ label: page.link, href: page.href })),\n }\n}\n\nconst PATH = /^\\/[a-z0-9-]+$/\n\n/** The store links a layout unit's inputs carry; none for any other layout. */\nexport function aiStoreFrameLinksOf(inputs: Readonly<Record<string, unknown>> | null | undefined): AiStoreFrameLinks | null {\n const raw = inputs?.[AI_SITE_STORE_LINKS_INPUT]\n if (!raw || typeof raw !== 'object') return null\n const { account, footer } = raw as Record<string, unknown>\n const links = Array.isArray(footer)\n ? footer.flatMap((entry) => {\n const { label, href } = (entry ?? {}) as Record<string, unknown>\n return typeof label === 'string' && label.trim() && typeof href === 'string' && PATH.test(href)\n ? [{ label: label.trim().slice(0, 40), href }]\n : []\n })\n : []\n const accountPath = typeof account === 'string' && PATH.test(account) ? account : null\n return accountPath || links.length ? { account: accountPath, footer: links.slice(0, 6) } : null\n}\n\n// ── The pages, as node trees ──────────────────────────────────────────────\n\n/** The canvas root every stored page hangs from (`CANVAS_ROOT_ELEMENT_ID`). */\nconst ROOT = '_@_'\n\n/** The longest one Markdown element is given: the element's own text limit. */\nexport const AI_STORE_MARKDOWN_MAX = 2000\n\n/** The commerce plugin's elements a store page places, by the ids it persists (`ai-site-store-pages.spec.ts` holds them). */\nexport const AI_STORE_ELEMENTS = {\n account: 'customer-account',\n wishlist: 'wishlist',\n cart: 'cart',\n} as const\n\n/** The host token that reads the site's own name at render (`AI_SITE_NAME_TOKEN`). */\nconst NAME = '{{host.businessName}}'\n\n/** What a store page's words may link: the site's contact page and its shop, where the plan has them. */\nexport interface AiStorePageFacts {\n /** The contact page's path, or `null` where the plan has none. */\n contactPath: string | null\n /** The shop's path, `/shop` where the plan has none. */\n shopPath: string\n /** The shipping page's path, as the terms link it. */\n shippingPath: string\n}\n\ninterface StoredNode {\n $id: string\n type: 'node'\n componentId: string\n pluginId: string\n parentId: string | null\n nodes: string[]\n props?: Record<string, unknown>\n sx?: Record<string, unknown>\n}\n\n/** How a sentence asks a shopper to get in touch: the contact page, else a reply to their order email. */\nfunction contact(facts: AiStorePageFacts, words = 'contact us'): string {\n return facts.contactPath ? `[${words}](${facts.contactPath})` : `${words} by replying to your order email`\n}\n\n/** The policy's sections, as markdown, each a `##` heading and its paragraphs. */\nexport function aiStorePolicySections(key: 'shipping' | 'privacy' | 'terms', facts: AiStorePageFacts): string[] {\n if (key === 'shipping') {\n return [\n '## Shipping\\n\\nWe ship orders from [where you ship from]. Orders are packed within [number] business days, and you will get an email with tracking as soon as yours is on its way.\\n\\nShipping costs and delivery options are shown at checkout before you pay. [List the countries or regions you ship to, and any free-shipping offer you make.]',\n `## Returns\\n\\nYou can return an item within [number] days of delivery if it is [unused and in its original packaging]. [Name anything that cannot be returned, such as gift cards or personalized items.]\\n\\nTo start a return, open the link in your order email and choose Request a return, or ${contact(facts)}. [Say who pays for return shipping.]`,\n '## Refunds\\n\\nOnce we receive and check your return, we refund it to your original payment method within [number] business days. [Say whether the original shipping cost is refunded.]',\n `## Damaged or wrong items\\n\\nIf your order arrives damaged or is not what you ordered, ${contact(facts)} within [number] days of delivery with your order number and a photo, and we will put it right.`,\n ]\n }\n if (key === 'privacy') {\n return [\n `## Who we are\\n\\n${NAME} runs this online store. This policy explains what we collect when you visit, shop or create an account, and what we do with it. [Add your business’s legal name and postal address.]`,\n '## What we collect\\n\\n- **When you order:** your name, email address, shipping and billing address, your phone number if you give one, and what you bought.\\n- **When you create an account:** your name, email address, saved addresses, order history and saved items.\\n- **When you sign up for emails:** your email address and your choice to hear from us.\\n- **Payments:** card payments are processed by Stripe. We never see or store your full card number.',\n '## How we use it\\n\\nWe use your information to process and deliver your orders, to send order and shipping emails, to answer your questions, and, only if you opt in, to send you news and offers. You can unsubscribe from marketing emails at any time with the link in each one.',\n '## Who we share it with\\n\\nWe share only what is needed with the services that run the store: our website and store platform, our payment processor (Stripe), and the carriers that deliver your order. [List any other services you use, such as an email or analytics tool.] We do not sell your personal information.',\n '## Cookies\\n\\nThis site uses cookies to keep your cart and your sign-in working. [Describe any analytics or advertising cookies you use, and how visitors choose.]',\n `## Your choices\\n\\nYou can ask to see, correct or delete the information we hold about you: ${contact(facts)}. [Say how long you keep order records, and the privacy rights that apply where you sell.]\\n\\nWe post any change to this policy on this page. Last updated: [date].`,\n ]\n }\n return [\n `## About these terms\\n\\nThese terms apply to purchases from ${NAME} through this website. By placing an order you agree to them. [Add your business’s legal name and where it is registered.]`,\n '## Orders\\n\\nYour order is an offer to buy. We accept it when we send your order confirmation email. If an item turns out to be unavailable, we will tell you and refund what you paid for it.',\n '## Prices and payment\\n\\nPrices are shown in [currency] and [include or exclude] [sales tax or VAT]. Shipping and any taxes are shown at checkout before you pay. Payment is taken securely through Stripe when you place your order.',\n `## Shipping, returns and refunds\\n\\nHow we ship, and how returns and refunds work, is set out on our [Shipping & returns](${facts.shippingPath}) page.`,\n '## Products\\n\\nWe describe and photograph our products as accurately as we can. [Note anything that naturally varies, such as handmade pieces, colors or sizes.]',\n `## Liability\\n\\n[State any limit on your liability, as the law where you sell allows.] Nothing in these terms limits your rights under consumer law.\\n\\n## Contact\\n\\nQuestions about an order? ${contact(facts, 'Contact us')}. Last updated: [date].`,\n ]\n}\n\n/** Sections joined into as few Markdown elements as fit the element's limit, in order. */\nexport function aiStoreMarkdownChunks(sections: readonly string[], max = AI_STORE_MARKDOWN_MAX): string[] {\n const chunks: string[] = []\n for (const section of sections) {\n const last = chunks[chunks.length - 1]\n if (last !== undefined && last.length + 2 + section.length <= max) chunks[chunks.length - 1] = `${last}\\n\\n${section}`\n else chunks.push(section.slice(0, max))\n }\n return chunks\n}\n\n/**\n * A store page's node tree, root first, as a page's first version stores it:\n * the page's heading over the platform's elements, in one section the\n * site's layout renders inside.\n */\nexport function aiStorePageNodes(key: AiStorePageKey, facts: AiStorePageFacts): Record<string, StoredNode> {\n const page = AI_STORE_PAGES.find((one) => one.key === key) as AiStorePageDefinition\n const nodes: Record<string, StoredNode> = {}\n const id = (role: string) => `store-${key}-${role}`\n const container = id('container')\n const add = (role: string, componentId: string, pluginId: string, props: Record<string, unknown>, sx?: Record<string, unknown>): string => {\n const nodeId = id(role)\n nodes[nodeId] = { $id: nodeId, type: 'node', componentId, pluginId, parentId: container, nodes: [], props, ...(sx ? { sx } : {}) }\n return nodeId\n }\n const children: string[] = [add('title', 'muiTypography', 'mui', { children: page.title, variant: 'h1', component: 'h1' }, { mb: 4 })]\n if (key === 'account') {\n children.push(\n add('account', AI_STORE_ELEMENTS.account, 'commerce', { signedOutHeading: 'Sign in or create an account' }),\n add(\n 'guest',\n 'muiTypography',\n 'mui',\n { children: 'Checked out as a guest? The link in your order email opens your order’s status and tracking.', variant: 'body2', component: 'p' },\n { color: 'text.secondary', mt: 3 },\n ),\n add(\n 'saved',\n AI_STORE_ELEMENTS.wishlist,\n 'commerce',\n { heading: 'Saved items', emptyText: 'Nothing saved yet. Tap Save on any product to keep it here.' },\n { mt: 8 },\n ),\n )\n } else if (key === 'cart') {\n children.push(\n add('cart', AI_STORE_ELEMENTS.cart, 'commerce', { variant: 'inline', showCoupon: true }),\n add('shop', 'muiButton', 'mui', { children: 'Continue shopping', variant: 'outlined', href: facts.shopPath }, { mt: 4 }),\n )\n } else {\n aiStoreMarkdownChunks(aiStorePolicySections(key, facts)).forEach((content, index) => {\n children.push(add(`text${index + 1}`, 'markdown', 'mui', { content }))\n })\n }\n const section = id('section')\n nodes[ROOT] = { $id: ROOT, type: 'node', componentId: 'div', pluginId: 'mui', parentId: null, nodes: [section] }\n nodes[section] = {\n $id: section,\n type: 'node',\n componentId: 'section',\n pluginId: 'mui',\n parentId: ROOT,\n nodes: [container],\n props: { element: 'section', ariaLabel: page.title },\n }\n nodes[container] = {\n $id: container,\n type: 'node',\n componentId: 'muiContainer',\n pluginId: 'mui',\n parentId: section,\n nodes: children,\n props: { maxWidth: key === 'account' || key === 'cart' ? 'lg' : 'md' },\n sx: { py: { xs: 6, md: 10 } },\n }\n return nodes\n}\n\n// ── Finishing the store ───────────────────────────────────────────────────\n\n/** One thing left before an AI-built store sells, as its done page lists it. */\nexport interface AiStoreFinishStep {\n id: 'payments' | 'products' | 'shipping' | 'policies' | 'links'\n title: string\n text: string\n /**\n * The record route kind whose list the step opens (`plugin-record-routes`),\n * `pages` for the site's Pages, `layouts` for its Layouts, or `layout` for\n * the layout version a build drafted its store's links into (AGL-3676).\n */\n opens: 'store-settings' | 'product' | 'pages' | 'layouts' | 'layout'\n action: string\n}\n\n/**\n * What is left once an AI store is built (Zach, 2026-10-10): connect\n * payments, review the products and their prices, set shipping and tax, and\n * fill in the policies' brackets. Everything else — the storefront, the\n * account, cart and policy pages, the header's cart and account — is done.\n */\nexport const AI_STORE_FINISH_STEPS: readonly AiStoreFinishStep[] = [\n {\n id: 'payments',\n title: 'Connect payments',\n text: 'Connect Stripe so your store can take payments and pay you out.',\n opens: 'store-settings',\n action: 'Set up payments',\n },\n {\n id: 'products',\n title: 'Review your products and prices',\n text: 'Your first products are listed with a starting price. Set your real prices, photos and stock.',\n opens: 'product',\n action: 'Open products',\n },\n {\n id: 'shipping',\n title: 'Set shipping and tax',\n text: 'Choose your shipping rates and how tax is charged at checkout.',\n opens: 'store-settings',\n action: 'Open store settings',\n },\n {\n id: 'policies',\n title: 'Fill in your policies',\n text: 'Your Shipping & returns, Privacy policy and Terms of sale pages are drafts: replace the parts in [brackets].',\n opens: 'pages',\n action: 'Open pages',\n },\n]\n"],"names":["AI_STORE_PAGES","key","title","slug","link","noindex","seo","description","AI_SITE_STORE_PAGES_LABEL","AI_SITE_STORE_PAGES_NOTE","SHIPPING_WORDS","wordsOf","screen","replace","firstSegment","trim","split","toLowerCase","aiStorePagesOfPlan","screens","pages","filter","record","map","page","holds","find","test","undefined","href","planned","aiStorePagesToWrite","AI_SITE_STORE_LINKS_INPUT","aiStoreFrameLinks","account","footer","label","PATH","aiStoreFrameLinksOf","inputs","raw","links","Array","isArray","flatMap","entry","slice","accountPath","length","ROOT","AI_STORE_MARKDOWN_MAX","AI_STORE_ELEMENTS","wishlist","cart","NAME","contact","facts","words","contactPath","aiStorePolicySections","shippingPath","aiStoreMarkdownChunks","sections","max","chunks","section","last","push","aiStorePageNodes","one","nodes","id","role","container","add","componentId","pluginId","props","sx","nodeId","$id","type","parentId","children","variant","component","mb","signedOutHeading","color","mt","heading","emptyText","showCoupon","shopPath","forEach","content","index","element","ariaLabel","maxWidth","py","xs","md","AI_STORE_FINISH_STEPS","text","opens","action"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CC,GAED,iGAAiG;AAoBjG,kFAAkF,GAClF,OAAO,MAAMA,iBAAmD;IAC9D;QACEC,KAAK;QACLC,OAAO;QACPC,MAAM;QACNC,MAAM;QACNC,SAAS;QACTC,KAAK;YAAEJ,OAAO;YAAgBK,aAAa;QAA+D;IAC5G;IACA;QACEN,KAAK;QACLC,OAAO;QACPC,MAAM;QACNC,MAAM;QACNC,SAAS;QACTC,KAAK;YAAEJ,OAAO;YAAaK,aAAa;QAAkC;IAC5E;IACA;QACEN,KAAK;QACLC,OAAO;QACPC,MAAM;QACNC,MAAM;QACNE,KAAK;YAAEJ,OAAO;YAAsBK,aAAa;QAA4D;IAC/G;IACA;QACEN,KAAK;QACLC,OAAO;QACPC,MAAM;QACNC,MAAM;QACNE,KAAK;YAAEJ,OAAO;YAAkBK,aAAa;QAAiE;IAChH;IACA;QACEN,KAAK;QACLC,OAAO;QACPC,MAAM;QACNC,MAAM;QACNE,KAAK;YAAEJ,OAAO;YAAiBK,aAAa;QAAoD;IAClG;CACD,CAAA;AAED,kDAAkD,GAClD,OAAO,MAAMC,4BAA4B,6CAA4C;AAErF,gFAAgF,GAChF,OAAO,MAAMC,2BACX,oHAAmH;AAErH,6GAA6G,GAC7G,MAAMC,iBAAiB;AAEvB,6DAA6D,GAC7D,MAAMC,UAAU,CAACC,SAA4C,GAAGA,OAAOT,IAAI,CAACU,OAAO,CAAC,WAAW,KAAK,CAAC,EAAED,OAAOV,KAAK,EAAE;AAErH,MAAMY,eAAe,CAACX,OAAiBA,KAAKY,IAAI,GAAGF,OAAO,CAAC,QAAQ,IAAIG,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,WAAW;AAkBhG;;;;CAIC,GACD,OAAO,SAASC,mBAAmBC,OAAyE;IAC1G,MAAMC,QAAQD,QAAQE,MAAM,CAAC,CAACT,SAAW,CAACA,OAAOU,MAAM,IAAIR,aAAaF,OAAOT,IAAI,MAAM;IACzF,OAAOH,eAAeuB,GAAG,CAAC,CAACC;YAEvBJ;QADF,MAAMK,SACJL,cAAAA,MAAMM,IAAI,CAAC,CAACd,SAAWE,aAAaF,OAAOT,IAAI,MAAMqB,KAAKrB,IAAI,aAA9DiB,cACCI,KAAKvB,GAAG,KAAK,aAAamB,MAAMM,IAAI,CAAC,CAACd,SAAWF,eAAeiB,IAAI,CAAChB,QAAQC,YAAYgB;QAC5F,IAAIH,OAAO;YACT,MAAMtB,OAAOW,aAAaW,MAAMtB,IAAI;YACpC,OAAO;gBAAEF,KAAKuB,KAAKvB,GAAG;gBAAEC,OAAOsB,KAAKtB,KAAK;gBAAEC;gBAAM0B,MAAM,CAAC,CAAC,EAAE1B,MAAM;gBAAEC,MAAMqB,MAAMvB,KAAK,CAACa,IAAI,MAAMS,KAAKpB,IAAI;gBAAE0B,SAASL,MAAMvB,KAAK;YAAC;QACjI;QACA,OAAO;YAAED,KAAKuB,KAAKvB,GAAG;YAAEC,OAAOsB,KAAKtB,KAAK;YAAEC,MAAMqB,KAAKrB,IAAI;YAAE0B,MAAM,CAAC,CAAC,EAAEL,KAAKrB,IAAI,EAAE;YAAEC,MAAMoB,KAAKpB,IAAI;QAAC;IACrG;AACF;AAEA,6EAA6E,GAC7E,OAAO,SAAS2B,oBAAoBX,KAAiC;IACnE,OAAOA,MAAMC,MAAM,CAAC,CAACG,OAAS,CAACA,KAAKM,OAAO;AAC7C;AAEA,kEAAkE,GAClE,OAAO,MAAME,4BAA4B,aAAY;AAUrD,sGAAsG,GACtG,OAAO,SAASC,kBAAkBb,KAAiC;;IACjE,MAAMc,UAAUd,MAAMM,IAAI,CAAC,CAACF,OAASA,KAAKvB,GAAG,KAAK;IAClD,OAAO;QACLiC,OAAO,UAAEA,2BAAAA,QAASL,IAAI,mBAAI;QAC1BM,QAAQf,MAAMC,MAAM,CAAC,CAACG,OAASA,KAAKvB,GAAG,KAAK,QAAQsB,GAAG,CAAC,CAACC,OAAU,CAAA;gBAAEY,OAAOZ,KAAKpB,IAAI;gBAAEyB,MAAML,KAAKK,IAAI;YAAC,CAAA;IACzG;AACF;AAEA,MAAMQ,OAAO;AAEb,6EAA6E,GAC7E,OAAO,SAASC,oBAAoBC,MAA4D;IAC9F,MAAMC,MAAMD,0BAAAA,MAAQ,CAACP,0BAA0B;IAC/C,IAAI,CAACQ,OAAO,OAAOA,QAAQ,UAAU,OAAO;IAC5C,MAAM,EAAEN,OAAO,EAAEC,MAAM,EAAE,GAAGK;IAC5B,MAAMC,QAAQC,MAAMC,OAAO,CAACR,UACxBA,OAAOS,OAAO,CAAC,CAACC;QACd,MAAM,EAAET,KAAK,EAAEP,IAAI,EAAE,GAAIgB,gBAAAA,QAAS,CAAC;QACnC,OAAO,OAAOT,UAAU,YAAYA,MAAMrB,IAAI,MAAM,OAAOc,SAAS,YAAYQ,KAAKV,IAAI,CAACE,QACtF;YAAC;gBAAEO,OAAOA,MAAMrB,IAAI,GAAG+B,KAAK,CAAC,GAAG;gBAAKjB;YAAK;SAAE,GAC5C,EAAE;IACR,KACA,EAAE;IACN,MAAMkB,cAAc,OAAOb,YAAY,YAAYG,KAAKV,IAAI,CAACO,WAAWA,UAAU;IAClF,OAAOa,eAAeN,MAAMO,MAAM,GAAG;QAAEd,SAASa;QAAaZ,QAAQM,MAAMK,KAAK,CAAC,GAAG;IAAG,IAAI;AAC7F;AAEA,6EAA6E;AAE7E,6EAA6E,GAC7E,MAAMG,OAAO;AAEb,6EAA6E,GAC7E,OAAO,MAAMC,wBAAwB,KAAI;AAEzC,2HAA2H,GAC3H,OAAO,MAAMC,oBAAoB;IAC/BjB,SAAS;IACTkB,UAAU;IACVC,MAAM;AACR,EAAU;AAEV,oFAAoF,GACpF,MAAMC,OAAO;AAuBb,wGAAwG,GACxG,SAASC,QAAQC,KAAuB,EAAEC,QAAQ,YAAY;IAC5D,OAAOD,MAAME,WAAW,GAAG,CAAC,CAAC,EAAED,MAAM,EAAE,EAAED,MAAME,WAAW,CAAC,CAAC,CAAC,GAAG,GAAGD,MAAM,gCAAgC,CAAC;AAC5G;AAEA,gFAAgF,GAChF,OAAO,SAASE,sBAAsB1D,GAAqC,EAAEuD,KAAuB;IAClG,IAAIvD,QAAQ,YAAY;QACtB,OAAO;YACL;YACA,CAAC,kSAAkS,EAAEsD,QAAQC,OAAO,qCAAqC,CAAC;YAC1V;YACA,CAAC,uFAAuF,EAAED,QAAQC,OAAO,+FAA+F,CAAC;SAC1M;IACH;IACA,IAAIvD,QAAQ,WAAW;QACrB,OAAO;YACL,CAAC,iBAAiB,EAAEqD,KAAK,qLAAqL,CAAC;YAC/M;YACA;YACA;YACA;YACA,CAAC,4FAA4F,EAAEC,QAAQC,OAAO,mKAAmK,CAAC;SACnR;IACH;IACA,OAAO;QACL,CAAC,4DAA4D,EAAEF,KAAK,0HAA0H,CAAC;QAC/L;QACA;QACA,CAAC,0HAA0H,EAAEE,MAAMI,YAAY,CAAC,OAAO,CAAC;QACxJ;QACA,CAAC,gMAAgM,EAAEL,QAAQC,OAAO,cAAc,uBAAuB,CAAC;KACzP;AACH;AAEA,wFAAwF,GACxF,OAAO,SAASK,sBAAsBC,QAA2B,EAAEC,MAAMb,qBAAqB;IAC5F,MAAMc,SAAmB,EAAE;IAC3B,KAAK,MAAMC,WAAWH,SAAU;QAC9B,MAAMI,OAAOF,MAAM,CAACA,OAAOhB,MAAM,GAAG,EAAE;QACtC,IAAIkB,SAAStC,aAAasC,KAAKlB,MAAM,GAAG,IAAIiB,QAAQjB,MAAM,IAAIe,KAAKC,MAAM,CAACA,OAAOhB,MAAM,GAAG,EAAE,GAAG,GAAGkB,KAAK,IAAI,EAAED,SAAS;aACjHD,OAAOG,IAAI,CAACF,QAAQnB,KAAK,CAAC,GAAGiB;IACpC;IACA,OAAOC;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASI,iBAAiBnE,GAAmB,EAAEuD,KAAuB;IAC3E,MAAMhC,OAAOxB,eAAe0B,IAAI,CAAC,CAAC2C,MAAQA,IAAIpE,GAAG,KAAKA;IACtD,MAAMqE,QAAoC,CAAC;IAC3C,MAAMC,KAAK,CAACC,OAAiB,CAAC,MAAM,EAAEvE,IAAI,CAAC,EAAEuE,MAAM;IACnD,MAAMC,YAAYF,GAAG;IACrB,MAAMG,MAAM,CAACF,MAAcG,aAAqBC,UAAkBC,OAAgCC;QAChG,MAAMC,SAASR,GAAGC;QAClBF,KAAK,CAACS,OAAO,GAAG;YAAEC,KAAKD;YAAQE,MAAM;YAAQN;YAAaC;YAAUM,UAAUT;YAAWH,OAAO,EAAE;YAAEO;WAAWC,KAAK;YAAEA;QAAG,IAAI,CAAC;QAC9H,OAAOC;IACT;IACA,MAAMI,WAAqB;QAACT,IAAI,SAAS,iBAAiB,OAAO;YAAES,UAAU3D,KAAKtB,KAAK;YAAEkF,SAAS;YAAMC,WAAW;QAAK,GAAG;YAAEC,IAAI;QAAE;KAAG;IACtI,IAAIrF,QAAQ,WAAW;QACrBkF,SAAShB,IAAI,CACXO,IAAI,WAAWvB,kBAAkBjB,OAAO,EAAE,YAAY;YAAEqD,kBAAkB;QAA+B,IACzGb,IACE,SACA,iBACA,OACA;YAAES,UAAU;YAAgGC,SAAS;YAASC,WAAW;QAAI,GAC7I;YAAEG,OAAO;YAAkBC,IAAI;QAAE,IAEnCf,IACE,SACAvB,kBAAkBC,QAAQ,EAC1B,YACA;YAAEsC,SAAS;YAAeC,WAAW;QAA8D,GACnG;YAAEF,IAAI;QAAE;IAGd,OAAO,IAAIxF,QAAQ,QAAQ;QACzBkF,SAAShB,IAAI,CACXO,IAAI,QAAQvB,kBAAkBE,IAAI,EAAE,YAAY;YAAE+B,SAAS;YAAUQ,YAAY;QAAK,IACtFlB,IAAI,QAAQ,aAAa,OAAO;YAAES,UAAU;YAAqBC,SAAS;YAAYvD,MAAM2B,MAAMqC,QAAQ;QAAC,GAAG;YAAEJ,IAAI;QAAE;IAE1H,OAAO;QACL5B,sBAAsBF,sBAAsB1D,KAAKuD,QAAQsC,OAAO,CAAC,CAACC,SAASC;YACzEb,SAAShB,IAAI,CAACO,IAAI,CAAC,IAAI,EAAEsB,QAAQ,GAAG,EAAE,YAAY,OAAO;gBAAED;YAAQ;QACrE;IACF;IACA,MAAM9B,UAAUM,GAAG;IACnBD,KAAK,CAACrB,KAAK,GAAG;QAAE+B,KAAK/B;QAAMgC,MAAM;QAAQN,aAAa;QAAOC,UAAU;QAAOM,UAAU;QAAMZ,OAAO;YAACL;SAAQ;IAAC;IAC/GK,KAAK,CAACL,QAAQ,GAAG;QACfe,KAAKf;QACLgB,MAAM;QACNN,aAAa;QACbC,UAAU;QACVM,UAAUjC;QACVqB,OAAO;YAACG;SAAU;QAClBI,OAAO;YAAEoB,SAAS;YAAWC,WAAW1E,KAAKtB,KAAK;QAAC;IACrD;IACAoE,KAAK,CAACG,UAAU,GAAG;QACjBO,KAAKP;QACLQ,MAAM;QACNN,aAAa;QACbC,UAAU;QACVM,UAAUjB;QACVK,OAAOa;QACPN,OAAO;YAAEsB,UAAUlG,QAAQ,aAAaA,QAAQ,SAAS,OAAO;QAAK;QACrE6E,IAAI;YAAEsB,IAAI;gBAAEC,IAAI;gBAAGC,IAAI;YAAG;QAAE;IAC9B;IACA,OAAOhC;AACT;AAkBA;;;;;CAKC,GACD,OAAO,MAAMiC,wBAAsD;IACjE;QACEhC,IAAI;QACJrE,OAAO;QACPsG,MAAM;QACNC,OAAO;QACPC,QAAQ;IACV;IACA;QACEnC,IAAI;QACJrE,OAAO;QACPsG,MAAM;QACNC,OAAO;QACPC,QAAQ;IACV;IACA;QACEnC,IAAI;QACJrE,OAAO;QACPsG,MAAM;QACNC,OAAO;QACPC,QAAQ;IACV;IACA;QACEnC,IAAI;QACJrE,OAAO;QACPsG,MAAM;QACNC,OAAO;QACPC,QAAQ;IACV;CACD,CAAA"}