emdash 0.9.0 → 0.10.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.
Files changed (195) hide show
  1. package/dist/{adapters-DoNJiveC.d.mts → adapters-BktHA7EO.d.mts} +1 -1
  2. package/dist/{adapters-DoNJiveC.d.mts.map → adapters-BktHA7EO.d.mts.map} +1 -1
  3. package/dist/{apply-BzltprvY.mjs → apply-UsrFuO7l.mjs} +156 -254
  4. package/dist/apply-UsrFuO7l.mjs.map +1 -0
  5. package/dist/astro/index.d.mts +6 -6
  6. package/dist/astro/index.mjs +10 -2
  7. package/dist/astro/index.mjs.map +1 -1
  8. package/dist/astro/middleware/auth.d.mts +5 -5
  9. package/dist/astro/middleware/auth.mjs +5 -5
  10. package/dist/astro/middleware/redirect.mjs +5 -5
  11. package/dist/astro/middleware/request-context.mjs +4 -4
  12. package/dist/astro/middleware/setup.mjs +1 -1
  13. package/dist/astro/middleware.mjs +35 -34
  14. package/dist/astro/middleware.mjs.map +1 -1
  15. package/dist/astro/types.d.mts +8 -9
  16. package/dist/astro/types.d.mts.map +1 -1
  17. package/dist/{base64-BRICGH2l.mjs → base64-MBPo9ozB.mjs} +1 -1
  18. package/dist/{base64-BRICGH2l.mjs.map → base64-MBPo9ozB.mjs.map} +1 -1
  19. package/dist/{byline-BSaNL1w7.mjs → byline-C3vnhIpU.mjs} +4 -4
  20. package/dist/{byline-BSaNL1w7.mjs.map → byline-C3vnhIpU.mjs.map} +1 -1
  21. package/dist/{bylines-CvJ3PYz2.mjs → bylines-esI7ioa9.mjs} +5 -5
  22. package/dist/{bylines-CvJ3PYz2.mjs.map → bylines-esI7ioa9.mjs.map} +1 -1
  23. package/dist/{cache-C6N_hhN7.mjs → cache-fTzxgMFJ.mjs} +3 -3
  24. package/dist/{cache-C6N_hhN7.mjs.map → cache-fTzxgMFJ.mjs.map} +1 -1
  25. package/dist/{chunks-NBQVDOci.mjs → chunks-Da2-b-oA.mjs} +2 -2
  26. package/dist/{chunks-NBQVDOci.mjs.map → chunks-Da2-b-oA.mjs.map} +1 -1
  27. package/dist/cli/index.mjs +251 -79
  28. package/dist/cli/index.mjs.map +1 -1
  29. package/dist/client/cf-access.d.mts +1 -1
  30. package/dist/client/index.d.mts +1 -1
  31. package/dist/client/index.mjs +1 -1
  32. package/dist/{config-BI0V3ICQ.mjs → config-CVssduLe.mjs} +1 -1
  33. package/dist/{config-BI0V3ICQ.mjs.map → config-CVssduLe.mjs.map} +1 -1
  34. package/dist/{content-8lOYF0pr.mjs → content-C7G4QXkK.mjs} +14 -3
  35. package/dist/content-C7G4QXkK.mjs.map +1 -0
  36. package/dist/db/index.d.mts +3 -3
  37. package/dist/db/index.mjs +1 -1
  38. package/dist/db/libsql.d.mts +1 -1
  39. package/dist/db/postgres.d.mts +1 -1
  40. package/dist/db/sqlite.d.mts +1 -1
  41. package/dist/{db-errors-WRezodiz.mjs → db-errors-B7P2pSCn.mjs} +1 -1
  42. package/dist/{db-errors-WRezodiz.mjs.map → db-errors-B7P2pSCn.mjs.map} +1 -1
  43. package/dist/{default-D8ksjWhO.mjs → default-pHuz9WF6.mjs} +1 -1
  44. package/dist/{default-D8ksjWhO.mjs.map → default-pHuz9WF6.mjs.map} +1 -1
  45. package/dist/{error-D_-tqP-I.mjs → error-DqnRMM5z.mjs} +1 -1
  46. package/dist/{error-D_-tqP-I.mjs.map → error-DqnRMM5z.mjs.map} +1 -1
  47. package/dist/{index-BFRaVcD6.d.mts → index-DjPMOfO0.d.mts} +82 -67
  48. package/dist/index-DjPMOfO0.d.mts.map +1 -0
  49. package/dist/index.d.mts +10 -10
  50. package/dist/index.mjs +28 -27
  51. package/dist/{load-DDqMMvZL.mjs → load-sXRuM7Us.mjs} +2 -2
  52. package/dist/{load-DDqMMvZL.mjs.map → load-sXRuM7Us.mjs.map} +1 -1
  53. package/dist/{loader-CKLbBnhK.mjs → loader-Bx2_9-5e.mjs} +31 -6
  54. package/dist/loader-Bx2_9-5e.mjs.map +1 -0
  55. package/dist/{manifest-schema-DqWNC3lM.mjs → manifest-schema-CXAbd1vH.mjs} +1 -1
  56. package/dist/{manifest-schema-DqWNC3lM.mjs.map → manifest-schema-CXAbd1vH.mjs.map} +1 -1
  57. package/dist/media/index.d.mts +1 -1
  58. package/dist/media/index.mjs +1 -1
  59. package/dist/media/local-runtime.d.mts +7 -7
  60. package/dist/media/local-runtime.mjs +3 -3
  61. package/dist/{media-BW32b4gi.mjs → media-D8FbNsl0.mjs} +2 -2
  62. package/dist/{media-BW32b4gi.mjs.map → media-D8FbNsl0.mjs.map} +1 -1
  63. package/dist/{mode-ier8jbBk.mjs → mode-YhqNVef_.mjs} +1 -1
  64. package/dist/{mode-ier8jbBk.mjs.map → mode-YhqNVef_.mjs.map} +1 -1
  65. package/dist/{options-BVp3UsTS.mjs → options-nPxWnrya.mjs} +1 -1
  66. package/dist/{options-BVp3UsTS.mjs.map → options-nPxWnrya.mjs.map} +1 -1
  67. package/dist/page/index.d.mts +2 -2
  68. package/dist/{patterns-CrCYkMBb.mjs → patterns-DsUZ4uxI.mjs} +1 -1
  69. package/dist/{patterns-CrCYkMBb.mjs.map → patterns-DsUZ4uxI.mjs.map} +1 -1
  70. package/dist/{placeholder-BE4o_2dc.d.mts → placeholder-CDPtkelt.d.mts} +1 -1
  71. package/dist/{placeholder-BE4o_2dc.d.mts.map → placeholder-CDPtkelt.d.mts.map} +1 -1
  72. package/dist/{placeholder-CIJejMlK.mjs → placeholder-Ci0RLeCk.mjs} +1 -1
  73. package/dist/{placeholder-CIJejMlK.mjs.map → placeholder-Ci0RLeCk.mjs.map} +1 -1
  74. package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
  75. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  76. package/dist/{public-url-DByxYjUw.mjs → public-url-B1AxbbbQ.mjs} +1 -1
  77. package/dist/{public-url-DByxYjUw.mjs.map → public-url-B1AxbbbQ.mjs.map} +1 -1
  78. package/dist/{query-Cg9ZKRQ0.mjs → query-Bo-msrmu.mjs} +13 -13
  79. package/dist/{query-Cg9ZKRQ0.mjs.map → query-Bo-msrmu.mjs.map} +1 -1
  80. package/dist/{redirect-BhUBKRc1.mjs → redirect-C5H7VGIX.mjs} +3 -3
  81. package/dist/{redirect-BhUBKRc1.mjs.map → redirect-C5H7VGIX.mjs.map} +1 -1
  82. package/dist/{registry-Dw70ChxB.mjs → registry-Beb7wxFc.mjs} +5 -5
  83. package/dist/{registry-Dw70ChxB.mjs.map → registry-Beb7wxFc.mjs.map} +1 -1
  84. package/dist/{request-cache-B-bmkipQ.mjs → request-cache-C-tIpYIw.mjs} +1 -1
  85. package/dist/{request-cache-B-bmkipQ.mjs.map → request-cache-C-tIpYIw.mjs.map} +1 -1
  86. package/dist/{runner-Bnoj7vjK.d.mts → runner-Clwe4Mme.d.mts} +2 -2
  87. package/dist/{runner-Bnoj7vjK.d.mts.map → runner-Clwe4Mme.d.mts.map} +1 -1
  88. package/dist/{runner-C7ADox5q.mjs → runner-DMnlIkh4.mjs} +433 -138
  89. package/dist/runner-DMnlIkh4.mjs.map +1 -0
  90. package/dist/runtime.d.mts +6 -6
  91. package/dist/runtime.mjs +3 -3
  92. package/dist/{search-dOGEccMa.mjs → search-DkN-BqsS.mjs} +164 -92
  93. package/dist/search-DkN-BqsS.mjs.map +1 -0
  94. package/dist/{secrets-CW3reAnU.mjs → secrets-CZ8rxLX3.mjs} +3 -3
  95. package/dist/{secrets-CW3reAnU.mjs.map → secrets-CZ8rxLX3.mjs.map} +1 -1
  96. package/dist/seed/index.d.mts +2 -2
  97. package/dist/seed/index.mjs +15 -14
  98. package/dist/seo/index.d.mts +1 -1
  99. package/dist/storage/local.d.mts +1 -1
  100. package/dist/storage/local.mjs +1 -1
  101. package/dist/storage/s3.d.mts +1 -1
  102. package/dist/storage/s3.mjs +1 -1
  103. package/dist/taxonomies-CTtewrSQ.mjs +407 -0
  104. package/dist/taxonomies-CTtewrSQ.mjs.map +1 -0
  105. package/dist/taxonomy-DSxx2K2L.mjs +218 -0
  106. package/dist/taxonomy-DSxx2K2L.mjs.map +1 -0
  107. package/dist/{tokens-D7zMmWi2.mjs → tokens-CyRDPVW2.mjs} +2 -2
  108. package/dist/{tokens-D7zMmWi2.mjs.map → tokens-CyRDPVW2.mjs.map} +1 -1
  109. package/dist/{transaction-Cn2rjY78.mjs → transaction-D44LBXvU.mjs} +1 -1
  110. package/dist/{transaction-Cn2rjY78.mjs.map → transaction-D44LBXvU.mjs.map} +1 -1
  111. package/dist/{transport-DNEfeMaU.d.mts → transport-DX_5rpsq.d.mts} +1 -1
  112. package/dist/{transport-DNEfeMaU.d.mts.map → transport-DX_5rpsq.d.mts.map} +1 -1
  113. package/dist/{transport-BeMCmin1.mjs → transport-xpzIjCIB.mjs} +1 -1
  114. package/dist/{transport-BeMCmin1.mjs.map → transport-xpzIjCIB.mjs.map} +1 -1
  115. package/dist/{types-CRxNbK-Z.mjs → types-BIgulNsW.mjs} +2 -2
  116. package/dist/{types-CRxNbK-Z.mjs.map → types-BIgulNsW.mjs.map} +1 -1
  117. package/dist/{types-CJsYGpco.d.mts → types-B_CXXnzh.d.mts} +1 -1
  118. package/dist/{types-CJsYGpco.d.mts.map → types-B_CXXnzh.d.mts.map} +1 -1
  119. package/dist/{types-M78DQ1lx.d.mts → types-C-aFbqmA.d.mts} +1 -1
  120. package/dist/{types-M78DQ1lx.d.mts.map → types-C-aFbqmA.d.mts.map} +1 -1
  121. package/dist/{types-4fVtCIm0.mjs → types-CoO6mpV3.mjs} +1 -1
  122. package/dist/{types-4fVtCIm0.mjs.map → types-CoO6mpV3.mjs.map} +1 -1
  123. package/dist/{types-BuBIptGk.d.mts → types-D19uBYWn.d.mts} +149 -4
  124. package/dist/types-D19uBYWn.d.mts.map +1 -0
  125. package/dist/{types-BSyXeCFW.d.mts → types-Dl1fgFjn.d.mts} +1 -1
  126. package/dist/{types-BSyXeCFW.d.mts.map → types-Dl1fgFjn.d.mts.map} +1 -1
  127. package/dist/{types-CrtWgIvl.d.mts → types-Dtx1mSMX.d.mts} +9 -1
  128. package/dist/types-Dtx1mSMX.d.mts.map +1 -0
  129. package/dist/{types-CIOg5AR8.mjs → types-Eg829jj9.mjs} +1 -1
  130. package/dist/{types-CIOg5AR8.mjs.map → types-Eg829jj9.mjs.map} +1 -1
  131. package/dist/{types-CDbKp7ND.mjs → types-K-EkEQCI.mjs} +1 -1
  132. package/dist/{types-CDbKp7ND.mjs.map → types-K-EkEQCI.mjs.map} +1 -1
  133. package/dist/{validate-Baqf0slj.mjs → validate-CBIbxM3L.mjs} +14 -10
  134. package/dist/validate-CBIbxM3L.mjs.map +1 -0
  135. package/dist/{validate-BfQh_C_y.d.mts → validate-DHGwADqO.d.mts} +18 -5
  136. package/dist/validate-DHGwADqO.d.mts.map +1 -0
  137. package/dist/{validation-BfEI7tNe.mjs → validation-B1NYiEos.mjs} +5 -5
  138. package/dist/{validation-BfEI7tNe.mjs.map → validation-B1NYiEos.mjs.map} +1 -1
  139. package/dist/version-CMD42IRC.mjs +7 -0
  140. package/dist/{version-DoxrVdYf.mjs.map → version-CMD42IRC.mjs.map} +1 -1
  141. package/dist/{zod-generator-CC0xNe_K.mjs → zod-generator-BNJDQBSZ.mjs} +8 -3
  142. package/dist/zod-generator-BNJDQBSZ.mjs.map +1 -0
  143. package/package.json +6 -6
  144. package/src/api/handlers/content.ts +11 -0
  145. package/src/api/handlers/dashboard.ts +29 -36
  146. package/src/api/handlers/menus.ts +256 -75
  147. package/src/api/handlers/taxonomies.ts +273 -97
  148. package/src/api/schemas/common.ts +7 -0
  149. package/src/api/schemas/menus.ts +23 -0
  150. package/src/api/schemas/taxonomies.ts +39 -0
  151. package/src/astro/integration/routes.ts +10 -0
  152. package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +1 -1
  153. package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +196 -0
  154. package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +9 -177
  155. package/src/astro/routes/api/menus/[name]/items.ts +16 -6
  156. package/src/astro/routes/api/menus/[name]/reorder.ts +8 -3
  157. package/src/astro/routes/api/menus/[name]/translations.ts +82 -0
  158. package/src/astro/routes/api/menus/[name].ts +19 -10
  159. package/src/astro/routes/api/menus/index.ts +9 -6
  160. package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +89 -0
  161. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +22 -22
  162. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +11 -14
  163. package/src/astro/routes/api/taxonomies/index.ts +9 -6
  164. package/src/cli/commands/export-seed.ts +82 -21
  165. package/src/cli/commands/plugin-init.ts +216 -90
  166. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +477 -0
  167. package/src/database/migrations/runner.ts +2 -0
  168. package/src/database/repositories/content.ts +11 -0
  169. package/src/database/repositories/taxonomy.ts +193 -89
  170. package/src/database/types.ts +10 -2
  171. package/src/i18n/resolve.ts +37 -0
  172. package/src/loader.ts +49 -2
  173. package/src/mcp/server.ts +77 -18
  174. package/src/menus/index.ts +143 -124
  175. package/src/menus/types.ts +15 -1
  176. package/src/schema/zod-generator.ts +12 -2
  177. package/src/seed/apply.ts +140 -54
  178. package/src/seed/types.ts +14 -1
  179. package/src/seed/validate.ts +27 -13
  180. package/src/taxonomies/index.ts +230 -213
  181. package/src/taxonomies/types.ts +10 -0
  182. package/dist/apply-BzltprvY.mjs.map +0 -1
  183. package/dist/content-8lOYF0pr.mjs.map +0 -1
  184. package/dist/index-BFRaVcD6.d.mts.map +0 -1
  185. package/dist/loader-CKLbBnhK.mjs.map +0 -1
  186. package/dist/runner-C7ADox5q.mjs.map +0 -1
  187. package/dist/search-dOGEccMa.mjs.map +0 -1
  188. package/dist/taxonomies-ZlRtD6AG.mjs +0 -315
  189. package/dist/taxonomies-ZlRtD6AG.mjs.map +0 -1
  190. package/dist/types-BuBIptGk.d.mts.map +0 -1
  191. package/dist/types-CrtWgIvl.d.mts.map +0 -1
  192. package/dist/validate-Baqf0slj.mjs.map +0 -1
  193. package/dist/validate-BfQh_C_y.d.mts.map +0 -1
  194. package/dist/version-DoxrVdYf.mjs +0 -7
  195. package/dist/zod-generator-CC0xNe_K.mjs.map +0 -1
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * emdash plugin init
3
3
  *
4
- * Scaffold a new EmDash plugin. Generates the standard-format boilerplate:
4
+ * Scaffold a new EmDash plugin. Generates the sandboxed-format boilerplate:
5
5
  * src/index.ts -- descriptor factory
6
6
  * src/sandbox-entry.ts -- definePlugin({ hooks, routes })
7
7
  * package.json
8
8
  * tsconfig.json
9
9
  *
10
- * Use --native to generate native-format boilerplate instead (createPlugin + React admin).
10
+ * Use --format=native (or --native) to generate native-format boilerplate
11
+ * instead (createPlugin + React admin). When neither is passed and stdout
12
+ * is a TTY, the user is prompted to choose.
11
13
  *
12
14
  */
13
15
 
@@ -22,6 +24,8 @@ import { fileExists } from "./bundle-utils.js";
22
24
  const SLUG_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
23
25
  const SCOPE_RE = /^@[^/]+\//;
24
26
 
27
+ type PluginFormat = "standard" | "native";
28
+
25
29
  export const pluginInitCommand = defineCommand({
26
30
  meta: {
27
31
  name: "init",
@@ -37,15 +41,27 @@ export const pluginInitCommand = defineCommand({
37
41
  type: "string",
38
42
  description: "Plugin name/id (e.g. my-plugin or @org/my-plugin)",
39
43
  },
44
+ format: {
45
+ type: "string",
46
+ description:
47
+ "Plugin format: sandboxed or native. Prompts when running interactively if not set.",
48
+ valueHint: "sandboxed|native",
49
+ },
40
50
  native: {
41
51
  type: "boolean",
42
- description: "Generate native-format plugin (createPlugin + React admin)",
52
+ description: "Shortcut for --format=native",
43
53
  default: false,
44
54
  },
45
55
  },
46
56
  async run({ args }) {
47
57
  const targetDir = resolve(args.dir);
48
- const isNative = args.native;
58
+
59
+ const format = await resolveFormat(args.format, args.native);
60
+ if (!format) {
61
+ consola.info("Cancelled");
62
+ return;
63
+ }
64
+ const isNative = format === "native";
49
65
 
50
66
  // Derive plugin name from --name or directory name
51
67
  let pluginName = args.name || basename(targetDir);
@@ -71,7 +87,7 @@ export const pluginInitCommand = defineCommand({
71
87
  process.exit(1);
72
88
  }
73
89
 
74
- consola.start(`Scaffolding ${isNative ? "native" : "standard"} plugin: ${pluginName}`);
90
+ consola.start(`Scaffolding ${isNative ? "native" : "sandboxed"} plugin: ${pluginName}`);
75
91
 
76
92
  await mkdir(srcDir, { recursive: true });
77
93
 
@@ -83,22 +99,99 @@ export const pluginInitCommand = defineCommand({
83
99
 
84
100
  consola.success(`Plugin scaffolded in ${targetDir}`);
85
101
  consola.info("Next steps:");
86
- if (args.dir !== ".") {
87
- consola.info(` 1. cd ${args.dir}`);
88
- }
89
- consola.info(` ${args.dir !== "." ? "2" : "1"}. pnpm install`);
90
- if (isNative) {
91
- consola.info(` ${args.dir !== "." ? "3" : "2"}. Edit src/index.ts to add hooks and routes`);
102
+ const steps: string[] = [];
103
+ if (args.dir !== ".") steps.push(`cd ${args.dir}`);
104
+ steps.push("pnpm install");
105
+ steps.push(
106
+ isNative
107
+ ? "Edit src/index.ts to add hooks and routes"
108
+ : "Edit src/sandbox-entry.ts to add hooks and routes",
109
+ );
110
+ steps.push("pnpm build");
111
+ if (!isNative) steps.push("emdash plugin validate --dir .");
112
+ steps.forEach((step, i) => consola.info(` ${i + 1}. ${step}`));
113
+ },
114
+ });
115
+
116
+ async function resolveFormat(
117
+ formatArg: string | undefined,
118
+ nativeFlag: boolean,
119
+ ): Promise<PluginFormat | null> {
120
+ if (formatArg) {
121
+ const normalized = formatArg.toLowerCase();
122
+ let parsed: PluginFormat;
123
+ if (normalized === "native") {
124
+ parsed = "native";
125
+ } else if (normalized === "sandboxed" || normalized === "standard") {
126
+ parsed = "standard";
92
127
  } else {
93
- consola.info(
94
- ` ${args.dir !== "." ? "3" : "2"}. Edit src/sandbox-entry.ts to add hooks and routes`,
95
- );
128
+ consola.error(`Invalid --format "${formatArg}". Use "sandboxed" or "native".`);
129
+ process.exit(1);
130
+ }
131
+ if (nativeFlag && parsed !== "native") {
132
+ consola.error(`Conflicting flags: --native and --format=${formatArg}. Pass only one.`);
133
+ process.exit(1);
96
134
  }
97
- consola.info(` ${args.dir !== "." ? "4" : "3"}. emdash plugin validate --dir .`);
135
+ return parsed;
136
+ }
137
+ if (nativeFlag) return "native";
138
+
139
+ if (!process.stdout.isTTY) return "standard";
140
+
141
+ const choice = await consola.prompt("Which plugin format?", {
142
+ type: "select",
143
+ initial: "standard",
144
+ options: [
145
+ {
146
+ label: "Sandboxed",
147
+ value: "standard",
148
+ hint: "runs in an isolated sandbox; safe to install from the marketplace",
149
+ },
150
+ {
151
+ label: "Native",
152
+ value: "native",
153
+ hint: "full runtime access; install from npm",
154
+ },
155
+ ],
156
+ cancel: "null",
157
+ });
158
+ if (choice === null) return null;
159
+ return choice as PluginFormat;
160
+ }
161
+
162
+ function camelCase(slug: string): string {
163
+ return slug
164
+ .split("-")
165
+ .map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1)))
166
+ .join("");
167
+ }
168
+
169
+ function pascalCase(slug: string): string {
170
+ return slug
171
+ .split("-")
172
+ .map((s) => s[0].toUpperCase() + s.slice(1))
173
+ .join("");
174
+ }
175
+
176
+ const TSCONFIG = {
177
+ compilerOptions: {
178
+ target: "ES2022",
179
+ module: "preserve",
180
+ moduleResolution: "bundler",
181
+ strict: true,
182
+ esModuleInterop: true,
183
+ declaration: true,
184
+ outDir: "./dist",
185
+ rootDir: "./src",
98
186
  },
99
- });
187
+ include: ["src/**/*"],
188
+ exclude: ["node_modules", "dist"],
189
+ } as const;
190
+
191
+ const TSDOWN_VERSION = "^0.20.0";
192
+ const TYPESCRIPT_VERSION = "^5.9.0";
100
193
 
101
- // ── Standard format scaffolding ──────────────────────────────────
194
+ // ── Sandboxed format scaffolding ─────────────────────────────────
102
195
 
103
196
  async function scaffoldStandard(
104
197
  targetDir: string,
@@ -106,13 +199,8 @@ async function scaffoldStandard(
106
199
  pluginName: string,
107
200
  slug: string,
108
201
  ): Promise<void> {
109
- // Derive the camelCase function name from slug
110
- const fnName = slug
111
- .split("-")
112
- .map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1)))
113
- .join("");
202
+ const fnName = camelCase(slug);
114
203
 
115
- // package.json
116
204
  await writeFile(
117
205
  join(targetDir, "package.json"),
118
206
  JSON.stringify(
@@ -120,74 +208,98 @@ async function scaffoldStandard(
120
208
  name: pluginName,
121
209
  version: "0.1.0",
122
210
  type: "module",
211
+ main: "./dist/index.mjs",
123
212
  exports: {
124
- ".": "./src/index.ts",
125
- "./sandbox": "./src/sandbox-entry.ts",
213
+ ".": {
214
+ types: "./dist/index.d.mts",
215
+ import: "./dist/index.mjs",
216
+ },
217
+ "./sandbox": {
218
+ types: "./dist/sandbox-entry.d.mts",
219
+ import: "./dist/sandbox-entry.mjs",
220
+ },
126
221
  },
127
- files: ["src"],
222
+ files: ["dist"],
223
+ scripts: {
224
+ build: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --clean",
225
+ dev: "tsdown src/index.ts src/sandbox-entry.ts --format esm --dts --watch",
226
+ typecheck: "tsc --noEmit",
227
+ },
228
+ keywords: ["emdash", "emdash-plugin"],
229
+ license: "MIT",
128
230
  peerDependencies: {
129
231
  emdash: "*",
130
232
  },
131
- },
132
- null,
133
- "\t",
134
- ) + "\n",
135
- );
136
-
137
- // tsconfig.json
138
- await writeFile(
139
- join(targetDir, "tsconfig.json"),
140
- JSON.stringify(
141
- {
142
- compilerOptions: {
143
- target: "ES2022",
144
- module: "preserve",
145
- moduleResolution: "bundler",
146
- strict: true,
147
- esModuleInterop: true,
148
- declaration: true,
149
- outDir: "./dist",
150
- rootDir: "./src",
233
+ devDependencies: {
234
+ emdash: "*",
235
+ tsdown: TSDOWN_VERSION,
236
+ typescript: TYPESCRIPT_VERSION,
151
237
  },
152
- include: ["src/**/*"],
153
- exclude: ["node_modules", "dist"],
154
238
  },
155
239
  null,
156
240
  "\t",
157
241
  ) + "\n",
158
242
  );
159
243
 
160
- // src/index.ts -- descriptor factory
244
+ await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, "\t") + "\n");
245
+
161
246
  await writeFile(
162
247
  join(srcDir, "index.ts"),
163
248
  `import type { PluginDescriptor } from "emdash";
164
249
 
165
250
  export function ${fnName}Plugin(): PluginDescriptor {
166
251
  \treturn {
167
- \t\tid: "${pluginName}",
252
+ \t\tid: "${slug}",
168
253
  \t\tversion: "0.1.0",
169
254
  \t\tformat: "standard",
170
255
  \t\tentrypoint: "${pluginName}/sandbox",
171
- \t\tcapabilities: [],
256
+
257
+ \t\tcapabilities: ["content:read"],
258
+ \t\tstorage: {
259
+ \t\t\tevents: { indexes: ["timestamp"] },
260
+ \t\t},
172
261
  \t};
173
262
  }
174
263
  `,
175
264
  );
176
265
 
177
- // src/sandbox-entry.ts -- plugin definition
178
266
  await writeFile(
179
267
  join(srcDir, "sandbox-entry.ts"),
180
268
  `import { definePlugin } from "emdash";
181
269
  import type { PluginContext } from "emdash";
182
270
 
271
+ interface ContentSaveEvent {
272
+ \tcollection: string;
273
+ \tcontent: { id: string };
274
+ \tisNew: boolean;
275
+ }
276
+
183
277
  export default definePlugin({
184
278
  \thooks: {
185
279
  \t\t"content:afterSave": {
186
- \t\t\thandler: async (event: any, ctx: PluginContext) => {
280
+ \t\t\thandler: async (event: ContentSaveEvent, ctx: PluginContext) => {
187
281
  \t\t\t\tctx.log.info("Content saved", {
188
282
  \t\t\t\t\tcollection: event.collection,
189
283
  \t\t\t\t\tid: event.content.id,
190
284
  \t\t\t\t});
285
+
286
+ \t\t\t\tawait ctx.storage.events.put(\`save-\${Date.now()}\`, {
287
+ \t\t\t\t\ttimestamp: new Date().toISOString(),
288
+ \t\t\t\t\tcollection: event.collection,
289
+ \t\t\t\t\tcontentId: event.content.id,
290
+ \t\t\t\t});
291
+ \t\t\t},
292
+ \t\t},
293
+ \t},
294
+
295
+ \troutes: {
296
+ \t\trecent: {
297
+ \t\t\thandler: async (_routeCtx, ctx: PluginContext) => {
298
+ \t\t\t\tconst result = await ctx.storage.events.query({
299
+ \t\t\t\t\torderBy: { timestamp: "desc" },
300
+ \t\t\t\t\tlimit: 10,
301
+ \t\t\t\t});
302
+ \t\t\t\treturn { events: result.items };
191
303
  \t\t\t},
192
304
  \t\t},
193
305
  \t},
@@ -204,12 +316,9 @@ async function scaffoldNative(
204
316
  pluginName: string,
205
317
  slug: string,
206
318
  ): Promise<void> {
207
- const fnName = slug
208
- .split("-")
209
- .map((s, i) => (i === 0 ? s : s[0].toUpperCase() + s.slice(1)))
210
- .join("");
319
+ const fnName = camelCase(slug);
320
+ const typeName = pascalCase(slug);
211
321
 
212
- // package.json
213
322
  await writeFile(
214
323
  join(targetDir, "package.json"),
215
324
  JSON.stringify(
@@ -217,71 +326,88 @@ async function scaffoldNative(
217
326
  name: pluginName,
218
327
  version: "0.1.0",
219
328
  type: "module",
329
+ main: "./dist/index.mjs",
220
330
  exports: {
221
- ".": "./src/index.ts",
331
+ ".": {
332
+ types: "./dist/index.d.mts",
333
+ import: "./dist/index.mjs",
334
+ },
335
+ },
336
+ files: ["dist"],
337
+ scripts: {
338
+ build: "tsdown src/index.ts --format esm --dts --clean",
339
+ dev: "tsdown src/index.ts --format esm --dts --watch",
340
+ typecheck: "tsc --noEmit",
222
341
  },
223
- files: ["src"],
342
+ keywords: ["emdash", "emdash-plugin"],
343
+ license: "MIT",
224
344
  peerDependencies: {
225
345
  emdash: "*",
226
346
  },
227
- },
228
- null,
229
- "\t",
230
- ) + "\n",
231
- );
232
-
233
- // tsconfig.json
234
- await writeFile(
235
- join(targetDir, "tsconfig.json"),
236
- JSON.stringify(
237
- {
238
- compilerOptions: {
239
- target: "ES2022",
240
- module: "preserve",
241
- moduleResolution: "bundler",
242
- strict: true,
243
- esModuleInterop: true,
244
- declaration: true,
245
- outDir: "./dist",
246
- rootDir: "./src",
347
+ devDependencies: {
348
+ emdash: "*",
349
+ tsdown: TSDOWN_VERSION,
350
+ typescript: TYPESCRIPT_VERSION,
247
351
  },
248
- include: ["src/**/*"],
249
- exclude: ["node_modules", "dist"],
250
352
  },
251
353
  null,
252
354
  "\t",
253
355
  ) + "\n",
254
356
  );
255
357
 
256
- // src/index.ts -- descriptor + createPlugin
358
+ await writeFile(join(targetDir, "tsconfig.json"), JSON.stringify(TSCONFIG, null, "\t") + "\n");
359
+
257
360
  await writeFile(
258
361
  join(srcDir, "index.ts"),
259
362
  `import { definePlugin } from "emdash";
260
363
  import type { PluginDescriptor } from "emdash";
261
364
 
262
- export function ${fnName}Plugin(): PluginDescriptor {
365
+ export interface ${typeName}Options {
366
+ \tenabled?: boolean;
367
+ }
368
+
369
+ export function ${fnName}Plugin(options: ${typeName}Options = {}): PluginDescriptor<${typeName}Options> {
263
370
  \treturn {
264
- \t\tid: "${pluginName}",
371
+ \t\tid: "${slug}",
265
372
  \t\tversion: "0.1.0",
266
373
  \t\tformat: "native",
267
374
  \t\tentrypoint: "${pluginName}",
268
- \t\toptions: {},
375
+ \t\toptions,
269
376
  \t};
270
377
  }
271
378
 
272
- export function createPlugin() {
379
+ export function createPlugin(options: ${typeName}Options = {}) {
273
380
  \treturn definePlugin({
274
- \t\tid: "${pluginName}",
381
+ \t\tid: "${slug}",
275
382
  \t\tversion: "0.1.0",
276
383
 
384
+ \t\tcapabilities: ["content:read"],
385
+ \t\tstorage: {
386
+ \t\t\tevents: { indexes: ["createdAt"] },
387
+ \t\t},
388
+
277
389
  \t\thooks: {
278
390
  \t\t\t"content:afterSave": async (event, ctx) => {
279
- \t\t\t\tctx.log.info("Content saved", {
391
+ \t\t\t\tif (options.enabled === false) return;
392
+ \t\t\t\tawait ctx.storage.events.put(\`evt_\${Date.now()}\`, {
280
393
  \t\t\t\t\tcollection: event.collection,
281
- \t\t\t\t\tid: event.content.id,
394
+ \t\t\t\t\tcontentId: event.content.id,
395
+ \t\t\t\t\tcreatedAt: new Date().toISOString(),
282
396
  \t\t\t\t});
283
397
  \t\t\t},
284
398
  \t\t},
399
+
400
+ \t\troutes: {
401
+ \t\t\trecent: {
402
+ \t\t\t\thandler: async (ctx) => {
403
+ \t\t\t\t\tconst result = await ctx.storage.events.query({
404
+ \t\t\t\t\t\torderBy: { createdAt: "desc" },
405
+ \t\t\t\t\t\tlimit: 10,
406
+ \t\t\t\t\t});
407
+ \t\t\t\t\treturn { events: result.items };
408
+ \t\t\t\t},
409
+ \t\t\t},
410
+ \t\t},
285
411
  \t});
286
412
  }
287
413