@businessdash/sdk 0.9.60 → 0.9.80
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 +991 -295
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +622 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +888 -1
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +107 -0
- package/dist/consent-core/index.d.ts +103 -0
- package/dist/consent-core/index.d.ts.map +1 -0
- package/dist/consent-core/index.js +157 -0
- package/dist/consent-core/index.js.map +1 -0
- package/dist/contracts.d.ts +3526 -767
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +571 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1355 -24
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4516 -1311
- package/package.json +128 -11
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +1097 -0
- package/src/consent-core/index.ts +213 -0
- package/src/contracts.ts +768 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- package/src/vue/index.ts +3 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/seed` — define a schema, push it, and seed data, from any
|
|
3
|
+
* framework's build step.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists alongside the CLI
|
|
6
|
+
*
|
|
7
|
+
* `businessdash sync-data-model` and friends already do this, and they are the
|
|
8
|
+
* right tool at a terminal. They are the wrong tool everywhere else: a Nuxt
|
|
9
|
+
* module, a SvelteKit prerender hook, an Angular builder, a Vite plugin, a
|
|
10
|
+
* Next `instrumentation.ts` or a CI step that already has the credentials
|
|
11
|
+
* loaded cannot shell out to an interactive command and read its output.
|
|
12
|
+
*
|
|
13
|
+
* So the same operations are exposed as plain async functions. Nothing here is
|
|
14
|
+
* framework-specific, because seeding happens at BUILD time — there is no
|
|
15
|
+
* reactivity to bridge, only an integration point per framework, and every
|
|
16
|
+
* framework has one that can call a function.
|
|
17
|
+
*
|
|
18
|
+
* The CLI imports this module rather than the other way round, so there is one
|
|
19
|
+
* implementation of the transport, the auth header and the error handling.
|
|
20
|
+
*
|
|
21
|
+
* ## What can and cannot be seeded
|
|
22
|
+
*
|
|
23
|
+
* The package API decides this, not the SDK:
|
|
24
|
+
*
|
|
25
|
+
* | Target | Seedable | Endpoint |
|
|
26
|
+
* |-----------------------|----------|----------|
|
|
27
|
+
* | Custom-database schema| yes | `data-model/plan` + `draft-schema` |
|
|
28
|
+
* | Custom-database records| yes | `data-model/records` |
|
|
29
|
+
* | Static collections | yes | `collections`, `collections/{slug}/rows` |
|
|
30
|
+
* | Coupons | yes | `coupons` |
|
|
31
|
+
* | Products (+ variants) | yes | `storefront/products` |
|
|
32
|
+
* | Blog posts | yes | `blog/posts` |
|
|
33
|
+
*
|
|
34
|
+
* Products and blog posts were absent from the first version of this module
|
|
35
|
+
* because the package API had no create route for either — the dashboard could
|
|
36
|
+
* do it and the SDK could not. Both routes now exist, so a site can be seeded
|
|
37
|
+
* whole: schema, records, collections, catalogue, content and coupons.
|
|
38
|
+
*
|
|
39
|
+
* ## Pushing a schema does not apply it
|
|
40
|
+
*
|
|
41
|
+
* `pushDataModel` writes the DRAFT slot and returns the plan describing what
|
|
42
|
+
* promoting it would do. Promotion is a separate, deliberate step in the
|
|
43
|
+
* dashboard. That is the platform's design and this module does not route
|
|
44
|
+
* around it: an automated deploy that silently altered a live customer
|
|
45
|
+
* database would be the wrong default.
|
|
46
|
+
*
|
|
47
|
+
* @module
|
|
48
|
+
*/
|
|
49
|
+
function normalizeBaseUrl(raw) {
|
|
50
|
+
return raw.replace(/\/+$/, "");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One request to the package API, shared by every function here AND by the CLI.
|
|
54
|
+
*
|
|
55
|
+
* Errors carry the server's own message when it sent one. A seeder that failed
|
|
56
|
+
* with `HTTP 403` and nothing else would send someone hunting through their
|
|
57
|
+
* own code for a problem the API already described.
|
|
58
|
+
*/
|
|
59
|
+
export async function callPackageApi(target, input) {
|
|
60
|
+
const url = `${normalizeBaseUrl(target.baseUrl)}/${input.path}`;
|
|
61
|
+
const res = await fetch(url, {
|
|
62
|
+
method: input.method,
|
|
63
|
+
headers: {
|
|
64
|
+
"content-type": "application/json",
|
|
65
|
+
authorization: `Bearer ${target.apiKey}`,
|
|
66
|
+
},
|
|
67
|
+
...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),
|
|
68
|
+
});
|
|
69
|
+
const text = await res.text();
|
|
70
|
+
let parsed = null;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(text);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
parsed = null;
|
|
76
|
+
}
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
const message = typeof parsed?.message === "string"
|
|
79
|
+
? parsed.message
|
|
80
|
+
: `HTTP ${res.status} ${res.statusText} — ${text.slice(0, 240)}`;
|
|
81
|
+
throw new Error(`${input.method} ${input.path}: ${message}`);
|
|
82
|
+
}
|
|
83
|
+
if (!parsed) {
|
|
84
|
+
throw new Error(`${input.method} ${input.path}: server replied ${res.status} but the body wasn't JSON — first 240 chars: ${text.slice(0, 240)}`);
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
/** Data-model routes are all under the site's data-model namespace. */
|
|
89
|
+
function dataModelPath(target, suffix) {
|
|
90
|
+
return `sites/${encodeURIComponent(target.siteId)}/data-model/${suffix}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Plan, then push a schema into the DRAFT slot.
|
|
94
|
+
*
|
|
95
|
+
* The plan is always computed, even when pushing, because a push whose
|
|
96
|
+
* consequences nobody looked at is how a destructive migration reaches a live
|
|
97
|
+
* database. The returned `plan.changes` carry a `class` — `safe`, `backfill`,
|
|
98
|
+
* `destructive` or `exposure` — which is what a CI gate should branch on.
|
|
99
|
+
*/
|
|
100
|
+
export async function pushDataModel(target, schema, options = {}) {
|
|
101
|
+
const planRes = await callPackageApi(target, {
|
|
102
|
+
path: dataModelPath(target, "plan"),
|
|
103
|
+
method: "POST",
|
|
104
|
+
body: { schema, prune: options.prune ?? false },
|
|
105
|
+
});
|
|
106
|
+
const plan = (planRes.plan ?? { changes: [] });
|
|
107
|
+
if (options.dryRun) {
|
|
108
|
+
return { plan, version: null, noop: false, planOnly: true };
|
|
109
|
+
}
|
|
110
|
+
const res = await callPackageApi(target, {
|
|
111
|
+
path: dataModelPath(target, "draft-schema"),
|
|
112
|
+
method: "POST",
|
|
113
|
+
body: {
|
|
114
|
+
schema,
|
|
115
|
+
...(options.checksum ? { checksum: options.checksum } : {}),
|
|
116
|
+
note: options.note ?? null,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
plan,
|
|
121
|
+
version: typeof res.version === "number" ? res.version : null,
|
|
122
|
+
noop: res.noop === true,
|
|
123
|
+
planOnly: false,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Does this plan contain anything that would lose or expose data? */
|
|
127
|
+
export function planHasDangerousChanges(plan) {
|
|
128
|
+
return (plan.changes ?? []).some((c) => c.class === "destructive" || c.class === "exposure");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Seed rows into the CUSTOM DATABASE (the metadata engine).
|
|
132
|
+
*
|
|
133
|
+
* This is not the same store as `seedCollectionRows` below. The custom database
|
|
134
|
+
* is the schema you defined and promoted; static collections are a simpler
|
|
135
|
+
* key/value-ish store on the site. Mixing them up is easy and the failure is
|
|
136
|
+
* silent — the rows land somewhere real, just not where they were wanted — so
|
|
137
|
+
* the two functions are named for the store rather than for the verb.
|
|
138
|
+
*/
|
|
139
|
+
export async function seedRecords(target, records) {
|
|
140
|
+
const res = await callPackageApi(target, {
|
|
141
|
+
path: dataModelPath(target, "records"),
|
|
142
|
+
method: "POST",
|
|
143
|
+
body: { records },
|
|
144
|
+
});
|
|
145
|
+
const written = (res.written ?? {});
|
|
146
|
+
return { written };
|
|
147
|
+
}
|
|
148
|
+
/** Create a STATIC collection on the site. Idempotent by slug server-side. */
|
|
149
|
+
export async function seedCollection(target, definition) {
|
|
150
|
+
return callPackageApi(target, {
|
|
151
|
+
path: `sites/${encodeURIComponent(target.siteId)}/collections`,
|
|
152
|
+
method: "POST",
|
|
153
|
+
body: definition,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Insert rows into a static collection.
|
|
158
|
+
*
|
|
159
|
+
* Rows go one request each because the route takes one row — batching would
|
|
160
|
+
* mean inventing a bulk endpoint that does not exist. `concurrency` keeps a
|
|
161
|
+
* large seed from opening hundreds of sockets at once.
|
|
162
|
+
*/
|
|
163
|
+
export async function seedCollectionRows(target, slug, rows, options = {}) {
|
|
164
|
+
const concurrency = Math.max(1, options.concurrency ?? 4);
|
|
165
|
+
const path = `sites/${encodeURIComponent(target.siteId)}/collections/${encodeURIComponent(slug)}/rows`;
|
|
166
|
+
let written = 0;
|
|
167
|
+
for (let i = 0; i < rows.length; i += concurrency) {
|
|
168
|
+
const batch = rows.slice(i, i + concurrency);
|
|
169
|
+
await Promise.all(batch.map(async (row) => {
|
|
170
|
+
await callPackageApi(target, { path, method: "POST", body: row });
|
|
171
|
+
written += 1;
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
return { written };
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create products, each with its variants and cross-variants.
|
|
178
|
+
*
|
|
179
|
+
* The whole tree goes in ONE request per product, because the route writes it
|
|
180
|
+
* in a transaction — a seed that created a product and then failed partway
|
|
181
|
+
* through its variants would leave a catalogue nobody could trust.
|
|
182
|
+
*
|
|
183
|
+
* Products land unpublished unless `isLive` says otherwise, and are NOT pushed
|
|
184
|
+
* to Stripe: that is a separate deliberate call, because it mints objects in an
|
|
185
|
+
* account the org is billed against.
|
|
186
|
+
*/
|
|
187
|
+
export async function seedProducts(target, products) {
|
|
188
|
+
const results = [];
|
|
189
|
+
for (const product of products) {
|
|
190
|
+
const res = await callPackageApi(target, {
|
|
191
|
+
path: "storefront/products",
|
|
192
|
+
method: "POST",
|
|
193
|
+
body: product,
|
|
194
|
+
});
|
|
195
|
+
results.push({
|
|
196
|
+
name: product.name,
|
|
197
|
+
id: String(res.id ?? ""),
|
|
198
|
+
variantCount: Number(res.variantCount ?? 0),
|
|
199
|
+
crossVariantCount: Number(res.crossVariantCount ?? 0),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create blog posts.
|
|
206
|
+
*
|
|
207
|
+
* An existing slug is REPORTED rather than thrown, on the same reasoning as
|
|
208
|
+
* coupons: re-running a seed must be safe, and a conflict on the third post
|
|
209
|
+
* must not abandon the remaining seven.
|
|
210
|
+
*/
|
|
211
|
+
export async function seedBlogPosts(target, posts) {
|
|
212
|
+
const results = [];
|
|
213
|
+
for (const post of posts) {
|
|
214
|
+
try {
|
|
215
|
+
const res = await callPackageApi(target, {
|
|
216
|
+
path: "blog/posts",
|
|
217
|
+
method: "POST",
|
|
218
|
+
body: post,
|
|
219
|
+
});
|
|
220
|
+
results.push({
|
|
221
|
+
slug: post.slug,
|
|
222
|
+
created: true,
|
|
223
|
+
published: res.published === true,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
catch (cause) {
|
|
227
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
228
|
+
if (/already exists|conflict/i.test(message)) {
|
|
229
|
+
results.push({
|
|
230
|
+
slug: post.slug,
|
|
231
|
+
created: false,
|
|
232
|
+
published: false,
|
|
233
|
+
message: "already exists — left alone",
|
|
234
|
+
});
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
throw cause;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return results;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Create coupons in the org's own Stripe account as well as locally.
|
|
244
|
+
*
|
|
245
|
+
* An existing code is REPORTED, not treated as a failure: re-running a seed
|
|
246
|
+
* should be safe, and a 409 on the third of ten coupons must not abandon the
|
|
247
|
+
* remaining seven. Anything else still throws — a bad key or a malformed
|
|
248
|
+
* coupon is a real problem and swallowing it would leave a half-seeded store
|
|
249
|
+
* looking successful.
|
|
250
|
+
*/
|
|
251
|
+
export async function seedCoupons(target, coupons) {
|
|
252
|
+
const results = [];
|
|
253
|
+
for (const coupon of coupons) {
|
|
254
|
+
try {
|
|
255
|
+
const res = await callPackageApi(target, {
|
|
256
|
+
path: "coupons",
|
|
257
|
+
method: "POST",
|
|
258
|
+
body: {
|
|
259
|
+
...coupon,
|
|
260
|
+
...(coupon.validFrom
|
|
261
|
+
? {
|
|
262
|
+
validFrom: coupon.validFrom instanceof Date
|
|
263
|
+
? coupon.validFrom.toISOString()
|
|
264
|
+
: coupon.validFrom,
|
|
265
|
+
}
|
|
266
|
+
: {}),
|
|
267
|
+
...(coupon.validUntil
|
|
268
|
+
? {
|
|
269
|
+
validUntil: coupon.validUntil instanceof Date
|
|
270
|
+
? coupon.validUntil.toISOString()
|
|
271
|
+
: coupon.validUntil,
|
|
272
|
+
}
|
|
273
|
+
: {}),
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
results.push({
|
|
277
|
+
code: coupon.code,
|
|
278
|
+
created: true,
|
|
279
|
+
stripeSynced: res.stripeSynced === true,
|
|
280
|
+
...(typeof res.stripeError === "string" && res.stripeError
|
|
281
|
+
? { message: res.stripeError }
|
|
282
|
+
: {}),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (cause) {
|
|
286
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
287
|
+
// The route answers 409 for a code that already exists.
|
|
288
|
+
if (/already exists|conflict/i.test(message)) {
|
|
289
|
+
results.push({
|
|
290
|
+
code: coupon.code,
|
|
291
|
+
created: false,
|
|
292
|
+
stripeSynced: false,
|
|
293
|
+
message: "already exists — left alone",
|
|
294
|
+
});
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
throw cause;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return results;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Run a whole seed, in dependency order: schema first, then the records that
|
|
304
|
+
* depend on it, then static collections, then products, posts and coupons.
|
|
305
|
+
*
|
|
306
|
+
* Order matters and is not configurable — seeding records into a collection
|
|
307
|
+
* whose schema has not been promoted yet fails in a way that reads as a
|
|
308
|
+
* permissions problem.
|
|
309
|
+
*/
|
|
310
|
+
export async function runSeed(target, seed, options = {}) {
|
|
311
|
+
const log = options.onProgress ?? (() => { });
|
|
312
|
+
const failOnDangerous = options.failOnDangerousPlan ?? true;
|
|
313
|
+
const result = {};
|
|
314
|
+
if (seed.dataModel) {
|
|
315
|
+
// `DataModelArtifact` is a Record, so `"toJSONSchema" in x` narrows the
|
|
316
|
+
// union to the record branch rather than the schema branch — the property
|
|
317
|
+
// check has to be on the VALUE being callable, not on the key existing.
|
|
318
|
+
const candidate = seed.dataModel;
|
|
319
|
+
const artifact = typeof candidate.toJSONSchema === "function"
|
|
320
|
+
? candidate.toJSONSchema()
|
|
321
|
+
: seed.dataModel;
|
|
322
|
+
log("Pushing data model…");
|
|
323
|
+
const pushed = await pushDataModel(target, artifact, seed.dataModelOptions);
|
|
324
|
+
result.dataModel = pushed;
|
|
325
|
+
if (failOnDangerous && planHasDangerousChanges(pushed.plan)) {
|
|
326
|
+
const summary = pushed.plan.changes
|
|
327
|
+
.filter((c) => c.class === "destructive" || c.class === "exposure")
|
|
328
|
+
.map((c) => ` ${c.class}: ${c.summary}`)
|
|
329
|
+
.join("\n");
|
|
330
|
+
throw new Error(`The schema plan contains changes that would lose or expose data:\n${summary}\n\nThe draft was ${pushed.planOnly ? "not uploaded" : "uploaded but NOT promoted"}. Review and promote it deliberately, or pass \`failOnDangerousPlan: false\` if you have already decided.`);
|
|
331
|
+
}
|
|
332
|
+
log(pushed.planOnly
|
|
333
|
+
? "Plan only — nothing uploaded."
|
|
334
|
+
: pushed.noop
|
|
335
|
+
? "Data model unchanged."
|
|
336
|
+
: `Draft v${pushed.version} uploaded (promote it in the dashboard to apply).`);
|
|
337
|
+
}
|
|
338
|
+
if (seed.records) {
|
|
339
|
+
log("Seeding custom-database records…");
|
|
340
|
+
result.records = await seedRecords(target, seed.records);
|
|
341
|
+
const total = Object.values(result.records.written).reduce((a, b) => a + b, 0);
|
|
342
|
+
log(`Wrote ${total} record(s).`);
|
|
343
|
+
}
|
|
344
|
+
if (seed.collections?.length) {
|
|
345
|
+
result.collections = [];
|
|
346
|
+
for (const definition of seed.collections) {
|
|
347
|
+
const { rows, ...collectionInput } = definition;
|
|
348
|
+
log(`Creating static collection "${collectionInput.name}"…`);
|
|
349
|
+
const created = await seedCollection(target, collectionInput);
|
|
350
|
+
const slug = (typeof created.slug === "string" ? created.slug : undefined) ??
|
|
351
|
+
collectionInput.slug ??
|
|
352
|
+
collectionInput.name;
|
|
353
|
+
let rowsWritten = 0;
|
|
354
|
+
if (rows?.length) {
|
|
355
|
+
rowsWritten = (await seedCollectionRows(target, slug, rows)).written;
|
|
356
|
+
log(` ${rowsWritten} row(s).`);
|
|
357
|
+
}
|
|
358
|
+
result.collections.push({ slug, rowsWritten });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (seed.products?.length) {
|
|
362
|
+
log(`Seeding ${seed.products.length} product(s)…`);
|
|
363
|
+
result.products = await seedProducts(target, seed.products);
|
|
364
|
+
const variants = result.products.reduce((a, p) => a + p.variantCount, 0);
|
|
365
|
+
log(`${result.products.length} product(s), ${variants} variant(s).`);
|
|
366
|
+
}
|
|
367
|
+
if (seed.posts?.length) {
|
|
368
|
+
log(`Seeding ${seed.posts.length} blog post(s)…`);
|
|
369
|
+
result.posts = await seedBlogPosts(target, seed.posts);
|
|
370
|
+
const created = result.posts.filter((p) => p.created).length;
|
|
371
|
+
log(`${created} created, ${result.posts.length - created} already existed.`);
|
|
372
|
+
}
|
|
373
|
+
if (seed.coupons?.length) {
|
|
374
|
+
log(`Seeding ${seed.coupons.length} coupon(s)…`);
|
|
375
|
+
result.coupons = await seedCoupons(target, seed.coupons);
|
|
376
|
+
const created = result.coupons.filter((c) => c.created).length;
|
|
377
|
+
log(`${created} created, ${result.coupons.length - created} already existed.`);
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Read a seed target from environment variables.
|
|
383
|
+
*
|
|
384
|
+
* The names match what the CLI already reads, so a project that has the CLI
|
|
385
|
+
* working needs no new configuration to call these functions from a build step.
|
|
386
|
+
*/
|
|
387
|
+
export function seedTargetFromEnv(env = typeof process === "undefined"
|
|
388
|
+
? {}
|
|
389
|
+
: process.env) {
|
|
390
|
+
// BIAB_API_KEY first, because that is the name the CLI reads and the name
|
|
391
|
+
// every starter's .env.example sets. Reading a different name would mean a
|
|
392
|
+
// project whose CLI works fine still fails here, with an error naming a
|
|
393
|
+
// variable the consumer has never seen.
|
|
394
|
+
const apiKey = env.BIAB_API_KEY ?? env.BUSINESSDASH_API_KEY ?? env.BIAB_PACKAGE_API_KEY;
|
|
395
|
+
const siteId = env.BIAB_SITE_ID ?? env.BUSINESSDASH_SITE_ID;
|
|
396
|
+
const baseUrl = env.BIAB_PACKAGE_API_BASE_URL ??
|
|
397
|
+
env.BUSINESSDASH_API_BASE_URL ??
|
|
398
|
+
env.NEXT_PUBLIC_BIAB_PACKAGE_API_BASE_URL;
|
|
399
|
+
const missing = [
|
|
400
|
+
!apiKey && "BIAB_API_KEY",
|
|
401
|
+
!siteId && "BIAB_SITE_ID",
|
|
402
|
+
!baseUrl && "BIAB_PACKAGE_API_BASE_URL",
|
|
403
|
+
].filter(Boolean);
|
|
404
|
+
if (missing.length > 0) {
|
|
405
|
+
throw new Error(`Missing ${missing.join(", ")}. Seeding needs a package API key, a site id and the API base URL — the same three the CLI reads from .env.local.`);
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
apiKey: apiKey,
|
|
409
|
+
siteId: siteId,
|
|
410
|
+
baseUrl: baseUrl,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Type-only helper for a `businessdash.seed.ts`, so a config file gets
|
|
415
|
+
* completion and checking without importing anything at runtime.
|
|
416
|
+
*/
|
|
417
|
+
export function defineSeed(seed) {
|
|
418
|
+
return seed;
|
|
419
|
+
}
|
|
420
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seed-core/index.ts"],"names":[],"mappings":"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"}
|
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,sBAAsB,CAAC"}
|
package/dist/seed.js
ADDED
package/dist/seed.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO for blog posts and blog listings.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a post needs more than a title
|
|
5
|
+
*
|
|
6
|
+
* A `BlogPosting` node is what turns a link in search results into a dated,
|
|
7
|
+
* attributed article card. Without it a post competes as a bare URL, and the
|
|
8
|
+
* publish date — the thing readers use to decide whether it is still true —
|
|
9
|
+
* never appears.
|
|
10
|
+
*
|
|
11
|
+
* ## Gated posts stay indexed, and say so
|
|
12
|
+
*
|
|
13
|
+
* A subscriber-only post should still be findable: being discoverable is how
|
|
14
|
+
* anyone subscribes. But serving a full article to a crawler and a paywall to
|
|
15
|
+
* a reader is **cloaking**, which is penalised.
|
|
16
|
+
*
|
|
17
|
+
* The correct answer is schema.org's paywall markup — `isAccessibleForFree:
|
|
18
|
+
* false` plus a `hasPart` naming the gated region. That tells a crawler the
|
|
19
|
+
* content is restricted, so the difference between what it sees and what a
|
|
20
|
+
* reader sees is declared rather than deceptive.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import type { SeoInput } from "./index.js";
|
|
25
|
+
/** A blog post, as it arrives from the platform. */
|
|
26
|
+
export type BlogPostSeoSource = {
|
|
27
|
+
title: string;
|
|
28
|
+
slug: string;
|
|
29
|
+
excerpt?: string | null;
|
|
30
|
+
imageUrl?: string | null;
|
|
31
|
+
publishedAt?: string | null;
|
|
32
|
+
updatedAt?: string | null;
|
|
33
|
+
authorName?: string | null;
|
|
34
|
+
tags?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* `public` is open. Anything else is gated — the platform writes both
|
|
37
|
+
* `members`/`subscribers` and `followers`/`paid` into this column, so
|
|
38
|
+
* anything that is not `public` is treated as restricted.
|
|
39
|
+
*/
|
|
40
|
+
accessLevel?: string | null;
|
|
41
|
+
};
|
|
42
|
+
export type BlogPostSeoInput = {
|
|
43
|
+
siteUrl: string;
|
|
44
|
+
post: BlogPostSeoSource;
|
|
45
|
+
/** Path of this post, e.g. `/blog/my-post`. Defaults to `/blog/{slug}`. */
|
|
46
|
+
path?: string;
|
|
47
|
+
/** Extra crumbs between Home and the post. */
|
|
48
|
+
breadcrumbs?: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
url: string;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* CSS selector wrapping the gated body, for the paywall markup.
|
|
54
|
+
*
|
|
55
|
+
* Defaults to `.biab-post-body`. It only has to match the element your
|
|
56
|
+
* template actually hides — a selector that matches nothing makes the
|
|
57
|
+
* markup a claim you are not backing up.
|
|
58
|
+
*/
|
|
59
|
+
paywallSelector?: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* SEO for one blog post.
|
|
63
|
+
*
|
|
64
|
+
* ```ts
|
|
65
|
+
* const seo = resolveSeo(layoutDefaults, blogPostSeo({ siteUrl, post }))
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function blogPostSeo(input: BlogPostSeoInput): SeoInput;
|
|
69
|
+
export type BlogListingSeoInput = {
|
|
70
|
+
siteUrl: string;
|
|
71
|
+
/** Path of the listing, e.g. `/blog` or `/blog/tag/plumbing`. */
|
|
72
|
+
path?: string;
|
|
73
|
+
title?: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
posts: readonly BlogPostSeoSource[];
|
|
76
|
+
/** Path builder, when posts do not live at `/blog/{slug}`. */
|
|
77
|
+
postPath?: (post: BlogPostSeoSource) => string;
|
|
78
|
+
/**
|
|
79
|
+
* Page number, for paginated archives.
|
|
80
|
+
*
|
|
81
|
+
* Pages 2+ are marked `noindex, follow` by convention: the individual posts
|
|
82
|
+
* are what should rank, and thin archive pages competing with them splits
|
|
83
|
+
* the signal. Links are still followed so the posts stay reachable.
|
|
84
|
+
*/
|
|
85
|
+
page?: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* SEO for a blog index or archive.
|
|
89
|
+
*
|
|
90
|
+
* Emits a `Blog` node with an `ItemList` of the posts on this page, so a
|
|
91
|
+
* crawler can see the structure without fetching every post.
|
|
92
|
+
*/
|
|
93
|
+
export declare function blogListingSeo(input: BlogListingSeoInput): SeoInput;
|
|
94
|
+
//# sourceMappingURL=blog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../src/seo-core/blog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAW,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpD,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;IACxB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAUF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,QAAQ,CA8E7D;AAED,MAAM,MAAM,mBAAmB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACpC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,MAAM,CAAC;IAC/C;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,QAAQ,CAgCnE"}
|