emdash 0.40.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/route-utils.mjs +6 -6
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/{api-cFFZpyS0.mjs → api-CuyNEHZG.mjs} +3 -3
- package/dist/{api-cFFZpyS0.mjs.map → api-CuyNEHZG.mjs.map} +1 -1
- package/dist/{apply-CoT603H3.mjs → apply-npY1TAhG.mjs} +237 -34
- package/dist/apply-npY1TAhG.mjs.map +1 -0
- package/dist/astro/index.d.mts +3 -3
- package/dist/astro/index.mjs +9 -9
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +3 -3
- package/dist/astro/middleware/redirect.mjs +1 -1
- package/dist/astro/middleware.d.mts +3 -3
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +19 -18
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +2 -2
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +12 -12
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/updates.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/capabilities.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/index.mjs +3 -3
- package/dist/astro/routes/api/dashboard.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/media.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +3 -3
- package/dist/astro/routes/api/manifest.mjs +1 -1
- package/dist/astro/routes/api/mcp.mjs +13 -33
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/upload.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/usage.mjs +2 -2
- package/dist/astro/routes/api/media/_id_.mjs +2 -2
- package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +1 -1
- package/dist/astro/routes/api/media.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/index.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/summary.mjs +2 -2
- package/dist/astro/routes/api/redirects/_id_.mjs +2 -2
- package/dist/astro/routes/api/redirects/index.mjs +2 -2
- package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/index.mjs +8 -8
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/export.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/index.mjs +2 -2
- package/dist/astro/routes/api/settings.mjs +3 -3
- package/dist/astro/routes/api/setup/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +35 -10
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/snapshot.mjs +2 -2
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/robots.txt.mjs +2 -2
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +2 -2
- package/dist/astro/routes/sitemap.xml.mjs +2 -2
- package/dist/astro/types.d.mts +7 -3
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{backup-CBu0LrXf.mjs → backup-BQRj3afm.mjs} +3 -3
- package/dist/{backup-CBu0LrXf.mjs.map → backup-BQRj3afm.mjs.map} +1 -1
- package/dist/cli/index.mjs +6 -6
- package/dist/client/index.d.mts +14 -14
- package/dist/{config-5ii2haYu.d.mts → config-BjEm_3xL.d.mts} +25 -16
- package/dist/config-BjEm_3xL.d.mts.map +1 -0
- package/dist/{context-BeGccdUX.mjs → context-BV_7t-Ua.mjs} +6 -6
- package/dist/{context-BeGccdUX.mjs.map → context-BV_7t-Ua.mjs.map} +1 -1
- package/dist/{dashboard-DjrB-xxZ.mjs → dashboard-BP-iJqpd.mjs} +2 -2
- package/dist/{dashboard-DjrB-xxZ.mjs.map → dashboard-BP-iJqpd.mjs.map} +1 -1
- package/dist/db/libsql-migrations.mjs +2 -2
- package/dist/db/postgres-migrations.mjs +2 -2
- package/dist/db/sqlite-migrations.mjs +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs → direct-executor-D2lNadwo.mjs} +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs.map → direct-executor-D2lNadwo.mjs.map} +1 -1
- package/dist/{emdash-runtime-DAjghyEG.mjs → emdash-runtime-CtfnZmPi.mjs} +56 -56
- package/dist/emdash-runtime-CtfnZmPi.mjs.map +1 -0
- package/dist/{entry-lock-DVhCt49A.d.mts → entry-lock-Cf5OoIgN.d.mts} +18 -18
- package/dist/{entry-lock-DVhCt49A.d.mts.map → entry-lock-Cf5OoIgN.d.mts.map} +1 -1
- package/dist/{hreflang-DMFBL2eb.mjs → hreflang-CbVI4paR.mjs} +2 -2
- package/dist/{hreflang-DMFBL2eb.mjs.map → hreflang-CbVI4paR.mjs.map} +1 -1
- package/dist/{identity-IyNWwXr6.mjs → identity-Be5kzutJ.mjs} +2 -2
- package/dist/{identity-IyNWwXr6.mjs.map → identity-Be5kzutJ.mjs.map} +1 -1
- package/dist/{import-C3JZplWq.mjs → import-YzfKCaco.mjs} +2 -2
- package/dist/{import-C3JZplWq.mjs.map → import-YzfKCaco.mjs.map} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +16 -16
- package/dist/{manifest-builder-B8zR-73J.mjs → manifest-builder-BmSAOOEF.mjs} +2 -2
- package/dist/{manifest-builder-B8zR-73J.mjs.map → manifest-builder-BmSAOOEF.mjs.map} +1 -1
- package/dist/media/local-runtime.d.mts +3 -3
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-Qp5S3eH3.mjs → media-CPXTbb1R.mjs} +20 -2
- package/dist/media-CPXTbb1R.mjs.map +1 -0
- package/dist/{media-h7tOiDLj.mjs → media-JXRWDhw3.mjs} +77 -4
- package/dist/media-JXRWDhw3.mjs.map +1 -0
- package/dist/{media-usage-My-Vi-ex.mjs → media-usage-De-zeR9E.mjs} +2 -2
- package/dist/{media-usage-My-Vi-ex.mjs.map → media-usage-De-zeR9E.mjs.map} +1 -1
- package/dist/{menus-TsSGmSR4.mjs → menus-Cm68rsAq.mjs} +4 -4
- package/dist/{menus-TsSGmSR4.mjs.map → menus-Cm68rsAq.mjs.map} +1 -1
- package/dist/migrations/index.mjs +2 -2
- package/dist/page/index.mjs +2 -2
- package/dist/plugin-test-runtime.d.mts +3 -3
- package/dist/plugin-test-runtime.mjs +18 -18
- package/dist/plugin-utils.d.mts +3 -3
- package/dist/plugins/adapt-sandbox-entry.d.mts +3 -3
- package/dist/plugins/host.d.mts +1 -1
- package/dist/plugins/host.mjs +6 -6
- package/dist/{redirect-iJf5fltD.mjs → redirect-B29UjdUn.mjs} +10 -14
- package/dist/redirect-B29UjdUn.mjs.map +1 -0
- package/dist/{redirects-CYu1u1aK.mjs → redirects-Ca2MPfEG.mjs} +2 -2
- package/dist/{redirects-CYu1u1aK.mjs.map → redirects-Ca2MPfEG.mjs.map} +1 -1
- package/dist/{routes-CUsM40Cc.mjs → routes-vrR95WR6.mjs} +2 -2
- package/dist/{routes-CUsM40Cc.mjs.map → routes-vrR95WR6.mjs.map} +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/seed/index.mjs +4 -4
- package/dist/seo/index.mjs +3 -3
- package/dist/{settings-CgjRWGW9.mjs → settings-B4vDl1fK.mjs} +2 -2
- package/dist/{settings-CgjRWGW9.mjs.map → settings-B4vDl1fK.mjs.map} +1 -1
- package/dist/{settings-2xNYr90o.mjs → settings-Dyj6v8C4.mjs} +2 -2
- package/dist/{settings-2xNYr90o.mjs.map → settings-Dyj6v8C4.mjs.map} +1 -1
- package/dist/{snapshot-CugwUqKQ.mjs → snapshot-PawoX-WG.mjs} +180 -3
- package/dist/snapshot-PawoX-WG.mjs.map +1 -0
- package/dist/{storage-query-Bdv7A62k.d.mts → storage-query-FXJH5pI8.d.mts} +6 -1
- package/dist/storage-query-FXJH5pI8.d.mts.map +1 -0
- package/dist/{transfer-BhUCrgDc.mjs → transfer-DgeCvHnj.mjs} +4 -4
- package/dist/{transfer-BhUCrgDc.mjs.map → transfer-DgeCvHnj.mjs.map} +1 -1
- package/dist/validate-CM79QXYE.d.mts.map +1 -1
- package/dist/version-DXsxRrob.mjs +7 -0
- package/dist/{version-DUQDHgrf.mjs.map → version-DXsxRrob.mjs.map} +1 -1
- package/package.json +5 -5
- package/src/api/handlers/index.ts +1 -0
- package/src/api/handlers/media-upload.ts +17 -71
- package/src/api/handlers/media.ts +97 -3
- package/src/api/handlers/snapshot.ts +257 -3
- package/src/astro/integration/vite-config.ts +10 -4
- package/src/astro/middleware.ts +1 -0
- package/src/astro/routes/api/setup/index.ts +66 -23
- package/src/astro/routes/api/snapshot.ts +2 -1
- package/src/astro/types.ts +5 -0
- package/src/cleanup.ts +10 -0
- package/src/components/Columns.astro +3 -1
- package/src/components/Cover.astro +3 -1
- package/src/database/repositories/media.ts +40 -1
- package/src/database/repositories/redirect.ts +17 -34
- package/src/emdash-runtime.ts +28 -0
- package/src/index.ts +1 -0
- package/src/mcp/server.ts +13 -48
- package/src/seed/apply.ts +322 -41
- package/dist/apply-CoT603H3.mjs.map +0 -1
- package/dist/config-5ii2haYu.d.mts.map +0 -1
- package/dist/emdash-runtime-DAjghyEG.mjs.map +0 -1
- package/dist/media-Qp5S3eH3.mjs.map +0 -1
- package/dist/media-h7tOiDLj.mjs.map +0 -1
- package/dist/redirect-iJf5fltD.mjs.map +0 -1
- package/dist/snapshot-CugwUqKQ.mjs.map +0 -1
- package/dist/storage-query-Bdv7A62k.d.mts.map +0 -1
- package/dist/version-DUQDHgrf.mjs +0 -7
|
@@ -3,10 +3,11 @@ import { o as resolveConfiguredLocale, r as getI18nConfig } from "./config-CvmjJ
|
|
|
3
3
|
import { t as OptionsRepository } from "./options-DhrI7eMv.mjs";
|
|
4
4
|
import { i as RevisionRepository, t as ContentRepository } from "./content-DFaIEDj9.mjs";
|
|
5
5
|
import { r as coalesceObjectCacheWrites } from "./object-cache-CqWNY5uP.mjs";
|
|
6
|
+
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-Dms7Uxbt.mjs";
|
|
6
7
|
import { t as withTransaction } from "./transaction-BcJO4Tmc.mjs";
|
|
7
|
-
import { t as MediaRepository } from "./media-
|
|
8
|
+
import { t as MediaRepository } from "./media-CPXTbb1R.mjs";
|
|
8
9
|
import { t as TaxonomyRepository } from "./taxonomy-C3Fu1YeH.mjs";
|
|
9
|
-
import { t as RedirectRepository } from "./redirect-
|
|
10
|
+
import { t as RedirectRepository } from "./redirect-B29UjdUn.mjs";
|
|
10
11
|
import { t as BylineRepository } from "./byline-DYmkXqvo.mjs";
|
|
11
12
|
import { t as INTERNAL_MEDIA_PREFIX } from "./normalize-CO2P8-iE.mjs";
|
|
12
13
|
import { t as imageSize } from "./dist-BNwANFJR.mjs";
|
|
@@ -14,7 +15,7 @@ import { t as FTSManager } from "./fts-manager-S-K0laoS.mjs";
|
|
|
14
15
|
import { d as findTaxonomyStructure, p as saveTaxonomyStructure } from "./loader-BzqeR1ZN.mjs";
|
|
15
16
|
import { U as markContentMediaUsageCollectionStaleSafely, a as SchemaRegistry, t as BlockTypeRegistry } from "./block-type-registry-DUI_3KxT.mjs";
|
|
16
17
|
import { i as resolveBlockTypes, r as normalizeBlocksData } from "./block-values-C_VIPr5t.mjs";
|
|
17
|
-
import { c as setSiteSettings, s as invalidateSiteSettingsCache } from "./settings-
|
|
18
|
+
import { c as setSiteSettings, s as invalidateSiteSettingsCache } from "./settings-B4vDl1fK.mjs";
|
|
18
19
|
import { a as ssrfSafeFetch, s as validateExternalUrl } from "./ssrf-CJUnJymJ.mjs";
|
|
19
20
|
import { n as validateSeed, t as findTaxonomyStructureSource } from "./validate-CHosPEhs.mjs";
|
|
20
21
|
import { ulid } from "ulidx";
|
|
@@ -131,7 +132,10 @@ function isRecord(value) {
|
|
|
131
132
|
* This is the core implementation that bootstraps an EmDash site from a seed file.
|
|
132
133
|
* Apply order is critical for foreign keys and references.
|
|
133
134
|
*/
|
|
134
|
-
var apply_exports = /* @__PURE__ */ __exportAll({
|
|
135
|
+
var apply_exports = /* @__PURE__ */ __exportAll({
|
|
136
|
+
applySeed: () => applySeed,
|
|
137
|
+
applySeedWithinBudget: () => applySeedWithinBudget
|
|
138
|
+
});
|
|
135
139
|
async function applySiteSettings(db, settings, onConflict, result) {
|
|
136
140
|
const entries = Object.entries(settings).filter(([, value]) => value !== void 0);
|
|
137
141
|
if (entries.length === 0) return;
|
|
@@ -203,12 +207,59 @@ function isUntouchedBuiltInTaxonomyDef(def) {
|
|
|
203
207
|
* @returns Result summary
|
|
204
208
|
*/
|
|
205
209
|
async function applySeed(db, seed, options = {}) {
|
|
206
|
-
|
|
210
|
+
const { result } = await coalesceObjectCacheWrites(() => applySeedWrites(db, seed, options, null));
|
|
211
|
+
return result;
|
|
207
212
|
}
|
|
208
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Apply a seed over several calls, for a platform that caps the queries or
|
|
215
|
+
* fetches of one request.
|
|
216
|
+
*
|
|
217
|
+
* The limits are thresholds, checked before each taxonomy term, byline and
|
|
218
|
+
* content entry is created and before the phases that follow content. Schema,
|
|
219
|
+
* taxonomy definitions and settings are applied in every call, the item that
|
|
220
|
+
* crosses a threshold finishes, and the phases after content (menus,
|
|
221
|
+
* redirects, widgets, sections) run in one call, so set the limits well below
|
|
222
|
+
* the platform's. A call that stops returns `complete: false`; applying the
|
|
223
|
+
* same seed again continues with the first item not yet created. A call
|
|
224
|
+
* creates at least one item before it stops, so repeated calls finish. A
|
|
225
|
+
* `$media` URL used by entries in different calls is downloaded and stored
|
|
226
|
+
* again, as a new media row, in each of those calls. Requires
|
|
227
|
+
* `onConflict: "skip"`.
|
|
228
|
+
*/
|
|
229
|
+
async function applySeedWithinBudget(db, seed, options, limits) {
|
|
230
|
+
const budget = new SeedBudget(limits);
|
|
231
|
+
return coalesceObjectCacheWrites(() => applySeedWrites(db.withPlugin(budget), seed, options, budget));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Counts what a budgeted call spends. As a Kysely plugin it sees every query
|
|
235
|
+
* the call runs, including those inside transactions.
|
|
236
|
+
*/
|
|
237
|
+
var SeedBudget = class {
|
|
238
|
+
#queries = 0;
|
|
239
|
+
#mediaDownloads = 0;
|
|
240
|
+
constructor(limits) {
|
|
241
|
+
this.limits = limits;
|
|
242
|
+
}
|
|
243
|
+
transformQuery(args) {
|
|
244
|
+
this.#queries++;
|
|
245
|
+
return args.node;
|
|
246
|
+
}
|
|
247
|
+
transformResult(args) {
|
|
248
|
+
return Promise.resolve(args.result);
|
|
249
|
+
}
|
|
250
|
+
countMediaDownload() {
|
|
251
|
+
this.#mediaDownloads++;
|
|
252
|
+
}
|
|
253
|
+
isSpent() {
|
|
254
|
+
const { queries, mediaDownloads } = this.limits;
|
|
255
|
+
return queries !== void 0 && this.#queries >= queries || mediaDownloads !== void 0 && this.#mediaDownloads >= mediaDownloads;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
async function applySeedWrites(db, seed, options, budget) {
|
|
209
259
|
const validation = validateSeed(seed);
|
|
210
260
|
if (!validation.valid) throw new Error(`Invalid seed file:\n${validation.errors.join("\n")}`);
|
|
211
261
|
const { includeContent = false, storage, skipMediaDownload = false, onConflict = "skip" } = options;
|
|
262
|
+
if (budget && onConflict !== "skip") throw new Error("A seed budget requires onConflict: \"skip\"");
|
|
212
263
|
const result = {
|
|
213
264
|
blockTypes: {
|
|
214
265
|
created: 0,
|
|
@@ -264,11 +315,18 @@ async function applySeedWrites(db, seed, options) {
|
|
|
264
315
|
skipped: 0
|
|
265
316
|
}
|
|
266
317
|
};
|
|
318
|
+
let complete = true;
|
|
319
|
+
const progress = {
|
|
320
|
+
done: 0,
|
|
321
|
+
total: countSeedItems(seed, includeContent)
|
|
322
|
+
};
|
|
323
|
+
const mayCreateItem = () => !budget || result.taxonomies.terms + result.bylines.created + result.content.created === 0 || !budget.isSpent();
|
|
267
324
|
const mediaContext = {
|
|
268
325
|
db,
|
|
269
326
|
storage: storage ?? null,
|
|
270
327
|
skipMediaDownload,
|
|
271
|
-
mediaCache: /* @__PURE__ */ new Map()
|
|
328
|
+
mediaCache: /* @__PURE__ */ new Map(),
|
|
329
|
+
budget
|
|
272
330
|
};
|
|
273
331
|
const seedIdMap = /* @__PURE__ */ new Map();
|
|
274
332
|
const seedBylineIdMap = /* @__PURE__ */ new Map();
|
|
@@ -441,21 +499,31 @@ async function applySeedWrites(db, seed, options) {
|
|
|
441
499
|
}
|
|
442
500
|
if (includeContent && taxonomy.terms && taxonomy.terms.length > 0) {
|
|
443
501
|
const termRepo = new TaxonomyRepository(db);
|
|
444
|
-
|
|
445
|
-
|
|
502
|
+
const existingTerms = await findExistingSeedTerms(db, taxonomy.name, taxonomy.terms);
|
|
503
|
+
if (structure.hierarchical) {
|
|
504
|
+
const applied = await applyHierarchicalTerms(termRepo, taxonomy.name, defLocale, taxonomy.terms, existingTerms, termSeedIdMap, result, onConflict, mayCreateItem);
|
|
505
|
+
progress.done += applied.processed;
|
|
506
|
+
complete = applied.complete;
|
|
507
|
+
} else for (const term of taxonomy.terms) {
|
|
446
508
|
const termLocale = resolveConfiguredLocale(term.locale ?? defLocale);
|
|
447
|
-
const
|
|
448
|
-
|
|
509
|
+
const termKey = seedTermKey(termLocale, term.slug);
|
|
510
|
+
const existingId = existingTerms.get(termKey);
|
|
511
|
+
if (existingId) {
|
|
449
512
|
if (onConflict === "error") throw new Error(`Conflict: taxonomy term "${term.slug}" in "${taxonomy.name}" (${termLocale}) already exists`);
|
|
450
513
|
if (onConflict === "update") {
|
|
451
|
-
await termRepo.update(
|
|
514
|
+
await termRepo.update(existingId, {
|
|
452
515
|
label: term.label,
|
|
453
516
|
data: term.description ? { description: term.description } : {}
|
|
454
517
|
});
|
|
455
518
|
result.taxonomies.terms++;
|
|
456
519
|
}
|
|
457
|
-
if (term.id) termSeedIdMap.set(term.id,
|
|
520
|
+
if (term.id) termSeedIdMap.set(term.id, existingId);
|
|
521
|
+
progress.done++;
|
|
458
522
|
} else {
|
|
523
|
+
if (!mayCreateItem()) {
|
|
524
|
+
complete = false;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
459
527
|
const translationOf = term.translationOf ? termSeedIdMap.get(term.translationOf) : void 0;
|
|
460
528
|
const created = await termRepo.create({
|
|
461
529
|
name: taxonomy.name,
|
|
@@ -465,25 +533,29 @@ async function applySeedWrites(db, seed, options) {
|
|
|
465
533
|
locale: termLocale,
|
|
466
534
|
translationOf
|
|
467
535
|
});
|
|
536
|
+
existingTerms.set(termKey, created.id);
|
|
468
537
|
if (term.id) termSeedIdMap.set(term.id, created.id);
|
|
469
538
|
result.taxonomies.terms++;
|
|
539
|
+
progress.done++;
|
|
470
540
|
}
|
|
471
541
|
}
|
|
472
542
|
}
|
|
543
|
+
if (!complete) break;
|
|
473
544
|
}
|
|
474
545
|
const { invalidateTaxonomyDefsCache } = await import("./taxonomies-C1IwNe8Q.mjs").then((n) => n.f);
|
|
475
546
|
invalidateTaxonomyDefsCache();
|
|
476
547
|
}
|
|
477
|
-
if (includeContent && seed.bylines) {
|
|
548
|
+
if (complete && includeContent && seed.bylines) {
|
|
478
549
|
const bylineRepo = new BylineRepository(db);
|
|
550
|
+
const existingBylines = await findExistingSeedBylines(db, seed.bylines.map((byline) => byline.slug));
|
|
479
551
|
for (const byline of seed.bylines) {
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
552
|
+
const existingId = existingBylines.get(byline.slug);
|
|
553
|
+
if (existingId) {
|
|
482
554
|
if (onConflict === "error") throw new Error(`Conflict: byline "${byline.slug}" already exists`);
|
|
483
555
|
if (onConflict === "update") {
|
|
484
556
|
const avatar = byline.avatar ? await resolveSeedBylineAvatar(db, byline.avatar) : null;
|
|
485
557
|
try {
|
|
486
|
-
if (!await bylineRepo.update(
|
|
558
|
+
if (!await bylineRepo.update(existingId, {
|
|
487
559
|
displayName: byline.displayName,
|
|
488
560
|
bio: byline.bio ?? null,
|
|
489
561
|
websiteUrl: byline.websiteUrl ?? null,
|
|
@@ -494,15 +566,21 @@ async function applySeedWrites(db, seed, options) {
|
|
|
494
566
|
if (avatar?.created) await deleteMediaRow(db, avatar.id);
|
|
495
567
|
throw error;
|
|
496
568
|
}
|
|
497
|
-
seedBylineIdMap.set(byline.id,
|
|
569
|
+
seedBylineIdMap.set(byline.id, existingId);
|
|
498
570
|
result.bylines.updated++;
|
|
499
571
|
if (avatar?.created) result.media.created++;
|
|
572
|
+
progress.done++;
|
|
500
573
|
continue;
|
|
501
574
|
}
|
|
502
|
-
seedBylineIdMap.set(byline.id,
|
|
575
|
+
seedBylineIdMap.set(byline.id, existingId);
|
|
503
576
|
result.bylines.skipped++;
|
|
577
|
+
progress.done++;
|
|
504
578
|
continue;
|
|
505
579
|
}
|
|
580
|
+
if (!mayCreateItem()) {
|
|
581
|
+
complete = false;
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
506
584
|
const avatar = byline.avatar ? await resolveSeedBylineAvatar(db, byline.avatar) : null;
|
|
507
585
|
let createdId;
|
|
508
586
|
try {
|
|
@@ -518,12 +596,14 @@ async function applySeedWrites(db, seed, options) {
|
|
|
518
596
|
if (avatar?.created) await deleteMediaRow(db, avatar.id);
|
|
519
597
|
throw error;
|
|
520
598
|
}
|
|
599
|
+
existingBylines.set(byline.slug, createdId);
|
|
521
600
|
seedBylineIdMap.set(byline.id, createdId);
|
|
522
601
|
result.bylines.created++;
|
|
523
602
|
if (avatar?.created) result.media.created++;
|
|
603
|
+
progress.done++;
|
|
524
604
|
}
|
|
525
605
|
}
|
|
526
|
-
if (includeContent && seed.content) {
|
|
606
|
+
if (complete && includeContent && seed.content) {
|
|
527
607
|
const contentRepo = new ContentRepository(db);
|
|
528
608
|
const schemaRegistry = new SchemaRegistry(db);
|
|
529
609
|
const datetimeContexts = /* @__PURE__ */ new Map();
|
|
@@ -532,10 +612,11 @@ async function applySeedWrites(db, seed, options) {
|
|
|
532
612
|
const collectionInfo = await schemaRegistry.getCollectionWithFields(collectionSlug);
|
|
533
613
|
const collectionRoutable = collectionInfo?.routable !== false;
|
|
534
614
|
const resolvedBlockTypes = collectionInfo?.fields.some((field) => field.type === "blocks") ? await resolveBlockTypes(db) : void 0;
|
|
615
|
+
const existingEntries = await findExistingSeedEntries(contentRepo, collectionSlug, entries, defaultLocale);
|
|
535
616
|
for (const entry of entries) {
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
const existing =
|
|
617
|
+
const { slug: entrySlug, locale: entryLocale } = seedEntryIdentity(entry, defaultLocale);
|
|
618
|
+
const entryKey = seedEntryKey(entry, entrySlug, entryLocale);
|
|
619
|
+
const existing = existingEntries.get(entryKey);
|
|
539
620
|
if (existing) {
|
|
540
621
|
if (onConflict === "error") throw new Error(`Conflict: content "${entrySlug ?? entry.id}" in "${collectionSlug}" already exists`);
|
|
541
622
|
if (onConflict === "update") {
|
|
@@ -580,13 +661,19 @@ async function applySeedWrites(db, seed, options) {
|
|
|
580
661
|
}
|
|
581
662
|
seedIdMap.set(entry.id, existing.id);
|
|
582
663
|
result.content.updated++;
|
|
664
|
+
progress.done++;
|
|
583
665
|
await markSeedContentCollectionStale(collectionSlug);
|
|
584
666
|
continue;
|
|
585
667
|
}
|
|
586
668
|
result.content.skipped++;
|
|
587
669
|
seedIdMap.set(entry.id, existing.id);
|
|
670
|
+
progress.done++;
|
|
588
671
|
continue;
|
|
589
672
|
}
|
|
673
|
+
if (!mayCreateItem()) {
|
|
674
|
+
complete = false;
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
590
677
|
let resolvedData = await resolveReferences(entry.data, seedIdMap, mediaContext, result);
|
|
591
678
|
if (collectionInfo) resolvedData = await normalizeBlocksData(db, collectionInfo, resolvedData, {}, { restoreBlocks: true }, false, resolvedBlockTypes);
|
|
592
679
|
let translationOf;
|
|
@@ -623,14 +710,26 @@ async function applySeedWrites(db, seed, options) {
|
|
|
623
710
|
throw error;
|
|
624
711
|
}
|
|
625
712
|
seedIdMap.set(entry.id, created.id);
|
|
713
|
+
existingEntries.set(entryKey, created);
|
|
626
714
|
result.content.created++;
|
|
715
|
+
progress.done++;
|
|
627
716
|
await markSeedContentCollectionStale(collectionSlug);
|
|
628
717
|
}
|
|
718
|
+
if (!complete) break;
|
|
629
719
|
}
|
|
630
720
|
} finally {
|
|
631
721
|
await retryFailedSeedContentStaleMarks();
|
|
632
722
|
}
|
|
633
723
|
}
|
|
724
|
+
if (complete && !mayCreateItem()) complete = false;
|
|
725
|
+
if (!complete) {
|
|
726
|
+
await invalidateSeedCaches();
|
|
727
|
+
return {
|
|
728
|
+
result,
|
|
729
|
+
complete,
|
|
730
|
+
progress
|
|
731
|
+
};
|
|
732
|
+
}
|
|
634
733
|
if (seed.menus) {
|
|
635
734
|
const menuSeedIdMap = /* @__PURE__ */ new Map();
|
|
636
735
|
const itemSeedIdMap = /* @__PURE__ */ new Map();
|
|
@@ -767,18 +866,110 @@ async function applySeedWrites(db, seed, options) {
|
|
|
767
866
|
}
|
|
768
867
|
}
|
|
769
868
|
}
|
|
869
|
+
await invalidateSeedCaches();
|
|
870
|
+
return {
|
|
871
|
+
result,
|
|
872
|
+
complete,
|
|
873
|
+
progress
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function countSeedItems(seed, includeContent) {
|
|
877
|
+
if (!includeContent) return 0;
|
|
878
|
+
const terms = (seed.taxonomies ?? []).reduce((total, taxonomy) => total + (taxonomy.terms?.length ?? 0), 0);
|
|
879
|
+
const entries = Object.values(seed.content ?? {}).reduce((total, collectionEntries) => total + collectionEntries.length, 0);
|
|
880
|
+
return terms + (seed.bylines?.length ?? 0) + entries;
|
|
881
|
+
}
|
|
882
|
+
function seedTermKey(locale, slug) {
|
|
883
|
+
return `${locale}::${slug}`;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Find which of a taxonomy's seed terms already exist, as term ids keyed by
|
|
887
|
+
* `seedTermKey`. Each query covers a batch of slugs.
|
|
888
|
+
*/
|
|
889
|
+
async function findExistingSeedTerms(db, taxonomyName, terms) {
|
|
890
|
+
const existing = /* @__PURE__ */ new Map();
|
|
891
|
+
for (const batch of chunks([...new Set(terms.map((term) => term.slug))], SQL_BATCH_SIZE)) {
|
|
892
|
+
const rows = await db.selectFrom("taxonomies").select([
|
|
893
|
+
"id",
|
|
894
|
+
"slug",
|
|
895
|
+
"locale"
|
|
896
|
+
]).where("name", "=", taxonomyName).where("slug", "in", batch).execute();
|
|
897
|
+
for (const row of rows) existing.set(seedTermKey(row.locale, row.slug), row.id);
|
|
898
|
+
}
|
|
899
|
+
return existing;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Find which seed bylines already exist, as byline ids keyed by slug. A slug
|
|
903
|
+
* in several locales resolves to the lowest locale code, as
|
|
904
|
+
* `BylineRepository.findBySlug` does. Each query covers a batch of slugs.
|
|
905
|
+
*/
|
|
906
|
+
async function findExistingSeedBylines(db, slugs) {
|
|
907
|
+
const existing = /* @__PURE__ */ new Map();
|
|
908
|
+
for (const batch of chunks([...new Set(slugs)], SQL_BATCH_SIZE)) {
|
|
909
|
+
const rows = await db.selectFrom("_emdash_bylines").select(["id", "slug"]).where("slug", "in", batch).orderBy("locale", "asc").execute();
|
|
910
|
+
for (const row of rows) if (!existing.has(row.slug)) existing.set(row.slug, row.id);
|
|
911
|
+
}
|
|
912
|
+
return existing;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Invalidate caches that may have been affected by seed data.
|
|
916
|
+
* Seed creates bylines, redirects, and collections, all of which
|
|
917
|
+
* have module-level caches in the hot path.
|
|
918
|
+
*/
|
|
919
|
+
async function invalidateSeedCaches() {
|
|
770
920
|
const { invalidateBylineCache } = await import("./bylines-BPwJqoYH.mjs").then((n) => n.t);
|
|
771
921
|
const { invalidateRedirectCache } = await import("./cache-m7UEkRFV.mjs").then((n) => n.t);
|
|
772
922
|
const { invalidateUrlPatternCache } = await import("./query-B48peoRT.mjs").then((n) => n.o);
|
|
773
923
|
invalidateBylineCache();
|
|
774
924
|
invalidateRedirectCache();
|
|
775
925
|
invalidateUrlPatternCache();
|
|
776
|
-
|
|
926
|
+
}
|
|
927
|
+
function seedEntryIdentity(entry, defaultLocale) {
|
|
928
|
+
return {
|
|
929
|
+
slug: typeof entry.slug === "string" && entry.slug.trim().length > 0 ? entry.slug : null,
|
|
930
|
+
locale: resolveConfiguredLocale(entry.locale ?? defaultLocale)
|
|
931
|
+
};
|
|
777
932
|
}
|
|
778
933
|
/**
|
|
779
|
-
*
|
|
934
|
+
* Slugful entries use the existing locale-aware key. Slugless seed entries
|
|
935
|
+
* persist their seed ID, which keeps re-application idempotent.
|
|
780
936
|
*/
|
|
781
|
-
|
|
937
|
+
function seedEntryKey(entry, slug, locale) {
|
|
938
|
+
return slug === null ? `id:${entry.id}` : `slug:${locale}:${slug}`;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Find which of a collection's seed entries already exist, keyed by
|
|
942
|
+
* `seedEntryKey`. Each query covers a batch of entries.
|
|
943
|
+
*/
|
|
944
|
+
async function findExistingSeedEntries(repo, collectionSlug, entries, defaultLocale) {
|
|
945
|
+
const identities = entries.map((entry) => ({
|
|
946
|
+
entry,
|
|
947
|
+
...seedEntryIdentity(entry, defaultLocale)
|
|
948
|
+
}));
|
|
949
|
+
const slugsByLocale = /* @__PURE__ */ new Map();
|
|
950
|
+
for (const { slug, locale } of identities) {
|
|
951
|
+
if (slug === null) continue;
|
|
952
|
+
const slugs = slugsByLocale.get(locale);
|
|
953
|
+
if (slugs) slugs.push(slug);
|
|
954
|
+
else slugsByLocale.set(locale, [slug]);
|
|
955
|
+
}
|
|
956
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
957
|
+
for (const [locale, slugs] of slugsByLocale) bySlug.set(locale, await repo.findManyBySlugsInLocale(collectionSlug, slugs, locale));
|
|
958
|
+
const byId = await repo.findManyByIds(collectionSlug, identities.filter(({ slug }) => slug === null).map(({ entry }) => entry.id));
|
|
959
|
+
const existing = /* @__PURE__ */ new Map();
|
|
960
|
+
for (const { entry, slug, locale } of identities) {
|
|
961
|
+
const item = slug === null ? byId.get(entry.id) : bySlug.get(locale)?.get(slug);
|
|
962
|
+
if (item) existing.set(seedEntryKey(entry, slug, locale), item);
|
|
963
|
+
}
|
|
964
|
+
return existing;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Apply hierarchical taxonomy terms (parents before children). Stops before
|
|
968
|
+
* creating a term once `mayCreate` returns false, and returns how many terms
|
|
969
|
+
* were found or created.
|
|
970
|
+
*/
|
|
971
|
+
async function applyHierarchicalTerms(termRepo, taxonomyName, defLocale, terms, existingTerms, termSeedIdMap, result, onConflict, mayCreate) {
|
|
972
|
+
let processed = 0;
|
|
782
973
|
const slugToId = /* @__PURE__ */ new Map();
|
|
783
974
|
const resolveTermLocale = (term) => resolveConfiguredLocale(term.locale ?? defLocale);
|
|
784
975
|
let remaining = [...terms];
|
|
@@ -792,20 +983,25 @@ async function applyHierarchicalTerms(termRepo, taxonomyName, defLocale, terms,
|
|
|
792
983
|
if (!parentReady || !translationReady) continue;
|
|
793
984
|
const parentId = term.parent ? slugToId.get(`${termLocale}::${term.parent}`) : void 0;
|
|
794
985
|
const translationOf = term.translationOf ? termSeedIdMap.get(term.translationOf) : void 0;
|
|
795
|
-
const
|
|
796
|
-
|
|
986
|
+
const termKey = seedTermKey(termLocale, term.slug);
|
|
987
|
+
const existingId = existingTerms.get(termKey);
|
|
988
|
+
if (existingId) {
|
|
797
989
|
if (onConflict === "error") throw new Error(`Conflict: taxonomy term "${term.slug}" in "${taxonomyName}" (${termLocale}) already exists`);
|
|
798
990
|
if (onConflict === "update") {
|
|
799
|
-
await termRepo.update(
|
|
991
|
+
await termRepo.update(existingId, {
|
|
800
992
|
label: term.label,
|
|
801
993
|
parentId,
|
|
802
994
|
data: term.description ? { description: term.description } : {}
|
|
803
995
|
});
|
|
804
996
|
result.taxonomies.terms++;
|
|
805
997
|
}
|
|
806
|
-
slugToId.set(
|
|
807
|
-
if (term.id) termSeedIdMap.set(term.id,
|
|
998
|
+
slugToId.set(termKey, existingId);
|
|
999
|
+
if (term.id) termSeedIdMap.set(term.id, existingId);
|
|
808
1000
|
} else {
|
|
1001
|
+
if (!mayCreate()) return {
|
|
1002
|
+
processed,
|
|
1003
|
+
complete: false
|
|
1004
|
+
};
|
|
809
1005
|
const created = await termRepo.create({
|
|
810
1006
|
name: taxonomyName,
|
|
811
1007
|
slug: term.slug,
|
|
@@ -815,16 +1011,22 @@ async function applyHierarchicalTerms(termRepo, taxonomyName, defLocale, terms,
|
|
|
815
1011
|
locale: termLocale,
|
|
816
1012
|
translationOf
|
|
817
1013
|
});
|
|
818
|
-
|
|
1014
|
+
existingTerms.set(termKey, created.id);
|
|
1015
|
+
slugToId.set(termKey, created.id);
|
|
819
1016
|
if (term.id) termSeedIdMap.set(term.id, created.id);
|
|
820
1017
|
result.taxonomies.terms++;
|
|
821
1018
|
}
|
|
1019
|
+
processed++;
|
|
822
1020
|
processedThisPass.push(term.slug + "::" + termLocale);
|
|
823
1021
|
}
|
|
824
1022
|
remaining = remaining.filter((term) => !processedThisPass.includes(term.slug + "::" + resolveTermLocale(term)));
|
|
825
1023
|
maxPasses--;
|
|
826
1024
|
}
|
|
827
1025
|
if (remaining.length > 0) console.warn(`Could not process ${remaining.length} terms due to missing parents/translations`);
|
|
1026
|
+
return {
|
|
1027
|
+
processed,
|
|
1028
|
+
complete: true
|
|
1029
|
+
};
|
|
828
1030
|
}
|
|
829
1031
|
/**
|
|
830
1032
|
* Apply byline credits to a content entry.
|
|
@@ -1057,6 +1259,7 @@ async function resolveMedia(ref, ctx, result) {
|
|
|
1057
1259
|
try {
|
|
1058
1260
|
validateExternalUrl(url);
|
|
1059
1261
|
console.log(` 📥 Downloading: ${url}`);
|
|
1262
|
+
ctx.budget?.countMediaDownload();
|
|
1060
1263
|
const response = await ssrfSafeFetch(url, { headers: { "User-Agent": "EmDash-CMS/1.0" } });
|
|
1061
1264
|
if (!response.ok) {
|
|
1062
1265
|
console.warn(` ⚠️ Failed to download ${url}: ${response.status}`);
|
|
@@ -1159,5 +1362,5 @@ function getImageDimensions(buffer) {
|
|
|
1159
1362
|
}
|
|
1160
1363
|
|
|
1161
1364
|
//#endregion
|
|
1162
|
-
export {
|
|
1163
|
-
//# sourceMappingURL=apply-
|
|
1365
|
+
export { sanitizeGalleryImages as a, resolveImageMedia as i, applySeedWithinBudget as n, createPublicMediaUrlResolver as o, apply_exports as r, applySeed as t };
|
|
1366
|
+
//# sourceMappingURL=apply-npY1TAhG.mjs.map
|