emdash 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-DoNJiveC.d.mts → adapters-BktHA7EO.d.mts} +1 -1
- package/dist/{adapters-DoNJiveC.d.mts.map → adapters-BktHA7EO.d.mts.map} +1 -1
- package/dist/{apply-BzltprvY.mjs → apply-Ded_1vng.mjs} +167 -254
- package/dist/apply-Ded_1vng.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.mjs +10 -2
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +5 -5
- package/dist/astro/middleware/auth.mjs +5 -5
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.mjs +4 -4
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +94 -43
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +12 -11
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{base64-BRICGH2l.mjs → base64-MBPo9ozB.mjs} +1 -1
- package/dist/{base64-BRICGH2l.mjs.map → base64-MBPo9ozB.mjs.map} +1 -1
- package/dist/{byline-BSaNL1w7.mjs → byline-gFn1r0vA.mjs} +4 -4
- package/dist/{byline-BSaNL1w7.mjs.map → byline-gFn1r0vA.mjs.map} +1 -1
- package/dist/{bylines-CvJ3PYz2.mjs → bylines-DTFI8nDM.mjs} +5 -5
- package/dist/{bylines-CvJ3PYz2.mjs.map → bylines-DTFI8nDM.mjs.map} +1 -1
- package/dist/{cache-C6N_hhN7.mjs → cache-BAJbeoZ8.mjs} +3 -3
- package/dist/{cache-C6N_hhN7.mjs.map → cache-BAJbeoZ8.mjs.map} +1 -1
- package/dist/{chunks-NBQVDOci.mjs → chunks-BK1oZS-l.mjs} +2 -2
- package/dist/{chunks-NBQVDOci.mjs.map → chunks-BK1oZS-l.mjs.map} +1 -1
- package/dist/cli/index.mjs +342 -95
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{config-BI0V3ICQ.mjs → config-CVssduLe.mjs} +1 -1
- package/dist/{config-BI0V3ICQ.mjs.map → config-CVssduLe.mjs.map} +1 -1
- package/dist/{content-8lOYF0pr.mjs → content-CERxPUN0.mjs} +14 -3
- package/dist/content-CERxPUN0.mjs.map +1 -0
- package/dist/database/instrumentation.d.mts +6 -4
- package/dist/database/instrumentation.d.mts.map +1 -1
- package/dist/database/instrumentation.mjs +19 -7
- package/dist/database/instrumentation.mjs.map +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-WRezodiz.mjs → db-errors-B7P2pSCn.mjs} +1 -1
- package/dist/{db-errors-WRezodiz.mjs.map → db-errors-B7P2pSCn.mjs.map} +1 -1
- package/dist/{default-D8ksjWhO.mjs → default-pHuz9WF6.mjs} +1 -1
- package/dist/{default-D8ksjWhO.mjs.map → default-pHuz9WF6.mjs.map} +1 -1
- package/dist/{error-D_-tqP-I.mjs → error-DqnRMM5z.mjs} +1 -1
- package/dist/{error-D_-tqP-I.mjs.map → error-DqnRMM5z.mjs.map} +1 -1
- package/dist/{index-BFRaVcD6.d.mts → index-Cg-rC4Gj.d.mts} +110 -87
- package/dist/index-Cg-rC4Gj.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +29 -28
- package/dist/{load-DDqMMvZL.mjs → load-DR1VwFXR.mjs} +2 -2
- package/dist/{load-DDqMMvZL.mjs.map → load-DR1VwFXR.mjs.map} +1 -1
- package/dist/{loader-CKLbBnhK.mjs → loader-ou_PXAjg.mjs} +31 -6
- package/dist/loader-ou_PXAjg.mjs.map +1 -0
- package/dist/{manifest-schema-DqWNC3lM.mjs → manifest-schema-CXAbd1vH.mjs} +1 -1
- package/dist/{manifest-schema-DqWNC3lM.mjs.map → manifest-schema-CXAbd1vH.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-BW32b4gi.mjs → media-1fFhub9c.mjs} +22 -10
- package/dist/media-1fFhub9c.mjs.map +1 -0
- package/dist/{mode-ier8jbBk.mjs → mode-YhqNVef_.mjs} +1 -1
- package/dist/{mode-ier8jbBk.mjs.map → mode-YhqNVef_.mjs.map} +1 -1
- package/dist/{options-BVp3UsTS.mjs → options-nPxWnrya.mjs} +1 -1
- package/dist/{options-BVp3UsTS.mjs.map → options-nPxWnrya.mjs.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{patterns-CrCYkMBb.mjs → patterns-DsUZ4uxI.mjs} +1 -1
- package/dist/{patterns-CrCYkMBb.mjs.map → patterns-DsUZ4uxI.mjs.map} +1 -1
- package/dist/{placeholder-BE4o_2dc.d.mts → placeholder-CDPtkelt.d.mts} +1 -1
- package/dist/{placeholder-BE4o_2dc.d.mts.map → placeholder-CDPtkelt.d.mts.map} +1 -1
- package/dist/{placeholder-CIJejMlK.mjs → placeholder-Ci0RLeCk.mjs} +1 -1
- package/dist/{placeholder-CIJejMlK.mjs.map → placeholder-Ci0RLeCk.mjs.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{public-url-DByxYjUw.mjs → public-url-B1AxbbbQ.mjs} +1 -1
- package/dist/{public-url-DByxYjUw.mjs.map → public-url-B1AxbbbQ.mjs.map} +1 -1
- package/dist/{query-Cg9ZKRQ0.mjs → query-8c_meo_K.mjs} +13 -13
- package/dist/{query-Cg9ZKRQ0.mjs.map → query-8c_meo_K.mjs.map} +1 -1
- package/dist/{redirect-BhUBKRc1.mjs → redirect-C5H7VGIX.mjs} +3 -3
- package/dist/{redirect-BhUBKRc1.mjs.map → redirect-C5H7VGIX.mjs.map} +1 -1
- package/dist/{registry-Dw70ChxB.mjs → registry-Do34mz_P.mjs} +7 -6
- package/dist/registry-Do34mz_P.mjs.map +1 -0
- package/dist/{request-cache-B-bmkipQ.mjs → request-cache-D4I69LeL.mjs} +6 -2
- package/dist/request-cache-D4I69LeL.mjs.map +1 -0
- package/dist/request-context.d.mts +27 -1
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs +16 -3
- package/dist/request-context.mjs.map +1 -1
- package/dist/{runner-C7ADox5q.mjs → runner-DIcU2UCC.mjs} +465 -148
- package/dist/runner-DIcU2UCC.mjs.map +1 -0
- package/dist/{runner-Bnoj7vjK.d.mts → runner-Iu3IZSDM.d.mts} +2 -2
- package/dist/{runner-Bnoj7vjK.d.mts.map → runner-Iu3IZSDM.d.mts.map} +1 -1
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +3 -3
- package/dist/{search-dOGEccMa.mjs → search-DuWhx4NG.mjs} +322 -108
- package/dist/search-DuWhx4NG.mjs.map +1 -0
- package/dist/{secrets-CW3reAnU.mjs → secrets-CZ8rxLX3.mjs} +3 -3
- package/dist/{secrets-CW3reAnU.mjs.map → secrets-CZ8rxLX3.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +15 -14
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/taxonomies-Bw76xAxo.mjs +407 -0
- package/dist/taxonomies-Bw76xAxo.mjs.map +1 -0
- package/dist/taxonomy-D6NvlKo8.mjs +218 -0
- package/dist/taxonomy-D6NvlKo8.mjs.map +1 -0
- package/dist/{tokens-D7zMmWi2.mjs → tokens-CyRDPVW2.mjs} +2 -2
- package/dist/{tokens-D7zMmWi2.mjs.map → tokens-CyRDPVW2.mjs.map} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs → transaction-D44LBXvU.mjs} +1 -1
- package/dist/{transaction-Cn2rjY78.mjs.map → transaction-D44LBXvU.mjs.map} +1 -1
- package/dist/{transport-DNEfeMaU.d.mts → transport-DX_5rpsq.d.mts} +1 -1
- package/dist/{transport-DNEfeMaU.d.mts.map → transport-DX_5rpsq.d.mts.map} +1 -1
- package/dist/{transport-BeMCmin1.mjs → transport-xpzIjCIB.mjs} +1 -1
- package/dist/{transport-BeMCmin1.mjs.map → transport-xpzIjCIB.mjs.map} +1 -1
- package/dist/{types-CIOg5AR8.mjs → types-56BKbld_.mjs} +1 -1
- package/dist/types-56BKbld_.mjs.map +1 -0
- package/dist/{types-CRxNbK-Z.mjs → types-BIgulNsW.mjs} +2 -2
- package/dist/{types-CRxNbK-Z.mjs.map → types-BIgulNsW.mjs.map} +1 -1
- package/dist/{types-CrtWgIvl.d.mts → types-BQx6ZXpR.d.mts} +10 -1
- package/dist/types-BQx6ZXpR.d.mts.map +1 -0
- package/dist/{types-CJsYGpco.d.mts → types-B_CXXnzh.d.mts} +1 -1
- package/dist/{types-CJsYGpco.d.mts.map → types-B_CXXnzh.d.mts.map} +1 -1
- package/dist/{types-M78DQ1lx.d.mts → types-C-aFbqmA.d.mts} +1 -1
- package/dist/{types-M78DQ1lx.d.mts.map → types-C-aFbqmA.d.mts.map} +1 -1
- package/dist/types-DiI8NOG_.mjs +16 -0
- package/dist/types-DiI8NOG_.mjs.map +1 -0
- package/dist/{types-BuBIptGk.d.mts → types-IN5z_S3P.d.mts} +158 -92
- package/dist/types-IN5z_S3P.d.mts.map +1 -0
- package/dist/{types-BSyXeCFW.d.mts → types-IZSZfEwv.d.mts} +4 -3
- package/dist/types-IZSZfEwv.d.mts.map +1 -0
- package/dist/{types-CDbKp7ND.mjs → types-K-EkEQCI.mjs} +1 -1
- package/dist/{types-CDbKp7ND.mjs.map → types-K-EkEQCI.mjs.map} +1 -1
- package/dist/{validate-BfQh_C_y.d.mts → validate-CO3JjFV5.d.mts} +22 -5
- package/dist/validate-CO3JjFV5.d.mts.map +1 -0
- package/dist/{validate-Baqf0slj.mjs → validate-UK4Ja1uo.mjs} +14 -10
- package/dist/validate-UK4Ja1uo.mjs.map +1 -0
- package/dist/{validation-BfEI7tNe.mjs → validation-Vc5DQkJa.mjs} +5 -5
- package/dist/{validation-BfEI7tNe.mjs.map → validation-Vc5DQkJa.mjs.map} +1 -1
- package/dist/version-Bg31I_Ff.mjs +7 -0
- package/dist/{version-DoxrVdYf.mjs.map → version-Bg31I_Ff.mjs.map} +1 -1
- package/dist/{zod-generator-CC0xNe_K.mjs → zod-generator-CHnJUP2l.mjs} +8 -3
- package/dist/zod-generator-CHnJUP2l.mjs.map +1 -0
- package/package.json +9 -8
- package/src/api/errors.ts +5 -0
- package/src/api/handlers/content.ts +20 -0
- package/src/api/handlers/dashboard.ts +29 -36
- package/src/api/handlers/media-allowlist.ts +40 -0
- package/src/api/handlers/media.ts +1 -1
- package/src/api/handlers/menus.ts +400 -89
- package/src/api/handlers/taxonomies.ts +273 -97
- package/src/api/handlers/validate-media-fields.ts +125 -0
- package/src/api/schemas/common.ts +7 -0
- package/src/api/schemas/media.ts +23 -3
- package/src/api/schemas/menus.ts +23 -0
- package/src/api/schemas/schema.ts +11 -2
- package/src/api/schemas/taxonomies.ts +39 -0
- package/src/astro/integration/routes.ts +10 -0
- package/src/astro/middleware.ts +46 -11
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +1 -1
- package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +196 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +9 -177
- package/src/astro/routes/api/media/upload-url.ts +10 -4
- package/src/astro/routes/api/media.ts +12 -4
- package/src/astro/routes/api/menus/[name]/items.ts +16 -6
- package/src/astro/routes/api/menus/[name]/reorder.ts +8 -3
- package/src/astro/routes/api/menus/[name]/translations.ts +82 -0
- package/src/astro/routes/api/menus/[name].ts +19 -10
- package/src/astro/routes/api/menus/index.ts +9 -6
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +89 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +22 -22
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +11 -14
- package/src/astro/routes/api/taxonomies/index.ts +9 -6
- package/src/astro/types.ts +5 -1
- package/src/auth/rate-limit.ts +3 -3
- package/src/cli/commands/bundle-utils.ts +81 -6
- package/src/cli/commands/bundle.ts +18 -15
- package/src/cli/commands/export-seed.ts +139 -24
- package/src/cli/commands/plugin-init.ts +216 -90
- package/src/database/instrumentation.ts +22 -8
- package/src/database/migrations/016_api_tokens.ts +18 -3
- package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +477 -0
- package/src/database/migrations/037_credential_algorithm.ts +18 -0
- package/src/database/migrations/runner.ts +4 -0
- package/src/database/repositories/content.ts +11 -0
- package/src/database/repositories/media.ts +40 -10
- package/src/database/repositories/taxonomy.ts +193 -89
- package/src/database/types.ts +12 -3
- package/src/emdash-runtime.ts +16 -3
- package/src/fields/file.ts +7 -6
- package/src/fields/image.ts +12 -11
- package/src/fields/types.ts +3 -0
- package/src/i18n/resolve.ts +37 -0
- package/src/index.ts +1 -1
- package/src/loader.ts +49 -2
- package/src/mcp/server.ts +114 -26
- package/src/media/mime.ts +75 -0
- package/src/menus/index.ts +143 -124
- package/src/menus/types.ts +15 -1
- package/src/plugins/types.ts +81 -191
- package/src/request-cache.ts +6 -2
- package/src/request-context.ts +42 -2
- package/src/schema/registry.ts +5 -5
- package/src/schema/types.ts +3 -2
- package/src/schema/zod-generator.ts +12 -2
- package/src/seed/apply.ts +157 -54
- package/src/seed/types.ts +18 -1
- package/src/seed/validate.ts +27 -13
- package/src/taxonomies/index.ts +230 -213
- package/src/taxonomies/types.ts +10 -0
- package/dist/apply-BzltprvY.mjs.map +0 -1
- package/dist/content-8lOYF0pr.mjs.map +0 -1
- package/dist/index-BFRaVcD6.d.mts.map +0 -1
- package/dist/loader-CKLbBnhK.mjs.map +0 -1
- package/dist/media-BW32b4gi.mjs.map +0 -1
- package/dist/registry-Dw70ChxB.mjs.map +0 -1
- package/dist/request-cache-B-bmkipQ.mjs.map +0 -1
- package/dist/runner-C7ADox5q.mjs.map +0 -1
- package/dist/search-dOGEccMa.mjs.map +0 -1
- package/dist/taxonomies-ZlRtD6AG.mjs +0 -315
- package/dist/taxonomies-ZlRtD6AG.mjs.map +0 -1
- package/dist/types-4fVtCIm0.mjs +0 -68
- package/dist/types-4fVtCIm0.mjs.map +0 -1
- package/dist/types-BSyXeCFW.d.mts.map +0 -1
- package/dist/types-BuBIptGk.d.mts.map +0 -1
- package/dist/types-CIOg5AR8.mjs.map +0 -1
- package/dist/types-CrtWgIvl.d.mts.map +0 -1
- package/dist/validate-Baqf0slj.mjs.map +0 -1
- package/dist/validate-BfQh_C_y.d.mts.map +0 -1
- package/dist/version-DoxrVdYf.mjs +0 -7
- package/dist/zod-generator-CC0xNe_K.mjs.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as __exportAll, r as runMigrations, t as getMigrationStatus } from "../runner-
|
|
2
|
+
import { i as __exportAll, r as runMigrations, t as getMigrationStatus } from "../runner-DIcU2UCC.mjs";
|
|
3
3
|
import { n as createDatabase } from "../connection-2igzM-AT.mjs";
|
|
4
4
|
import { c as listTablesLike } from "../dialect-helpers-BKCvISIQ.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import "../
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { t as
|
|
11
|
-
import "../
|
|
12
|
-
import "../
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import "../
|
|
16
|
-
import "../
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { t as
|
|
20
|
-
import {
|
|
5
|
+
import { r as isI18nEnabled } from "../config-CVssduLe.mjs";
|
|
6
|
+
import { a as slugify, t as ContentRepository } from "../content-CERxPUN0.mjs";
|
|
7
|
+
import { i as encodeBase64url } from "../base64-MBPo9ozB.mjs";
|
|
8
|
+
import "../types-BIgulNsW.mjs";
|
|
9
|
+
import { t as MediaRepository } from "../media-1fFhub9c.mjs";
|
|
10
|
+
import { t as TaxonomyRepository } from "../taxonomy-D6NvlKo8.mjs";
|
|
11
|
+
import { t as OptionsRepository } from "../options-nPxWnrya.mjs";
|
|
12
|
+
import "../redirect-C5H7VGIX.mjs";
|
|
13
|
+
import "../byline-gFn1r0vA.mjs";
|
|
14
|
+
import "../request-cache-D4I69LeL.mjs";
|
|
15
|
+
import { n as SchemaRegistry } from "../registry-Do34mz_P.mjs";
|
|
16
|
+
import "../loader-ou_PXAjg.mjs";
|
|
17
|
+
import { t as applySeed } from "../apply-Ded_1vng.mjs";
|
|
18
|
+
import { i as pluginManifestSchema } from "../manifest-schema-CXAbd1vH.mjs";
|
|
19
|
+
import { n as isDeprecatedCapability, t as CAPABILITY_RENAMES } from "../types-DiI8NOG_.mjs";
|
|
20
|
+
import { t as validateSeed } from "../validate-UK4Ja1uo.mjs";
|
|
21
|
+
import { n as fingerprintKey, r as generateEncryptionKey, t as EmDashSecretsError } from "../secrets-CZ8rxLX3.mjs";
|
|
21
22
|
import { LocalStorage } from "../storage/local.mjs";
|
|
22
|
-
import { o as convertDataForRead } from "../transport-
|
|
23
|
+
import { o as convertDataForRead } from "../transport-xpzIjCIB.mjs";
|
|
23
24
|
import { createHeaderAwareFetch, customHeadersInterceptor, getCachedAccessToken, isAccessRedirect, resolveCustomHeaders, runCloudflaredLogin } from "../client/cf-access.mjs";
|
|
24
25
|
import { EmDashClient } from "../client/index.mjs";
|
|
25
26
|
import { imageSize } from "image-size";
|
|
@@ -1248,49 +1249,96 @@ async function exportCollections(db) {
|
|
|
1248
1249
|
* Export taxonomy definitions and terms
|
|
1249
1250
|
*/
|
|
1250
1251
|
async function exportTaxonomies(db) {
|
|
1251
|
-
const
|
|
1252
|
+
const i18nEnabled = isI18nEnabled();
|
|
1253
|
+
const defs = await db.selectFrom("_emdash_taxonomy_defs").selectAll().orderBy(["name", "locale"]).execute();
|
|
1252
1254
|
const result = [];
|
|
1253
1255
|
const termRepo = new TaxonomyRepository(db);
|
|
1256
|
+
const defGroupToSeedId = /* @__PURE__ */ new Map();
|
|
1254
1257
|
for (const def of defs) {
|
|
1255
|
-
const
|
|
1256
|
-
const
|
|
1258
|
+
const defSeedId = i18nEnabled && def.locale ? `tax:${def.name}:${def.locale}` : `tax:${def.name}`;
|
|
1259
|
+
const terms = await termRepo.findByName(def.name, { locale: def.locale });
|
|
1257
1260
|
const idToSlug = /* @__PURE__ */ new Map();
|
|
1258
1261
|
for (const term of terms) idToSlug.set(term.id, term.slug);
|
|
1262
|
+
const termGroupToSeedId = /* @__PURE__ */ new Map();
|
|
1263
|
+
const seedTerms = [];
|
|
1259
1264
|
for (const term of terms) {
|
|
1265
|
+
const termSeedId = i18nEnabled && term.locale ? `term:${def.name}:${term.slug}:${term.locale}` : `term:${def.name}:${term.slug}`;
|
|
1260
1266
|
const seedTerm = {
|
|
1267
|
+
id: termSeedId,
|
|
1261
1268
|
slug: term.slug,
|
|
1262
1269
|
label: term.label,
|
|
1263
1270
|
description: typeof term.data?.description === "string" ? term.data.description : void 0
|
|
1264
1271
|
};
|
|
1265
1272
|
if (term.parentId) seedTerm.parent = idToSlug.get(term.parentId);
|
|
1273
|
+
if (i18nEnabled && term.locale) {
|
|
1274
|
+
seedTerm.locale = term.locale;
|
|
1275
|
+
if (term.translationGroup) {
|
|
1276
|
+
const anchor = termGroupToSeedId.get(term.translationGroup);
|
|
1277
|
+
if (anchor) seedTerm.translationOf = anchor;
|
|
1278
|
+
else termGroupToSeedId.set(term.translationGroup, termSeedId);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1266
1281
|
seedTerms.push(seedTerm);
|
|
1267
1282
|
}
|
|
1283
|
+
seedTerms.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
|
|
1268
1284
|
const taxonomy = {
|
|
1285
|
+
id: defSeedId,
|
|
1269
1286
|
name: def.name,
|
|
1270
1287
|
label: def.label,
|
|
1271
1288
|
labelSingular: def.label_singular || void 0,
|
|
1272
1289
|
hierarchical: def.hierarchical === 1,
|
|
1273
1290
|
collections: def.collections ? JSON.parse(def.collections) : []
|
|
1274
1291
|
};
|
|
1292
|
+
if (i18nEnabled && def.locale) {
|
|
1293
|
+
taxonomy.locale = def.locale;
|
|
1294
|
+
if (def.translation_group) {
|
|
1295
|
+
const anchor = defGroupToSeedId.get(def.translation_group);
|
|
1296
|
+
if (anchor) taxonomy.translationOf = anchor;
|
|
1297
|
+
else defGroupToSeedId.set(def.translation_group, defSeedId);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1275
1300
|
if (seedTerms.length > 0) taxonomy.terms = seedTerms;
|
|
1276
1301
|
result.push(taxonomy);
|
|
1277
1302
|
}
|
|
1303
|
+
result.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
|
|
1278
1304
|
return result;
|
|
1279
1305
|
}
|
|
1280
1306
|
/**
|
|
1281
1307
|
* Export menus with their items
|
|
1282
1308
|
*/
|
|
1283
1309
|
async function exportMenus(db) {
|
|
1284
|
-
const
|
|
1310
|
+
const i18nEnabled = isI18nEnabled();
|
|
1311
|
+
const menus = await db.selectFrom("_emdash_menus").selectAll().orderBy(["name", "locale"]).execute();
|
|
1285
1312
|
const result = [];
|
|
1313
|
+
const groupToSeedId = /* @__PURE__ */ new Map();
|
|
1314
|
+
const itemGroupToSeedId = /* @__PURE__ */ new Map();
|
|
1315
|
+
const usedItemSeedIds = /* @__PURE__ */ new Set();
|
|
1286
1316
|
for (const menu of menus) {
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1317
|
+
const seedId = i18nEnabled && menu.locale ? `menu:${menu.name}:${menu.locale}` : `menu:${menu.name}`;
|
|
1318
|
+
const seedItems = buildMenuItemTree(await db.selectFrom("_emdash_menu_items").selectAll().where("menu_id", "=", menu.id).orderBy("sort_order", "asc").execute(), {
|
|
1319
|
+
i18nEnabled,
|
|
1320
|
+
menuName: menu.name,
|
|
1321
|
+
menuLocale: menu.locale ?? null,
|
|
1322
|
+
itemGroupToSeedId,
|
|
1323
|
+
usedItemSeedIds
|
|
1324
|
+
});
|
|
1325
|
+
const seedMenu = {
|
|
1326
|
+
id: seedId,
|
|
1289
1327
|
name: menu.name,
|
|
1290
1328
|
label: menu.label,
|
|
1291
1329
|
items: seedItems
|
|
1292
|
-
}
|
|
1330
|
+
};
|
|
1331
|
+
if (i18nEnabled && menu.locale) {
|
|
1332
|
+
seedMenu.locale = menu.locale;
|
|
1333
|
+
if (menu.translation_group) {
|
|
1334
|
+
const anchor = groupToSeedId.get(menu.translation_group);
|
|
1335
|
+
if (anchor) seedMenu.translationOf = anchor;
|
|
1336
|
+
else groupToSeedId.set(menu.translation_group, seedId);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
result.push(seedMenu);
|
|
1293
1340
|
}
|
|
1341
|
+
result.sort((a, b) => Number(!!a.translationOf) - Number(!!b.translationOf));
|
|
1294
1342
|
return result;
|
|
1295
1343
|
}
|
|
1296
1344
|
/** Type guard for valid widget types */
|
|
@@ -1300,13 +1348,25 @@ function isWidgetType(t) {
|
|
|
1300
1348
|
/**
|
|
1301
1349
|
* Build hierarchical menu item tree from flat array
|
|
1302
1350
|
*/
|
|
1303
|
-
function buildMenuItemTree(items) {
|
|
1351
|
+
function buildMenuItemTree(items, i18nCtx) {
|
|
1304
1352
|
const childMap = /* @__PURE__ */ new Map();
|
|
1305
1353
|
for (const item of items) {
|
|
1306
1354
|
const parentId = item.parent_id;
|
|
1307
1355
|
if (!childMap.has(parentId)) childMap.set(parentId, []);
|
|
1308
1356
|
childMap.get(parentId).push(item);
|
|
1309
1357
|
}
|
|
1358
|
+
function makeSeedId(item) {
|
|
1359
|
+
const base = slugify(item.label || "") || item.id;
|
|
1360
|
+
const locale = i18nCtx.i18nEnabled ? item.locale ?? i18nCtx.menuLocale : null;
|
|
1361
|
+
const candidate = locale ? `item:${i18nCtx.menuName}:${base}:${locale}` : `item:${i18nCtx.menuName}:${base}`;
|
|
1362
|
+
if (!i18nCtx.usedItemSeedIds.has(candidate)) {
|
|
1363
|
+
i18nCtx.usedItemSeedIds.add(candidate);
|
|
1364
|
+
return candidate;
|
|
1365
|
+
}
|
|
1366
|
+
const fallback = locale ? `item:${i18nCtx.menuName}:${base}:${item.id}:${locale}` : `item:${i18nCtx.menuName}:${base}:${item.id}`;
|
|
1367
|
+
i18nCtx.usedItemSeedIds.add(fallback);
|
|
1368
|
+
return fallback;
|
|
1369
|
+
}
|
|
1310
1370
|
function buildLevel(parentId) {
|
|
1311
1371
|
return (childMap.get(parentId) || []).map((item) => {
|
|
1312
1372
|
const seedItem = {
|
|
@@ -1321,6 +1381,17 @@ function buildMenuItemTree(items) {
|
|
|
1321
1381
|
if (item.target === "_blank") seedItem.target = "_blank";
|
|
1322
1382
|
if (item.title_attr) seedItem.titleAttr = item.title_attr;
|
|
1323
1383
|
if (item.css_classes) seedItem.cssClasses = item.css_classes;
|
|
1384
|
+
if (i18nCtx.i18nEnabled) {
|
|
1385
|
+
const itemLocale = item.locale ?? i18nCtx.menuLocale;
|
|
1386
|
+
const seedId = makeSeedId(item);
|
|
1387
|
+
seedItem.id = seedId;
|
|
1388
|
+
if (itemLocale) seedItem.locale = itemLocale;
|
|
1389
|
+
if (item.translation_group) {
|
|
1390
|
+
const anchor = i18nCtx.itemGroupToSeedId.get(item.translation_group);
|
|
1391
|
+
if (anchor && anchor !== seedId) seedItem.translationOf = anchor;
|
|
1392
|
+
else if (!anchor) i18nCtx.itemGroupToSeedId.set(item.translation_group, seedId);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1324
1395
|
const itemChildren = buildLevel(item.id);
|
|
1325
1396
|
if (itemChildren.length > 0) seedItem.children = itemChildren;
|
|
1326
1397
|
return seedItem;
|
|
@@ -2156,7 +2227,9 @@ const menuCommand = defineCommand({
|
|
|
2156
2227
|
* Shared logic extracted from the bundle command so it can be tested
|
|
2157
2228
|
* without the CLI harness and tsdown dependency.
|
|
2158
2229
|
*/
|
|
2159
|
-
const MAX_BUNDLE_SIZE =
|
|
2230
|
+
const MAX_BUNDLE_SIZE = 256 * 1024;
|
|
2231
|
+
const MAX_FILE_SIZE = 128 * 1024;
|
|
2232
|
+
const MAX_FILE_COUNT = 20;
|
|
2160
2233
|
const MAX_SCREENSHOTS = 5;
|
|
2161
2234
|
const MAX_SCREENSHOT_WIDTH = 1920;
|
|
2162
2235
|
const MAX_SCREENSHOT_HEIGHT = 1080;
|
|
@@ -2329,21 +2402,66 @@ function findSourceExports(exports) {
|
|
|
2329
2402
|
return issues;
|
|
2330
2403
|
}
|
|
2331
2404
|
/**
|
|
2332
|
-
* Recursively
|
|
2405
|
+
* Recursively walk a staging directory and return a flat list of all files
|
|
2406
|
+
* with sizes. Names are relative to `dir` so they match what would appear
|
|
2407
|
+
* as the tarball entry name.
|
|
2333
2408
|
*/
|
|
2334
|
-
async function
|
|
2335
|
-
|
|
2409
|
+
async function collectBundleEntries(dir) {
|
|
2410
|
+
const entries = [];
|
|
2411
|
+
await walkBundle(dir, "", entries);
|
|
2412
|
+
return entries;
|
|
2413
|
+
}
|
|
2414
|
+
async function walkBundle(dir, prefix, into) {
|
|
2336
2415
|
const items = await readdir(dir, { withFileTypes: true });
|
|
2337
2416
|
for (const item of items) {
|
|
2338
2417
|
const fullPath = join(dir, item.name);
|
|
2418
|
+
const relPath = prefix ? `${prefix}/${item.name}` : item.name;
|
|
2339
2419
|
if (item.isFile()) {
|
|
2340
2420
|
const s = await stat(fullPath);
|
|
2341
|
-
|
|
2342
|
-
|
|
2421
|
+
into.push({
|
|
2422
|
+
name: relPath,
|
|
2423
|
+
bytes: s.size
|
|
2424
|
+
});
|
|
2425
|
+
} else if (item.isDirectory()) await walkBundle(fullPath, relPath, into);
|
|
2343
2426
|
}
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Sum the byte sizes of all entries.
|
|
2430
|
+
*/
|
|
2431
|
+
function totalBundleBytes(entries) {
|
|
2432
|
+
let total = 0;
|
|
2433
|
+
for (const e of entries) total += e.bytes;
|
|
2344
2434
|
return total;
|
|
2345
2435
|
}
|
|
2346
2436
|
/**
|
|
2437
|
+
* Check a bundle against the three size caps from RFC 0001:
|
|
2438
|
+
* - total decompressed ≤ MAX_BUNDLE_SIZE
|
|
2439
|
+
* - per-file decompressed ≤ MAX_FILE_SIZE
|
|
2440
|
+
* - file count ≤ MAX_FILE_COUNT
|
|
2441
|
+
*
|
|
2442
|
+
* Returns a list of violation messages (empty if the bundle is within all
|
|
2443
|
+
* caps). Messages are deterministic per input — the total/count violations
|
|
2444
|
+
* come first, then oversized files in alphabetical order — so the same
|
|
2445
|
+
* bundle always produces the same error text.
|
|
2446
|
+
*/
|
|
2447
|
+
function validateBundleSize(entries) {
|
|
2448
|
+
const violations = [];
|
|
2449
|
+
const total = totalBundleBytes(entries);
|
|
2450
|
+
if (total > MAX_BUNDLE_SIZE) violations.push(`Bundle size ${formatBytes(total)} exceeds maximum of ${formatBytes(MAX_BUNDLE_SIZE)}.`);
|
|
2451
|
+
if (entries.length > MAX_FILE_COUNT) violations.push(`Bundle contains ${entries.length} files, exceeds maximum of ${MAX_FILE_COUNT}.`);
|
|
2452
|
+
const oversized = entries.filter((e) => e.bytes > MAX_FILE_SIZE).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
2453
|
+
for (const e of oversized) violations.push(`File ${e.name} is ${formatBytes(e.bytes)}, exceeds per-file maximum of ${formatBytes(MAX_FILE_SIZE)}.`);
|
|
2454
|
+
return violations;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Render a byte count as a human-friendly string (e.g. "256.0 KB").
|
|
2458
|
+
*/
|
|
2459
|
+
function formatBytes(n) {
|
|
2460
|
+
if (n < 1024) return `${n} B`;
|
|
2461
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
2462
|
+
return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2347
2465
|
* Create a gzipped tarball from a directory.
|
|
2348
2466
|
*/
|
|
2349
2467
|
async function createTarball(sourceDir, outputPath) {
|
|
@@ -2686,15 +2804,12 @@ const bundleCommand = defineCommand({
|
|
|
2686
2804
|
hasErrors = true;
|
|
2687
2805
|
}
|
|
2688
2806
|
}
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2807
|
+
const bundleEntries = await collectBundleEntries(bundleDir);
|
|
2808
|
+
const sizeViolations = validateBundleSize(bundleEntries);
|
|
2809
|
+
if (sizeViolations.length > 0) {
|
|
2810
|
+
for (const v of sizeViolations) consola.error(v);
|
|
2693
2811
|
hasErrors = true;
|
|
2694
|
-
} else {
|
|
2695
|
-
const sizeKB = (totalSize / 1024).toFixed(1);
|
|
2696
|
-
consola.info(`Bundle size: ${sizeKB}KB`);
|
|
2697
|
-
}
|
|
2812
|
+
} else consola.info(`Bundle size: ${formatBytes(totalBundleBytes(bundleEntries))} across ${bundleEntries.length} file${bundleEntries.length === 1 ? "" : "s"}`);
|
|
2698
2813
|
if (hasErrors) {
|
|
2699
2814
|
consola.error("Bundle validation failed");
|
|
2700
2815
|
process.exit(1);
|
|
@@ -2726,13 +2841,15 @@ const bundleCommand = defineCommand({
|
|
|
2726
2841
|
/**
|
|
2727
2842
|
* emdash plugin init
|
|
2728
2843
|
*
|
|
2729
|
-
* Scaffold a new EmDash plugin. Generates the
|
|
2844
|
+
* Scaffold a new EmDash plugin. Generates the sandboxed-format boilerplate:
|
|
2730
2845
|
* src/index.ts -- descriptor factory
|
|
2731
2846
|
* src/sandbox-entry.ts -- definePlugin({ hooks, routes })
|
|
2732
2847
|
* package.json
|
|
2733
2848
|
* tsconfig.json
|
|
2734
2849
|
*
|
|
2735
|
-
* Use --native to generate native-format boilerplate
|
|
2850
|
+
* Use --format=native (or --native) to generate native-format boilerplate
|
|
2851
|
+
* instead (createPlugin + React admin). When neither is passed and stdout
|
|
2852
|
+
* is a TTY, the user is prompted to choose.
|
|
2736
2853
|
*
|
|
2737
2854
|
*/
|
|
2738
2855
|
const SLUG_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -2752,15 +2869,25 @@ const pluginInitCommand = defineCommand({
|
|
|
2752
2869
|
type: "string",
|
|
2753
2870
|
description: "Plugin name/id (e.g. my-plugin or @org/my-plugin)"
|
|
2754
2871
|
},
|
|
2872
|
+
format: {
|
|
2873
|
+
type: "string",
|
|
2874
|
+
description: "Plugin format: sandboxed or native. Prompts when running interactively if not set.",
|
|
2875
|
+
valueHint: "sandboxed|native"
|
|
2876
|
+
},
|
|
2755
2877
|
native: {
|
|
2756
2878
|
type: "boolean",
|
|
2757
|
-
description: "
|
|
2879
|
+
description: "Shortcut for --format=native",
|
|
2758
2880
|
default: false
|
|
2759
2881
|
}
|
|
2760
2882
|
},
|
|
2761
2883
|
async run({ args }) {
|
|
2762
2884
|
const targetDir = resolve(args.dir);
|
|
2763
|
-
const
|
|
2885
|
+
const format = await resolveFormat(args.format, args.native);
|
|
2886
|
+
if (!format) {
|
|
2887
|
+
consola.info("Cancelled");
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
const isNative = format === "native";
|
|
2764
2891
|
let pluginName = args.name || basename(targetDir);
|
|
2765
2892
|
if (!pluginName || pluginName === ".") pluginName = basename(resolve("."));
|
|
2766
2893
|
const slug = pluginName.replace(SCOPE_RE, "");
|
|
@@ -2773,69 +2900,162 @@ const pluginInitCommand = defineCommand({
|
|
|
2773
2900
|
consola.error(`package.json already exists in ${targetDir}`);
|
|
2774
2901
|
process.exit(1);
|
|
2775
2902
|
}
|
|
2776
|
-
consola.start(`Scaffolding ${isNative ? "native" : "
|
|
2903
|
+
consola.start(`Scaffolding ${isNative ? "native" : "sandboxed"} plugin: ${pluginName}`);
|
|
2777
2904
|
await mkdir(srcDir, { recursive: true });
|
|
2778
2905
|
if (isNative) await scaffoldNative(targetDir, srcDir, pluginName, slug);
|
|
2779
2906
|
else await scaffoldStandard(targetDir, srcDir, pluginName, slug);
|
|
2780
2907
|
consola.success(`Plugin scaffolded in ${targetDir}`);
|
|
2781
2908
|
consola.info("Next steps:");
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2909
|
+
const steps = [];
|
|
2910
|
+
if (args.dir !== ".") steps.push(`cd ${args.dir}`);
|
|
2911
|
+
steps.push("pnpm install");
|
|
2912
|
+
steps.push(isNative ? "Edit src/index.ts to add hooks and routes" : "Edit src/sandbox-entry.ts to add hooks and routes");
|
|
2913
|
+
steps.push("pnpm build");
|
|
2914
|
+
if (!isNative) steps.push("emdash plugin validate --dir .");
|
|
2915
|
+
steps.forEach((step, i) => consola.info(` ${i + 1}. ${step}`));
|
|
2787
2916
|
}
|
|
2788
2917
|
});
|
|
2918
|
+
async function resolveFormat(formatArg, nativeFlag) {
|
|
2919
|
+
if (formatArg) {
|
|
2920
|
+
const normalized = formatArg.toLowerCase();
|
|
2921
|
+
let parsed;
|
|
2922
|
+
if (normalized === "native") parsed = "native";
|
|
2923
|
+
else if (normalized === "sandboxed" || normalized === "standard") parsed = "standard";
|
|
2924
|
+
else {
|
|
2925
|
+
consola.error(`Invalid --format "${formatArg}". Use "sandboxed" or "native".`);
|
|
2926
|
+
process.exit(1);
|
|
2927
|
+
}
|
|
2928
|
+
if (nativeFlag && parsed !== "native") {
|
|
2929
|
+
consola.error(`Conflicting flags: --native and --format=${formatArg}. Pass only one.`);
|
|
2930
|
+
process.exit(1);
|
|
2931
|
+
}
|
|
2932
|
+
return parsed;
|
|
2933
|
+
}
|
|
2934
|
+
if (nativeFlag) return "native";
|
|
2935
|
+
if (!process.stdout.isTTY) return "standard";
|
|
2936
|
+
const choice = await consola.prompt("Which plugin format?", {
|
|
2937
|
+
type: "select",
|
|
2938
|
+
initial: "standard",
|
|
2939
|
+
options: [{
|
|
2940
|
+
label: "Sandboxed",
|
|
2941
|
+
value: "standard",
|
|
2942
|
+
hint: "runs in an isolated sandbox; safe to install from the marketplace"
|
|
2943
|
+
}, {
|
|
2944
|
+
label: "Native",
|
|
2945
|
+
value: "native",
|
|
2946
|
+
hint: "full runtime access; install from npm"
|
|
2947
|
+
}],
|
|
2948
|
+
cancel: "null"
|
|
2949
|
+
});
|
|
2950
|
+
if (choice === null) return null;
|
|
2951
|
+
return choice;
|
|
2952
|
+
}
|
|
2953
|
+
function camelCase(slug) {
|
|
2954
|
+
return slug.split("-").map((s, i) => i === 0 ? s : s[0].toUpperCase() + s.slice(1)).join("");
|
|
2955
|
+
}
|
|
2956
|
+
function pascalCase(slug) {
|
|
2957
|
+
return slug.split("-").map((s) => s[0].toUpperCase() + s.slice(1)).join("");
|
|
2958
|
+
}
|
|
2959
|
+
const TSCONFIG = {
|
|
2960
|
+
compilerOptions: {
|
|
2961
|
+
target: "ES2022",
|
|
2962
|
+
module: "preserve",
|
|
2963
|
+
moduleResolution: "bundler",
|
|
2964
|
+
strict: true,
|
|
2965
|
+
esModuleInterop: true,
|
|
2966
|
+
declaration: true,
|
|
2967
|
+
outDir: "./dist",
|
|
2968
|
+
rootDir: "./src"
|
|
2969
|
+
},
|
|
2970
|
+
include: ["src/**/*"],
|
|
2971
|
+
exclude: ["node_modules", "dist"]
|
|
2972
|
+
};
|
|
2973
|
+
const TSDOWN_VERSION = "^0.20.0";
|
|
2974
|
+
const TYPESCRIPT_VERSION = "^5.9.0";
|
|
2789
2975
|
async function scaffoldStandard(targetDir, srcDir, pluginName, slug) {
|
|
2790
|
-
const fnName = slug
|
|
2976
|
+
const fnName = camelCase(slug);
|
|
2791
2977
|
await writeFile(join(targetDir, "package.json"), JSON.stringify({
|
|
2792
2978
|
name: pluginName,
|
|
2793
2979
|
version: "0.1.0",
|
|
2794
2980
|
type: "module",
|
|
2981
|
+
main: "./dist/index.mjs",
|
|
2795
2982
|
exports: {
|
|
2796
|
-
".":
|
|
2797
|
-
|
|
2983
|
+
".": {
|
|
2984
|
+
types: "./dist/index.d.mts",
|
|
2985
|
+
import: "./dist/index.mjs"
|
|
2986
|
+
},
|
|
2987
|
+
"./sandbox": {
|
|
2988
|
+
types: "./dist/sandbox-entry.d.mts",
|
|
2989
|
+
import: "./dist/sandbox-entry.mjs"
|
|
2990
|
+
}
|
|
2798
2991
|
},
|
|
2799
|
-
files: ["
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
target: "ES2022",
|
|
2805
|
-
module: "preserve",
|
|
2806
|
-
moduleResolution: "bundler",
|
|
2807
|
-
strict: true,
|
|
2808
|
-
esModuleInterop: true,
|
|
2809
|
-
declaration: true,
|
|
2810
|
-
outDir: "./dist",
|
|
2811
|
-
rootDir: "./src"
|
|
2992
|
+
files: ["dist"],
|
|
2993
|
+
scripts: {
|
|
2994
|
+
build: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean",
|
|
2995
|
+
dev: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --watch",
|
|
2996
|
+
typecheck: "tsc --noEmit"
|
|
2812
2997
|
},
|
|
2813
|
-
|
|
2814
|
-
|
|
2998
|
+
keywords: ["emdash", "emdash-plugin"],
|
|
2999
|
+
license: "MIT",
|
|
3000
|
+
peerDependencies: { emdash: "*" },
|
|
3001
|
+
devDependencies: {
|
|
3002
|
+
emdash: "*",
|
|
3003
|
+
tsdown: TSDOWN_VERSION,
|
|
3004
|
+
typescript: TYPESCRIPT_VERSION
|
|
3005
|
+
}
|
|
2815
3006
|
}, null, " ") + "\n");
|
|
3007
|
+
await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, " ") + "\n");
|
|
2816
3008
|
await writeFile(join(srcDir, "index.ts"), `import type { PluginDescriptor } from "emdash";
|
|
2817
3009
|
|
|
2818
3010
|
export function ${fnName}Plugin(): PluginDescriptor {
|
|
2819
3011
|
\treturn {
|
|
2820
|
-
\t\tid: "${
|
|
3012
|
+
\t\tid: "${slug}",
|
|
2821
3013
|
\t\tversion: "0.1.0",
|
|
2822
3014
|
\t\tformat: "standard",
|
|
2823
3015
|
\t\tentrypoint: "${pluginName}/sandbox",
|
|
2824
|
-
|
|
3016
|
+
|
|
3017
|
+
\t\tcapabilities: ["content:read"],
|
|
3018
|
+
\t\tstorage: {
|
|
3019
|
+
\t\t\tevents: { indexes: ["timestamp"] },
|
|
3020
|
+
\t\t},
|
|
2825
3021
|
\t};
|
|
2826
3022
|
}
|
|
2827
3023
|
`);
|
|
2828
3024
|
await writeFile(join(srcDir, "sandbox-entry.ts"), `import { definePlugin } from "emdash";
|
|
2829
3025
|
import type { PluginContext } from "emdash";
|
|
2830
3026
|
|
|
3027
|
+
interface ContentSaveEvent {
|
|
3028
|
+
\tcollection: string;
|
|
3029
|
+
\tcontent: { id: string };
|
|
3030
|
+
\tisNew: boolean;
|
|
3031
|
+
}
|
|
3032
|
+
|
|
2831
3033
|
export default definePlugin({
|
|
2832
3034
|
\thooks: {
|
|
2833
3035
|
\t\t"content:afterSave": {
|
|
2834
|
-
\t\t\thandler: async (event:
|
|
3036
|
+
\t\t\thandler: async (event: ContentSaveEvent, ctx: PluginContext) => {
|
|
2835
3037
|
\t\t\t\tctx.log.info("Content saved", {
|
|
2836
3038
|
\t\t\t\t\tcollection: event.collection,
|
|
2837
3039
|
\t\t\t\t\tid: event.content.id,
|
|
2838
3040
|
\t\t\t\t});
|
|
3041
|
+
|
|
3042
|
+
\t\t\t\tawait ctx.storage.events.put(\`save-\${Date.now()}\`, {
|
|
3043
|
+
\t\t\t\t\ttimestamp: new Date().toISOString(),
|
|
3044
|
+
\t\t\t\t\tcollection: event.collection,
|
|
3045
|
+
\t\t\t\t\tcontentId: event.content.id,
|
|
3046
|
+
\t\t\t\t});
|
|
3047
|
+
\t\t\t},
|
|
3048
|
+
\t\t},
|
|
3049
|
+
\t},
|
|
3050
|
+
|
|
3051
|
+
\troutes: {
|
|
3052
|
+
\t\trecent: {
|
|
3053
|
+
\t\t\thandler: async (_routeCtx, ctx: PluginContext) => {
|
|
3054
|
+
\t\t\t\tconst result = await ctx.storage.events.query({
|
|
3055
|
+
\t\t\t\t\torderBy: { timestamp: "desc" },
|
|
3056
|
+
\t\t\t\t\tlimit: 10,
|
|
3057
|
+
\t\t\t\t});
|
|
3058
|
+
\t\t\t\treturn { events: result.items };
|
|
2839
3059
|
\t\t\t},
|
|
2840
3060
|
\t\t},
|
|
2841
3061
|
\t},
|
|
@@ -2843,55 +3063,82 @@ export default definePlugin({
|
|
|
2843
3063
|
`);
|
|
2844
3064
|
}
|
|
2845
3065
|
async function scaffoldNative(targetDir, srcDir, pluginName, slug) {
|
|
2846
|
-
const fnName = slug
|
|
3066
|
+
const fnName = camelCase(slug);
|
|
3067
|
+
const typeName = pascalCase(slug);
|
|
2847
3068
|
await writeFile(join(targetDir, "package.json"), JSON.stringify({
|
|
2848
3069
|
name: pluginName,
|
|
2849
3070
|
version: "0.1.0",
|
|
2850
3071
|
type: "module",
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
esModuleInterop: true,
|
|
2862
|
-
declaration: true,
|
|
2863
|
-
outDir: "./dist",
|
|
2864
|
-
rootDir: "./src"
|
|
3072
|
+
main: "./dist/index.mjs",
|
|
3073
|
+
exports: { ".": {
|
|
3074
|
+
types: "./dist/index.d.mts",
|
|
3075
|
+
import: "./dist/index.mjs"
|
|
3076
|
+
} },
|
|
3077
|
+
files: ["dist"],
|
|
3078
|
+
scripts: {
|
|
3079
|
+
build: "tsdown src/index.ts --format esm --dts --clean",
|
|
3080
|
+
dev: "tsdown src/index.ts --format esm --dts --watch",
|
|
3081
|
+
typecheck: "tsc --noEmit"
|
|
2865
3082
|
},
|
|
2866
|
-
|
|
2867
|
-
|
|
3083
|
+
keywords: ["emdash", "emdash-plugin"],
|
|
3084
|
+
license: "MIT",
|
|
3085
|
+
peerDependencies: { emdash: "*" },
|
|
3086
|
+
devDependencies: {
|
|
3087
|
+
emdash: "*",
|
|
3088
|
+
tsdown: TSDOWN_VERSION,
|
|
3089
|
+
typescript: TYPESCRIPT_VERSION
|
|
3090
|
+
}
|
|
2868
3091
|
}, null, " ") + "\n");
|
|
3092
|
+
await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, " ") + "\n");
|
|
2869
3093
|
await writeFile(join(srcDir, "index.ts"), `import { definePlugin } from "emdash";
|
|
2870
3094
|
import type { PluginDescriptor } from "emdash";
|
|
2871
3095
|
|
|
2872
|
-
export
|
|
3096
|
+
export interface ${typeName}Options {
|
|
3097
|
+
\tenabled?: boolean;
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
export function ${fnName}Plugin(options: ${typeName}Options = {}): PluginDescriptor<${typeName}Options> {
|
|
2873
3101
|
\treturn {
|
|
2874
|
-
\t\tid: "${
|
|
3102
|
+
\t\tid: "${slug}",
|
|
2875
3103
|
\t\tversion: "0.1.0",
|
|
2876
3104
|
\t\tformat: "native",
|
|
2877
3105
|
\t\tentrypoint: "${pluginName}",
|
|
2878
|
-
\t\toptions
|
|
3106
|
+
\t\toptions,
|
|
2879
3107
|
\t};
|
|
2880
3108
|
}
|
|
2881
3109
|
|
|
2882
|
-
export function createPlugin() {
|
|
3110
|
+
export function createPlugin(options: ${typeName}Options = {}) {
|
|
2883
3111
|
\treturn definePlugin({
|
|
2884
|
-
\t\tid: "${
|
|
3112
|
+
\t\tid: "${slug}",
|
|
2885
3113
|
\t\tversion: "0.1.0",
|
|
2886
3114
|
|
|
3115
|
+
\t\tcapabilities: ["content:read"],
|
|
3116
|
+
\t\tstorage: {
|
|
3117
|
+
\t\t\tevents: { indexes: ["createdAt"] },
|
|
3118
|
+
\t\t},
|
|
3119
|
+
|
|
2887
3120
|
\t\thooks: {
|
|
2888
3121
|
\t\t\t"content:afterSave": async (event, ctx) => {
|
|
2889
|
-
\t\t\t\
|
|
3122
|
+
\t\t\t\tif (options.enabled === false) return;
|
|
3123
|
+
\t\t\t\tawait ctx.storage.events.put(\`evt_\${Date.now()}\`, {
|
|
2890
3124
|
\t\t\t\t\tcollection: event.collection,
|
|
2891
|
-
\t\t\t\t\
|
|
3125
|
+
\t\t\t\t\tcontentId: event.content.id,
|
|
3126
|
+
\t\t\t\t\tcreatedAt: new Date().toISOString(),
|
|
2892
3127
|
\t\t\t\t});
|
|
2893
3128
|
\t\t\t},
|
|
2894
3129
|
\t\t},
|
|
3130
|
+
|
|
3131
|
+
\t\troutes: {
|
|
3132
|
+
\t\t\trecent: {
|
|
3133
|
+
\t\t\t\thandler: async (ctx) => {
|
|
3134
|
+
\t\t\t\t\tconst result = await ctx.storage.events.query({
|
|
3135
|
+
\t\t\t\t\t\torderBy: { createdAt: "desc" },
|
|
3136
|
+
\t\t\t\t\t\tlimit: 10,
|
|
3137
|
+
\t\t\t\t\t});
|
|
3138
|
+
\t\t\t\t\treturn { events: result.items };
|
|
3139
|
+
\t\t\t\t},
|
|
3140
|
+
\t\t\t},
|
|
3141
|
+
\t\t},
|
|
2895
3142
|
\t});
|
|
2896
3143
|
}
|
|
2897
3144
|
|