@phtngyn/cms 0.2.0-beta.1 → 0.2.0-beta.3

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 (86) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +40 -45
  3. package/package.json +7 -5
  4. package/src/ApplicationTypes.d.ts +7 -5
  5. package/src/{Configuration-CjVUBBwa.js → Configuration-BU-UyGoH.js} +4 -4
  6. package/src/{Configuration-CjVUBBwa.js.map → Configuration-BU-UyGoH.js.map} +1 -1
  7. package/src/Configuration.d.ts +41 -24
  8. package/src/Configuration.js.map +1 -1
  9. package/src/Content.js +42 -19
  10. package/src/Content.js.map +1 -1
  11. package/src/{ContentTransfer-CTzGGKFV.js → ContentTransfer-BhF9G56s.js} +2 -2
  12. package/src/{ContentTransfer-CTzGGKFV.js.map → ContentTransfer-BhF9G56s.js.map} +1 -1
  13. package/src/{DocumentEngine-BbhfnTAc.js → DocumentEngine-ClhFXW9M.js} +4 -4
  14. package/src/{DocumentEngine-BbhfnTAc.js.map → DocumentEngine-ClhFXW9M.js.map} +1 -1
  15. package/src/DocumentModel.d.ts +6 -1
  16. package/src/DocumentModel.js +4 -3
  17. package/src/DocumentModel.js.map +1 -1
  18. package/src/{Forms-CfcI3tPU.js → Forms-BbkE7QfC.js} +47 -16
  19. package/src/Forms-BbkE7QfC.js.map +1 -0
  20. package/src/{Forms-02-p8qEw.js → Forms-DoB56hfe.js} +24 -7
  21. package/src/Forms-DoB56hfe.js.map +1 -0
  22. package/src/{Forms-BorTcIlp.d.ts → Forms-R0HoTR-o.d.ts} +30 -9
  23. package/src/Forms.d.ts +16 -15
  24. package/src/Forms.js.map +1 -1
  25. package/src/{I18n-BXlVunfa.d.ts → I18n-zbqpWxki.d.ts} +3 -1
  26. package/src/I18n.d.ts +1 -1
  27. package/src/Module.js +4 -2
  28. package/src/Module.js.map +1 -1
  29. package/src/{S3MediaObjects-DC0c8sYC.js → S3MediaObjects-CR3p1WTg.js} +21 -13
  30. package/src/S3MediaObjects-CR3p1WTg.js.map +1 -0
  31. package/src/cli.js +60 -34
  32. package/src/cli.js.map +1 -1
  33. package/src/{connection-BPf4BQUZ.js → connection-D5S9Iph-.js} +2 -2
  34. package/src/{connection-BPf4BQUZ.js.map → connection-D5S9Iph-.js.map} +1 -1
  35. package/src/{dev-lifecycle-vQLxwCFq.js → dev-lifecycle-D3f9-upa.js} +129 -42
  36. package/src/dev-lifecycle-D3f9-upa.js.map +1 -0
  37. package/src/{installation-DsGst1BH.js → installation-fZtNxtoW.js} +68 -41
  38. package/src/installation-fZtNxtoW.js.map +1 -0
  39. package/src/internal/authoring-catalog.js +2 -2
  40. package/src/internal/authoring-metadata.js +1 -1
  41. package/src/internal/configuration.js +149 -50
  42. package/src/internal/configuration.js.map +1 -1
  43. package/src/internal/dev-lifecycle.js +2 -2
  44. package/src/internal/diagnostics.js +47 -0
  45. package/src/internal/diagnostics.js.map +1 -0
  46. package/src/internal/document-model.js +2 -2
  47. package/src/internal/form-capabilities.js +1 -1
  48. package/src/internal/i18n.js +6 -1
  49. package/src/internal/i18n.js.map +1 -1
  50. package/src/internal/studio-app.js +8 -6
  51. package/src/internal/studio-app.js.map +1 -1
  52. package/src/{location-BgYhXEZp.js → location-CPX7oj0o.js} +2 -2
  53. package/src/{location-BgYhXEZp.js.map → location-CPX7oj0o.js.map} +1 -1
  54. package/src/{models-k2nXyD6q.js → models-C9kF9JlQ.js} +2 -2
  55. package/src/{models-k2nXyD6q.js.map → models-C9kF9JlQ.js.map} +1 -1
  56. package/src/{models-DgkGTFus.js → models-ChRxBChy.js} +4 -2
  57. package/src/{models-DgkGTFus.js.map → models-ChRxBChy.js.map} +1 -1
  58. package/src/runtime/delivery/context.js +1 -1
  59. package/src/runtime/delivery/plugin.js +3 -2
  60. package/src/runtime/delivery/plugin.js.map +1 -1
  61. package/src/runtime/forms/document.js +1 -1
  62. package/src/runtime/forms/useCmsForm.js +73 -46
  63. package/src/runtime/forms/useCmsForm.js.map +1 -1
  64. package/src/runtime/i18n/useCmsI18n.d.ts +1 -1
  65. package/src/runtime/i18n/useCmsI18n.js.map +1 -1
  66. package/src/runtime/installation.js +1 -1
  67. package/src/runtime/plugin.js +12 -4
  68. package/src/runtime/plugin.js.map +1 -1
  69. package/src/runtime/preview/connection.js +1 -1
  70. package/src/runtime/preview/controller.js +2 -2
  71. package/src/runtime/preview/plugin.client.js +3 -3
  72. package/src/runtime/preview/plugin.client.js.map +1 -1
  73. package/src/runtime/preview/store.js +1 -1
  74. package/src/runtime/sitemap.js +1 -1
  75. package/src/{store-BYgSt7E3.js → store-CCsjHaZm.js} +8 -8
  76. package/src/store-CCsjHaZm.js.map +1 -0
  77. package/studio/{CreateDocumentDialog-BdiM77bb.js → CreateDocumentDialog-DJ8c0WZb.js} +3 -3
  78. package/studio/{documentPlanning-B7fyUj9r.js → documentPlanning-CDb31mnl.js} +6417 -6358
  79. package/studio/host.css +1 -1
  80. package/studio/host.js +7612 -7249
  81. package/src/Forms-02-p8qEw.js.map +0 -1
  82. package/src/Forms-CfcI3tPU.js.map +0 -1
  83. package/src/S3MediaObjects-DC0c8sYC.js.map +0 -1
  84. package/src/dev-lifecycle-vQLxwCFq.js.map +0 -1
  85. package/src/installation-DsGst1BH.js.map +0 -1
  86. package/src/store-BYgSt7E3.js.map +0 -1
package/src/cli.js CHANGED
@@ -1,18 +1,27 @@
1
1
  import { applyContent, diffContent, exportContent, planContent } from "./cli/content.js";
2
- import { n as doctorCmsDevelopment, r as resetCmsDevelopment, t as bootstrapCmsDevelopment } from "./dev-lifecycle-vQLxwCFq.js";
3
- import { Config, Console, Effect, Layer, Match, Option, Redacted } from "effect";
2
+ import { i as resetCmsDevelopment, n as doctorCmsDevelopment, r as loadCmsProjectEnvironment, t as bootstrapCmsDevelopment } from "./dev-lifecycle-D3f9-upa.js";
3
+ import { diagnosticFromCause, renderDiagnostic } from "./internal/diagnostics.js";
4
+ import { Config, Console, Effect, Layer, Match, Option, Redacted, Runtime, Schema } from "effect";
4
5
  import { NodeChildProcessSpawner, NodeCrypto, NodeFileSystem, NodePath, NodeRuntime, NodeStdio, NodeTerminal } from "@effect/platform-node-shared";
5
6
  import { FetchHttpClient } from "effect/unstable/http";
6
7
  import { Argument, CliError, Command, Flag, Prompt } from "effect/unstable/cli";
7
8
 
8
9
  //#region package.json
9
- var version = "0.2.0-beta.1";
10
+ var version = "0.2.0-beta.3";
10
11
 
11
12
  //#endregion
12
13
  //#region src/cli.ts
14
+ const rootDirectory = Flag.String("root").pipe(Flag.withDefault("."));
15
+ const envFile = Flag.String("env-file").pipe(Flag.withDefault(""));
16
+ const verbose = Flag.Boolean("verbose").pipe(Flag.withDefault(false));
17
+ const json = Flag.Boolean("json").pipe(Flag.withDefault(false));
13
18
  const root = Command.make("cms").pipe(Command.withSharedFlags({
14
19
  url: Flag.String("url").pipe(Flag.withDefault("")),
15
- email: Flag.String("email").pipe(Flag.withDefault(""))
20
+ email: Flag.String("email").pipe(Flag.withDefault("")),
21
+ rootDirectory,
22
+ envFile,
23
+ verbose,
24
+ json
16
25
  }));
17
26
  const directory = Argument.String("directory");
18
27
  const overwrite = Flag.Boolean("overwrite").pipe(Flag.withDefault(false));
@@ -65,61 +74,62 @@ const content = Command.make("content").pipe(Command.withSubcommands([
65
74
  planCommand,
66
75
  applyCommand
67
76
  ]));
68
- const json = Flag.Boolean("json").pipe(Flag.withDefault(false));
69
77
  const full = Flag.Boolean("full").pipe(Flag.withDefault(false));
70
- const rootDirectory = Flag.String("root").pipe(Flag.withDefault("."));
71
78
  const contentDirectory = Flag.String("content").pipe(Flag.withDefault(""));
72
79
  const administratorName = Flag.String("name").pipe(Flag.withDefault(""));
73
80
  const database = Flag.Boolean("database").pipe(Flag.withDefault(false));
74
81
  const media = Flag.Boolean("media").pipe(Flag.withDefault(false));
75
82
  const bootstrap = Flag.Boolean("bootstrap").pipe(Flag.withDefault(false));
76
83
  const yes = Flag.Boolean("yes").pipe(Flag.withDefault(false));
77
- const doctorCommand = Command.make("doctor", {
78
- full,
79
- json,
80
- rootDirectory
81
- }, ({ full, json, rootDirectory }) => Effect.gen(function* () {
82
- const report = yield* attempt(() => doctorCmsDevelopment(rootDirectory, full));
83
- if (json) yield* Console.log(JSON.stringify(report));
84
+ const doctorCommand = Command.make("doctor", { full }, ({ full }) => Effect.gen(function* () {
85
+ const options = yield* root;
86
+ const report = yield* attempt("doctor.configuration", options, () => doctorCmsDevelopment(options.rootDirectory, full, options.envFile || void 0));
87
+ if (options.json) yield* Console.log(JSON.stringify(report));
84
88
  else for (const check of report.checks) {
85
89
  const icon = Match.value(check.status).pipe(Match.when("pass", () => "✓"), Match.when("fail", () => "✗"), Match.when("not-verified", () => "−"), Match.exhaustive);
86
90
  yield* Console.log(`${icon} ${check.label}${check.detail ? `\n\n ${check.detail}` : ""}`);
87
91
  }
88
- if (!report.ok) return yield* cliError("One or more CMS development checks failed.");
92
+ if (!report.ok) return yield* options.json ? new JsonCommandFailed({}) : cliError("One or more CMS development checks failed.");
89
93
  })).pipe(Command.withDescription("Check local CMS configuration and infrastructure"));
90
94
  const bootstrapCommand = Command.make("bootstrap", {
91
95
  contentDirectory,
92
96
  administratorName,
93
- publish,
94
- rootDirectory
95
- }, ({ contentDirectory, administratorName, publish, rootDirectory }) => Effect.gen(function* () {
96
- const administrator = yield* administratorOptions(administratorName, contentDirectory, publish);
97
- const result = yield* attempt(() => bootstrapCmsDevelopment({
98
- rootDir: rootDirectory,
97
+ publish
98
+ }, ({ contentDirectory, administratorName, publish }) => Effect.gen(function* () {
99
+ const options = yield* root;
100
+ yield* attempt("bootstrap.environment", options, () => loadCmsProjectEnvironment(options.rootDirectory, options.envFile || void 0));
101
+ const administrator = yield* administratorOptions(administratorName, contentDirectory, publish, options, "bootstrap.credentials");
102
+ const result = yield* attempt("bootstrap.installation", options, () => bootstrapCmsDevelopment({
103
+ rootDir: options.rootDirectory,
104
+ envFile: options.envFile || void 0,
105
+ verbose: options.verbose,
99
106
  ...administrator
100
107
  }));
101
- yield* Console.log(`Bootstrap complete: administrator ${result.administrator}${result.contentApplied ? ", content applied" : ""}.`);
108
+ if (options.json) yield* Console.log(JSON.stringify(result));
109
+ else yield* Console.log(`Bootstrap complete: administrator ${result.administrator}${result.contentApplied ? ", content applied" : ""}.`);
102
110
  })).pipe(Command.withDescription("Provision and initialize a local CMS installation"));
103
111
  const resetCommand = Command.make("reset", {
104
112
  bootstrap,
105
113
  contentDirectory,
106
114
  database,
107
- json,
108
115
  media,
109
116
  publish,
110
- rootDirectory,
111
117
  yes
112
- }, ({ bootstrap, contentDirectory, database, json, media, publish, rootDirectory, yes }) => Effect.gen(function* () {
113
- if (!database && !media) return yield* cliError("Choose --database, --media, or both.");
114
- const administrator = bootstrap && yes ? yield* administratorOptions("", contentDirectory, publish) : void 0;
115
- const result = yield* attempt(() => resetCmsDevelopment({
116
- rootDir: rootDirectory,
118
+ }, ({ bootstrap, contentDirectory, database, media, publish, yes }) => Effect.gen(function* () {
119
+ const options = yield* root;
120
+ if (!database && !media) return yield* commandFailure("reset.arguments", options, /* @__PURE__ */ new Error("Choose --database, --media, or both."));
121
+ if (bootstrap && yes) yield* attempt("reset.environment", options, () => loadCmsProjectEnvironment(options.rootDirectory, options.envFile || void 0));
122
+ const administrator = bootstrap && yes ? yield* administratorOptions("", contentDirectory, publish, options, "reset.credentials") : void 0;
123
+ const result = yield* attempt("reset.installation", options, () => resetCmsDevelopment({
124
+ rootDir: options.rootDirectory,
125
+ envFile: options.envFile || void 0,
117
126
  database,
118
127
  media,
119
128
  yes,
129
+ verbose: options.verbose,
120
130
  bootstrap: administrator
121
131
  }));
122
- if (json) return yield* Console.log(JSON.stringify(result));
132
+ if (options.json) return yield* Console.log(JSON.stringify(result));
123
133
  for (const target of result.targets) yield* Console.log(`${result.executed ? "✓ Reset" : "Would reset"} ${target}`);
124
134
  if (!result.executed) yield* Console.log("Dry run only. Add --yes to execute these exact targets.");
125
135
  })).pipe(Command.withDescription("Reset CMS-owned local data with a dry-run default"));
@@ -128,11 +138,13 @@ const dev = Command.make("dev").pipe(Command.withSubcommands([
128
138
  bootstrapCommand,
129
139
  resetCommand
130
140
  ]));
131
- const administratorOptions = Effect.fn("CmsCli.dev.administratorOptions")(function* (name, content, publishContent) {
141
+ const administratorOptions = Effect.fn("CmsCli.dev.administratorOptions")(function* (name, content, publishContent, options, phase) {
132
142
  const email = yield* Config.String("CMS_EMAIL").pipe(Config.withDefault(""));
133
143
  const configuredPassword = yield* Config.Redacted("CMS_PASSWORD").pipe(Config.option);
134
- const password = Option.isSome(configuredPassword) ? Redacted.value(configuredPassword.value) : Redacted.value(yield* Prompt.Password({ message: `Password for ${email || "local administrator"}` }));
135
- if (!email || !password) return yield* cliError("CMS_EMAIL and CMS_PASSWORD are required.");
144
+ if (!email) return yield* commandFailure(phase, options, /* @__PURE__ */ new Error("CMS_EMAIL is required."));
145
+ if (Option.isNone(configuredPassword) && options.json) return yield* commandFailure(phase, options, /* @__PURE__ */ new Error("CMS_PASSWORD is required."));
146
+ const password = Option.isSome(configuredPassword) ? Redacted.value(configuredPassword.value) : Redacted.value(yield* Prompt.Password({ message: `Password for ${email}` }));
147
+ if (!password) return yield* commandFailure(phase, options, /* @__PURE__ */ new Error("CMS_PASSWORD is required."));
136
148
  return {
137
149
  content: content || void 0,
138
150
  email,
@@ -147,10 +159,24 @@ function cliError(message) {
147
159
  userMessage: message
148
160
  });
149
161
  }
150
- function attempt(operation) {
162
+ var CliOperationFailed = class extends Schema.TaggedError()("CliOperationFailed", { cause: Schema.Defect() }) {};
163
+ var JsonCommandFailed = class extends Schema.TaggedError()("JsonCommandFailed", {}) {
164
+ [Runtime.errorExitCode] = 1;
165
+ [Runtime.errorReported] = false;
166
+ };
167
+ function attempt(phase, options, operation) {
151
168
  return Effect.tryPromise({
152
169
  try: () => Promise.resolve(operation()),
153
- catch: (cause) => cliError(cause instanceof Error ? cause.message : String(cause))
170
+ catch: (cause) => new CliOperationFailed({ cause })
171
+ }).pipe(Effect.catch((failure) => commandFailure(phase, options, failure.cause)));
172
+ }
173
+ function commandFailure(phase, options, cause) {
174
+ return Effect.gen(function* () {
175
+ const diagnostic = diagnosticFromCause(phase, cause);
176
+ if (!options.json) return yield* cliError(renderDiagnostic(diagnostic, options.verbose));
177
+ const { details: _details, ...output } = diagnostic;
178
+ yield* Console.log(JSON.stringify(output));
179
+ return yield* new JsonCommandFailed({});
154
180
  });
155
181
  }
156
182
  root.pipe(Command.withSubcommands([content, dev]), Command.run({ version }), Effect.provide(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer, NodeCrypto.layer, NodeTerminal.layer, NodeStdio.layer, NodeChildProcessSpawner.layer.pipe(Layer.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer))), FetchHttpClient.layer)), NodeRuntime.runMain);
package/src/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":["packageManifest.version"],"sources":["../../../package.json","../../../src/cli.ts"],"sourcesContent":["","import {\n NodeChildProcessSpawner,\n NodeCrypto,\n NodeFileSystem,\n NodePath,\n NodeRuntime,\n NodeStdio,\n NodeTerminal,\n} from '@effect/platform-node-shared'\nimport { Config, Console, Effect, Layer, Match, Option, Redacted } from 'effect'\nimport { Argument, CliError, Command, Flag, Prompt } from 'effect/unstable/cli'\nimport { FetchHttpClient } from 'effect/unstable/http'\nimport packageManifest from '../package.json' with { type: 'json' }\nimport { applyContent, diffContent, exportContent, planContent } from './cli/content'\nimport { bootstrapCmsDevelopment, doctorCmsDevelopment, resetCmsDevelopment } from './internal/dev-lifecycle'\n\nconst root = Command.make('cms').pipe(\n Command.withSharedFlags({\n url: Flag.String('url').pipe(Flag.withDefault('')),\n email: Flag.String('email').pipe(Flag.withDefault('')),\n }),\n)\n\nconst directory = Argument.String('directory')\n\nconst overwrite = Flag.Boolean('overwrite').pipe(Flag.withDefault(false))\n\nconst publish = Flag.Boolean('publish').pipe(Flag.withDefault(false))\n\nconst exportCommand = Command.make('export', { directory }, ({ directory }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* exportContent({ url, email }, directory)\n }),\n).pipe(Command.withDescription('Export authored Markdown from a CMS installation'))\n\nconst diffCommand = Command.make('diff', { directory }, ({ directory }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* diffContent({ url, email }, directory)\n }),\n).pipe(Command.withDescription('Compare local Markdown with the current CMS content'))\n\nconst planCommand = Command.make('plan', { directory, overwrite, publish }, ({ directory, overwrite, publish }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* planContent({ url, email }, directory, { overwrite, publish })\n }),\n).pipe(Command.withDescription('Preview content changes without writing to the CMS'))\n\nconst applyCommand = Command.make('apply', { directory, overwrite, publish }, ({ directory, overwrite, publish }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* applyContent({ url, email }, directory, { overwrite, publish })\n }),\n).pipe(Command.withDescription('Apply a validated content plan to the CMS'))\n\nconst content = Command.make('content').pipe(\n Command.withSubcommands([exportCommand, diffCommand, planCommand, applyCommand]),\n)\n\nconst json = Flag.Boolean('json').pipe(Flag.withDefault(false))\n\nconst full = Flag.Boolean('full').pipe(Flag.withDefault(false))\n\nconst rootDirectory = Flag.String('root').pipe(Flag.withDefault('.'))\n\nconst contentDirectory = Flag.String('content').pipe(Flag.withDefault(''))\n\nconst administratorName = Flag.String('name').pipe(Flag.withDefault(''))\n\nconst database = Flag.Boolean('database').pipe(Flag.withDefault(false))\n\nconst media = Flag.Boolean('media').pipe(Flag.withDefault(false))\n\nconst bootstrap = Flag.Boolean('bootstrap').pipe(Flag.withDefault(false))\n\nconst yes = Flag.Boolean('yes').pipe(Flag.withDefault(false))\n\nconst doctorCommand = Command.make('doctor', { full, json, rootDirectory }, ({ full, json, rootDirectory }) =>\n Effect.gen(function* () {\n const report = yield* attempt(() => doctorCmsDevelopment(rootDirectory, full))\n\n if (json) {\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- This flag intentionally emits the report's exact JSON representation.\n yield* Console.log(JSON.stringify(report))\n } else\n for (const check of report.checks) {\n const icon = Match.value(check.status).pipe(\n Match.when('pass', () => '✓'),\n Match.when('fail', () => '✗'),\n Match.when('not-verified', () => '−'),\n Match.exhaustive,\n )\n\n yield* Console.log(`${icon} ${check.label}${check.detail ? `\\n\\n ${check.detail}` : ''}`)\n }\n\n if (!report.ok) return yield* cliError('One or more CMS development checks failed.')\n }),\n).pipe(Command.withDescription('Check local CMS configuration and infrastructure'))\n\nconst bootstrapCommand = Command.make(\n 'bootstrap',\n { contentDirectory, administratorName, publish, rootDirectory },\n ({ contentDirectory, administratorName, publish, rootDirectory }) =>\n Effect.gen(function* () {\n const administrator = yield* administratorOptions(administratorName, contentDirectory, publish)\n const result = yield* attempt(() => bootstrapCmsDevelopment({ rootDir: rootDirectory, ...administrator }))\n yield* Console.log(\n `Bootstrap complete: administrator ${result.administrator}${result.contentApplied ? ', content applied' : ''}.`,\n )\n }),\n).pipe(Command.withDescription('Provision and initialize a local CMS installation'))\n\nconst resetCommand = Command.make(\n 'reset',\n { bootstrap, contentDirectory, database, json, media, publish, rootDirectory, yes },\n ({ bootstrap, contentDirectory, database, json, media, publish, rootDirectory, yes }) =>\n Effect.gen(function* () {\n if (!database && !media) return yield* cliError('Choose --database, --media, or both.')\n const administrator = bootstrap && yes ? yield* administratorOptions('', contentDirectory, publish) : undefined\n\n const result = yield* attempt(() =>\n resetCmsDevelopment({ rootDir: rootDirectory, database, media, yes, bootstrap: administrator }),\n )\n\n if (json) {\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- This flag intentionally emits the report's exact JSON representation.\n return yield* Console.log(JSON.stringify(result))\n }\n\n for (const target of result.targets)\n yield* Console.log(`${result.executed ? '✓ Reset' : 'Would reset'} ${target}`)\n\n if (!result.executed) yield* Console.log('Dry run only. Add --yes to execute these exact targets.')\n }),\n).pipe(Command.withDescription('Reset CMS-owned local data with a dry-run default'))\n\nconst dev = Command.make('dev').pipe(Command.withSubcommands([doctorCommand, bootstrapCommand, resetCommand]))\n\nconst administratorOptions = Effect.fn('CmsCli.dev.administratorOptions')(function* (\n name: string,\n content: string,\n publishContent: boolean,\n) {\n const email = yield* Config.String('CMS_EMAIL').pipe(Config.withDefault(''))\n const configuredPassword = yield* Config.Redacted('CMS_PASSWORD').pipe(Config.option)\n\n const password = Option.isSome(configuredPassword)\n ? Redacted.value(configuredPassword.value)\n : Redacted.value(yield* Prompt.Password({ message: `Password for ${email || 'local administrator'}` }))\n\n if (!email || !password) return yield* cliError('CMS_EMAIL and CMS_PASSWORD are required.')\n\n return {\n content: content || undefined,\n email,\n name: name || (yield* Config.String('CMS_NAME').pipe(Config.withDefault('Administrator'))),\n password,\n publish: publishContent,\n }\n})\n\nfunction cliError(message: string) {\n return new CliError.UserError({ cause: new Error(message), userMessage: message })\n}\n\nfunction attempt<Value>(operation: () => Value | PromiseLike<Value>) {\n return Effect.tryPromise({\n try: () => Promise.resolve(operation()),\n catch: (cause) => cliError(cause instanceof Error ? cause.message : String(cause)),\n })\n}\n\nroot.pipe(\n Command.withSubcommands([content, dev]),\n Command.run({ version: packageManifest.version }),\n Effect.provide(\n Layer.mergeAll(\n NodeFileSystem.layer,\n NodePath.layer,\n NodeCrypto.layer,\n NodeTerminal.layer,\n NodeStdio.layer,\n NodeChildProcessSpawner.layer.pipe(Layer.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer))),\n FetchHttpClient.layer,\n ),\n ),\n NodeRuntime.runMain,\n)\n"],"mappings":";;;;;;;;;;;;ACgBA,MAAM,OAAO,QAAQ,KAAK,KAAK,CAAC,CAAC,KAC/B,QAAQ,gBAAgB;CACtB,KAAK,KAAK,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;CACjD,OAAO,KAAK,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;AACvD,CAAC,CACH;AAEA,MAAM,YAAY,SAAS,OAAO,WAAW;AAE7C,MAAM,YAAY,KAAK,QAAQ,WAAW,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAExE,MAAM,UAAU,KAAK,QAAQ,SAAS,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAEpE,MAAM,gBAAgB,QAAQ,KAAK,UAAU,EAAE,UAAU,IAAI,EAAE,gBAC7D,OAAO,IAAI,aAAa;CACtB,MAAM,EAAE,KAAK,UAAU,OAAO;CAE9B,OAAO,OAAO,cAAc;EAAE;EAAK;CAAM,GAAG,SAAS;AACvD,CAAC,CACH,CAAC,CAAC,KAAK,QAAQ,gBAAgB,kDAAkD,CAAC;AAElF,MAAM,cAAc,QAAQ,KAAK,QAAQ,EAAE,UAAU,IAAI,EAAE,gBACzD,OAAO,IAAI,aAAa;CACtB,MAAM,EAAE,KAAK,UAAU,OAAO;CAE9B,OAAO,OAAO,YAAY;EAAE;EAAK;CAAM,GAAG,SAAS;AACrD,CAAC,CACH,CAAC,CAAC,KAAK,QAAQ,gBAAgB,qDAAqD,CAAC;AAErF,MAAM,cAAc,QAAQ,KAAK,QAAQ;CAAE;CAAW;CAAW;AAAQ,IAAI,EAAE,WAAW,WAAW,cACnG,OAAO,IAAI,aAAa;CACtB,MAAM,EAAE,KAAK,UAAU,OAAO;CAE9B,OAAO,OAAO,YAAY;EAAE;EAAK;CAAM,GAAG,WAAW;EAAE;EAAW;CAAQ,CAAC;AAC7E,CAAC,CACH,CAAC,CAAC,KAAK,QAAQ,gBAAgB,oDAAoD,CAAC;AAEpF,MAAM,eAAe,QAAQ,KAAK,SAAS;CAAE;CAAW;CAAW;AAAQ,IAAI,EAAE,WAAW,WAAW,cACrG,OAAO,IAAI,aAAa;CACtB,MAAM,EAAE,KAAK,UAAU,OAAO;CAE9B,OAAO,OAAO,aAAa;EAAE;EAAK;CAAM,GAAG,WAAW;EAAE;EAAW;CAAQ,CAAC;AAC9E,CAAC,CACH,CAAC,CAAC,KAAK,QAAQ,gBAAgB,2CAA2C,CAAC;AAE3E,MAAM,UAAU,QAAQ,KAAK,SAAS,CAAC,CAAC,KACtC,QAAQ,gBAAgB;CAAC;CAAe;CAAa;CAAa;AAAY,CAAC,CACjF;AAEA,MAAM,OAAO,KAAK,QAAQ,MAAM,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAE9D,MAAM,OAAO,KAAK,QAAQ,MAAM,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAE9D,MAAM,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK,YAAY,GAAG,CAAC;AAEpE,MAAM,mBAAmB,KAAK,OAAO,SAAS,CAAC,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;AAEzE,MAAM,oBAAoB,KAAK,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;AAEvE,MAAM,WAAW,KAAK,QAAQ,UAAU,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAEtE,MAAM,QAAQ,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAEhE,MAAM,YAAY,KAAK,QAAQ,WAAW,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAExE,MAAM,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC,KAAK,KAAK,YAAY,KAAK,CAAC;AAE5D,MAAM,gBAAgB,QAAQ,KAAK,UAAU;CAAE;CAAM;CAAM;AAAc,IAAI,EAAE,MAAM,MAAM,oBACzF,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,cAAc,qBAAqB,eAAe,IAAI,CAAC;CAE7E,IAAI,MAEF,OAAO,QAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;MAEzC,KAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,CAAC,CAAC,KACrC,MAAM,KAAK,cAAc,GAAG,GAC5B,MAAM,KAAK,cAAc,GAAG,GAC5B,MAAM,KAAK,sBAAsB,GAAG,GACpC,MAAM,UACR;EAEA,OAAO,QAAQ,IAAI,GAAG,KAAK,GAAG,MAAM,QAAQ,MAAM,SAAS,SAAS,MAAM,WAAW,IAAI;CAC3F;CAEF,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,SAAS,4CAA4C;AACrF,CAAC,CACH,CAAC,CAAC,KAAK,QAAQ,gBAAgB,kDAAkD,CAAC;AAElF,MAAM,mBAAmB,QAAQ,KAC/B,aACA;CAAE;CAAkB;CAAmB;CAAS;AAAc,IAC7D,EAAE,kBAAkB,mBAAmB,SAAS,oBAC/C,OAAO,IAAI,aAAa;CACtB,MAAM,gBAAgB,OAAO,qBAAqB,mBAAmB,kBAAkB,OAAO;CAC9F,MAAM,SAAS,OAAO,cAAc,wBAAwB;EAAE,SAAS;EAAe,GAAG;CAAc,CAAC,CAAC;CACzG,OAAO,QAAQ,IACb,qCAAqC,OAAO,gBAAgB,OAAO,iBAAiB,sBAAsB,GAAG,EAC/G;AACF,CAAC,CACL,CAAC,CAAC,KAAK,QAAQ,gBAAgB,mDAAmD,CAAC;AAEnF,MAAM,eAAe,QAAQ,KAC3B,SACA;CAAE;CAAW;CAAkB;CAAU;CAAM;CAAO;CAAS;CAAe;AAAI,IACjF,EAAE,WAAW,kBAAkB,UAAU,MAAM,OAAO,SAAS,eAAe,UAC7E,OAAO,IAAI,aAAa;CACtB,IAAI,CAAC,YAAY,CAAC,OAAO,OAAO,OAAO,SAAS,sCAAsC;CACtF,MAAM,gBAAgB,aAAa,MAAM,OAAO,qBAAqB,IAAI,kBAAkB,OAAO,IAAI;CAEtG,MAAM,SAAS,OAAO,cACpB,oBAAoB;EAAE,SAAS;EAAe;EAAU;EAAO;EAAK,WAAW;CAAc,CAAC,CAChG;CAEA,IAAI,MAEF,OAAO,OAAO,QAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;CAGlD,KAAK,MAAM,UAAU,OAAO,SAC1B,OAAO,QAAQ,IAAI,GAAG,OAAO,WAAW,YAAY,cAAc,GAAG,QAAQ;CAE/E,IAAI,CAAC,OAAO,UAAU,OAAO,QAAQ,IAAI,yDAAyD;AACpG,CAAC,CACL,CAAC,CAAC,KAAK,QAAQ,gBAAgB,mDAAmD,CAAC;AAEnF,MAAM,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,KAAK,QAAQ,gBAAgB;CAAC;CAAe;CAAkB;AAAY,CAAC,CAAC;AAE7G,MAAM,uBAAuB,OAAO,GAAG,iCAAiC,CAAC,CAAC,WACxE,MACA,SACA,gBACA;CACA,MAAM,QAAQ,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC,KAAK,OAAO,YAAY,EAAE,CAAC;CAC3E,MAAM,qBAAqB,OAAO,OAAO,SAAS,cAAc,CAAC,CAAC,KAAK,OAAO,MAAM;CAEpF,MAAM,WAAW,OAAO,OAAO,kBAAkB,IAC7C,SAAS,MAAM,mBAAmB,KAAK,IACvC,SAAS,MAAM,OAAO,OAAO,SAAS,EAAE,SAAS,gBAAgB,SAAS,wBAAwB,CAAC,CAAC;CAExG,IAAI,CAAC,SAAS,CAAC,UAAU,OAAO,OAAO,SAAS,0CAA0C;CAE1F,OAAO;EACL,SAAS,WAAW;EACpB;EACA,MAAM,SAAS,OAAO,OAAO,OAAO,UAAU,CAAC,CAAC,KAAK,OAAO,YAAY,eAAe,CAAC;EACxF;EACA,SAAS;CACX;AACF,CAAC;AAED,SAAS,SAAS,SAAiB;CACjC,OAAO,IAAI,SAAS,UAAU;EAAE,OAAO,IAAI,MAAM,OAAO;EAAG,aAAa;CAAQ,CAAC;AACnF;AAEA,SAAS,QAAe,WAA6C;CACnE,OAAO,OAAO,WAAW;EACvB,WAAW,QAAQ,QAAQ,UAAU,CAAC;EACtC,QAAQ,UAAU,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CACnF,CAAC;AACH;AAEA,KAAK,KACH,QAAQ,gBAAgB,CAAC,SAAS,GAAG,CAAC,GACtC,QAAQ,IAAI,EAAWA,QAAwB,CAAC,GAChD,OAAO,QACL,MAAM,SACJ,eAAe,OACf,SAAS,OACT,WAAW,OACX,aAAa,OACb,UAAU,OACV,wBAAwB,MAAM,KAAK,MAAM,QAAQ,MAAM,MAAM,eAAe,OAAO,SAAS,KAAK,CAAC,CAAC,GACnG,gBAAgB,KAClB,CACF,GACA,YAAY,OACd"}
1
+ {"version":3,"file":"cli.js","names":["packageManifest.version"],"sources":["../../../package.json","../../../src/cli.ts"],"sourcesContent":["","import {\n NodeChildProcessSpawner,\n NodeCrypto,\n NodeFileSystem,\n NodePath,\n NodeRuntime,\n NodeStdio,\n NodeTerminal,\n} from '@effect/platform-node-shared'\nimport { Config, Console, Effect, Layer, Match, Option, Redacted, Runtime, Schema } from 'effect'\nimport { Argument, CliError, Command, Flag, Prompt } from 'effect/unstable/cli'\nimport { FetchHttpClient } from 'effect/unstable/http'\nimport packageManifest from '../package.json' with { type: 'json' }\nimport { applyContent, diffContent, exportContent, planContent } from './cli/content'\nimport {\n bootstrapCmsDevelopment,\n doctorCmsDevelopment,\n loadCmsProjectEnvironment,\n resetCmsDevelopment,\n} from './internal/dev-lifecycle'\nimport { diagnosticFromCause, renderDiagnostic } from './internal/diagnostics'\n\nconst rootDirectory = Flag.String('root').pipe(Flag.withDefault('.'))\n\nconst envFile = Flag.String('env-file').pipe(Flag.withDefault(''))\n\nconst verbose = Flag.Boolean('verbose').pipe(Flag.withDefault(false))\n\nconst json = Flag.Boolean('json').pipe(Flag.withDefault(false))\n\nconst root = Command.make('cms').pipe(\n Command.withSharedFlags({\n url: Flag.String('url').pipe(Flag.withDefault('')),\n email: Flag.String('email').pipe(Flag.withDefault('')),\n rootDirectory,\n envFile,\n verbose,\n json,\n }),\n)\n\nconst directory = Argument.String('directory')\n\nconst overwrite = Flag.Boolean('overwrite').pipe(Flag.withDefault(false))\n\nconst publish = Flag.Boolean('publish').pipe(Flag.withDefault(false))\n\nconst exportCommand = Command.make('export', { directory }, ({ directory }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* exportContent({ url, email }, directory)\n }),\n).pipe(Command.withDescription('Export authored Markdown from a CMS installation'))\n\nconst diffCommand = Command.make('diff', { directory }, ({ directory }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* diffContent({ url, email }, directory)\n }),\n).pipe(Command.withDescription('Compare local Markdown with the current CMS content'))\n\nconst planCommand = Command.make('plan', { directory, overwrite, publish }, ({ directory, overwrite, publish }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* planContent({ url, email }, directory, { overwrite, publish })\n }),\n).pipe(Command.withDescription('Preview content changes without writing to the CMS'))\n\nconst applyCommand = Command.make('apply', { directory, overwrite, publish }, ({ directory, overwrite, publish }) =>\n Effect.gen(function* () {\n const { url, email } = yield* root\n\n return yield* applyContent({ url, email }, directory, { overwrite, publish })\n }),\n).pipe(Command.withDescription('Apply a validated content plan to the CMS'))\n\nconst content = Command.make('content').pipe(\n Command.withSubcommands([exportCommand, diffCommand, planCommand, applyCommand]),\n)\n\nconst full = Flag.Boolean('full').pipe(Flag.withDefault(false))\n\nconst contentDirectory = Flag.String('content').pipe(Flag.withDefault(''))\n\nconst administratorName = Flag.String('name').pipe(Flag.withDefault(''))\n\nconst database = Flag.Boolean('database').pipe(Flag.withDefault(false))\n\nconst media = Flag.Boolean('media').pipe(Flag.withDefault(false))\n\nconst bootstrap = Flag.Boolean('bootstrap').pipe(Flag.withDefault(false))\n\nconst yes = Flag.Boolean('yes').pipe(Flag.withDefault(false))\n\nconst doctorCommand = Command.make('doctor', { full }, ({ full }) =>\n Effect.gen(function* () {\n const options = yield* root\n\n const report = yield* attempt('doctor.configuration', options, () =>\n doctorCmsDevelopment(options.rootDirectory, full, options.envFile || undefined),\n )\n\n if (options.json) {\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- This flag intentionally emits the report's exact JSON representation.\n yield* Console.log(JSON.stringify(report))\n } else\n for (const check of report.checks) {\n const icon = Match.value(check.status).pipe(\n Match.when('pass', () => '✓'),\n Match.when('fail', () => '✗'),\n Match.when('not-verified', () => '−'),\n Match.exhaustive,\n )\n\n yield* Console.log(`${icon} ${check.label}${check.detail ? `\\n\\n ${check.detail}` : ''}`)\n }\n\n if (!report.ok)\n return yield* options.json ? new JsonCommandFailed({}) : cliError('One or more CMS development checks failed.')\n }),\n).pipe(Command.withDescription('Check local CMS configuration and infrastructure'))\n\nconst bootstrapCommand = Command.make(\n 'bootstrap',\n { contentDirectory, administratorName, publish },\n ({ contentDirectory, administratorName, publish }) =>\n Effect.gen(function* () {\n const options = yield* root\n yield* attempt('bootstrap.environment', options, () =>\n loadCmsProjectEnvironment(options.rootDirectory, options.envFile || undefined),\n )\n\n const administrator = yield* administratorOptions(\n administratorName,\n contentDirectory,\n publish,\n options,\n 'bootstrap.credentials',\n )\n\n const result = yield* attempt('bootstrap.installation', options, () =>\n bootstrapCmsDevelopment({\n rootDir: options.rootDirectory,\n envFile: options.envFile || undefined,\n verbose: options.verbose,\n ...administrator,\n }),\n )\n\n if (options.json) {\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- This flag intentionally emits the report's exact JSON representation.\n yield* Console.log(JSON.stringify(result))\n } else\n yield* Console.log(\n `Bootstrap complete: administrator ${result.administrator}${result.contentApplied ? ', content applied' : ''}.`,\n )\n }),\n).pipe(Command.withDescription('Provision and initialize a local CMS installation'))\n\nconst resetCommand = Command.make(\n 'reset',\n { bootstrap, contentDirectory, database, media, publish, yes },\n ({ bootstrap, contentDirectory, database, media, publish, yes }) =>\n Effect.gen(function* () {\n const options = yield* root\n\n if (!database && !media)\n return yield* commandFailure('reset.arguments', options, new Error('Choose --database, --media, or both.'))\n\n if (bootstrap && yes)\n yield* attempt('reset.environment', options, () =>\n loadCmsProjectEnvironment(options.rootDirectory, options.envFile || undefined),\n )\n\n const administrator =\n bootstrap && yes\n ? yield* administratorOptions('', contentDirectory, publish, options, 'reset.credentials')\n : undefined\n\n const result = yield* attempt('reset.installation', options, () =>\n resetCmsDevelopment({\n rootDir: options.rootDirectory,\n envFile: options.envFile || undefined,\n database,\n media,\n yes,\n verbose: options.verbose,\n bootstrap: administrator,\n }),\n )\n\n if (options.json) {\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- This flag intentionally emits the report's exact JSON representation.\n return yield* Console.log(JSON.stringify(result))\n }\n\n for (const target of result.targets)\n yield* Console.log(`${result.executed ? '✓ Reset' : 'Would reset'} ${target}`)\n\n if (!result.executed) yield* Console.log('Dry run only. Add --yes to execute these exact targets.')\n }),\n).pipe(Command.withDescription('Reset CMS-owned local data with a dry-run default'))\n\nconst dev = Command.make('dev').pipe(Command.withSubcommands([doctorCommand, bootstrapCommand, resetCommand]))\n\nconst administratorOptions = Effect.fn('CmsCli.dev.administratorOptions')(function* (\n name: string,\n content: string,\n publishContent: boolean,\n options: { readonly json: boolean; readonly verbose: boolean },\n phase: string,\n) {\n const email = yield* Config.String('CMS_EMAIL').pipe(Config.withDefault(''))\n const configuredPassword = yield* Config.Redacted('CMS_PASSWORD').pipe(Config.option)\n\n if (!email) return yield* commandFailure(phase, options, new Error('CMS_EMAIL is required.'))\n\n if (Option.isNone(configuredPassword) && options.json)\n return yield* commandFailure(phase, options, new Error('CMS_PASSWORD is required.'))\n\n const password = Option.isSome(configuredPassword)\n ? Redacted.value(configuredPassword.value)\n : Redacted.value(yield* Prompt.Password({ message: `Password for ${email}` }))\n\n if (!password) return yield* commandFailure(phase, options, new Error('CMS_PASSWORD is required.'))\n\n return {\n content: content || undefined,\n email,\n name: name || (yield* Config.String('CMS_NAME').pipe(Config.withDefault('Administrator'))),\n password,\n publish: publishContent,\n }\n})\n\nfunction cliError(message: string) {\n return new CliError.UserError({ cause: new Error(message), userMessage: message })\n}\n\nclass CliOperationFailed extends Schema.TaggedError<CliOperationFailed>()('CliOperationFailed', {\n cause: Schema.Defect(),\n}) {}\n\nclass JsonCommandFailed extends Schema.TaggedError<JsonCommandFailed>()('JsonCommandFailed', {}) {\n override readonly [Runtime.errorExitCode] = 1\n override [Runtime.errorReported] = false\n}\n\nfunction attempt<Value>(\n phase: string,\n options: { readonly json: boolean; readonly verbose: boolean },\n operation: () => Value | PromiseLike<Value>,\n) {\n return Effect.tryPromise({\n try: () => Promise.resolve(operation()),\n catch: (cause) => new CliOperationFailed({ cause }),\n }).pipe(Effect.catch((failure) => commandFailure(phase, options, failure.cause)))\n}\n\nfunction commandFailure(phase: string, options: { readonly json: boolean; readonly verbose: boolean }, cause: unknown) {\n return Effect.gen(function* () {\n const diagnostic = diagnosticFromCause(phase, cause)\n\n if (!options.json) return yield* cliError(renderDiagnostic(diagnostic, options.verbose))\n\n const { details: _details, ...output } = diagnostic\n\n // oxlint-disable-next-line effecttsgo/prefer-schema-over-json -- CLI JSON is an intentionally stable plain object.\n yield* Console.log(JSON.stringify(output))\n\n return yield* new JsonCommandFailed({})\n })\n}\n\nroot.pipe(\n Command.withSubcommands([content, dev]),\n Command.run({ version: packageManifest.version }),\n Effect.provide(\n Layer.mergeAll(\n NodeFileSystem.layer,\n NodePath.layer,\n NodeCrypto.layer,\n NodeTerminal.layer,\n NodeStdio.layer,\n NodeChildProcessSpawner.layer.pipe(Layer.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer))),\n FetchHttpClient.layer,\n ),\n ),\n NodeRuntime.runMain,\n)\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as DocumentPreviewMessage, o as isDocumentPreviewMessage, t as DOCUMENT_PREVIEW_CHANNEL } from "./models-k2nXyD6q.js";
1
+ import { n as DocumentPreviewMessage, o as isDocumentPreviewMessage, t as DOCUMENT_PREVIEW_CHANNEL } from "./models-C9kF9JlQ.js";
2
2
  import { Effect, Exit, Result, Schedule, Schema, Scope } from "effect";
3
3
 
4
4
  //#region ../protocol/src/internal/preview/bridge.ts
@@ -89,4 +89,4 @@ async function openSitePreviewConnection(options) {
89
89
 
90
90
  //#endregion
91
91
  export { openSitePreviewConnection as t };
92
- //# sourceMappingURL=connection-BPf4BQUZ.js.map
92
+ //# sourceMappingURL=connection-D5S9Iph-.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection-BPf4BQUZ.js","names":["DocumentPreviewMessageSchema"],"sources":["../../../../protocol/src/internal/preview/bridge.ts","../../../src/runtime/preview/connection.ts"],"sourcesContent":["import type {\n DocumentPreviewMessage,\n PreviewOrigin,\n PreviewSessionId,\n PreviewSitePayload,\n StudioPreviewPayload,\n} from './models'\nimport type { Scope } from 'effect'\nimport { Effect, Result, Schedule, Schema } from 'effect'\nimport {\n DOCUMENT_PREVIEW_CHANNEL,\n DocumentPreviewMessage as DocumentPreviewMessageSchema,\n isDocumentPreviewMessage,\n} from './models'\n\nexport interface PreviewMessageEvent {\n readonly data: unknown\n readonly origin: string\n readonly source: object | null\n}\n\nexport interface PreviewMessageSource {\n readonly addEventListener: (type: 'message', listener: (event: PreviewMessageEvent) => void) => void\n readonly removeEventListener: (type: 'message', listener: (event: PreviewMessageEvent) => void) => void\n}\n\nexport interface PreviewMessageTarget {\n readonly postMessage: (message: DocumentPreviewMessage, targetOrigin: string) => void\n}\n\nexport type PreviewBridgeState = 'closed' | 'connecting' | 'ready'\n\nexport interface PreviewHostBridge {\n readonly reconnect: () => void\n readonly send: (payload: Exclude<StudioPreviewPayload, { readonly type: 'studio:connect' }>) => boolean\n readonly state: () => PreviewBridgeState\n readonly subscribe: (listener: (message: PreviewSitePayload) => void) => () => void\n}\n\nexport interface PreviewSiteBridge {\n readonly send: (payload: Exclude<PreviewSitePayload, { readonly type: 'preview:ready' }>) => boolean\n readonly subscribe: (listener: (message: StudioPreviewPayload) => void) => () => void\n}\n\nexport interface PreviewHostBridgeOptions {\n readonly handshakeIntervalMs?: number\n readonly sessionId: PreviewSessionId\n readonly target: () => PreviewMessageTarget | undefined\n readonly targetOrigin: PreviewOrigin\n readonly view: PreviewMessageSource\n}\n\nexport interface PreviewSiteBridgeOptions {\n readonly parent: PreviewMessageTarget\n readonly parentOrigin: PreviewOrigin\n readonly route: PreviewSitePayload & { readonly type: 'preview:ready' }\n readonly sessionId: PreviewSessionId\n readonly view: PreviewMessageSource\n}\n\nexport function makePreviewHostBridge(\n options: PreviewHostBridgeOptions,\n): Effect.Effect<PreviewHostBridge, never, Scope.Scope> {\n return Effect.acquireRelease(\n Effect.context<never>().pipe(\n Effect.map((context) => createPreviewHostBridge(options, Effect.runForkWith(context))),\n ),\n (bridge) => Effect.sync(() => bridge.close()),\n ).pipe(Effect.map(({ close: _close, ...bridge }) => bridge))\n}\n\nexport function makePreviewSiteBridge(\n options: PreviewSiteBridgeOptions,\n): Effect.Effect<PreviewSiteBridge, never, Scope.Scope> {\n return Effect.acquireRelease(\n Effect.sync(() => createPreviewSiteBridge(options)),\n (bridge) => Effect.sync(() => bridge.close()),\n ).pipe(Effect.map(({ close: _close, ...bridge }) => bridge))\n}\n\nfunction createPreviewHostBridge(options: PreviewHostBridgeOptions, runFork: typeof Effect.runFork) {\n const listeners = new Set<(message: PreviewSitePayload) => void>()\n let retry: ReturnType<typeof Effect.runFork> | undefined\n let state: PreviewBridgeState = 'connecting'\n\n const stopRetry = (): void => {\n if (retry === undefined) return\n retry.interruptUnsafe()\n retry = undefined\n }\n\n const post = (payload: StudioPreviewPayload): boolean => {\n const target = options.target()\n\n if (!target) return false\n target.postMessage(envelope(options.sessionId, payload), options.targetOrigin)\n\n return true\n }\n\n const connect = (): void => {\n post({ type: 'studio:connect' })\n }\n\n const reconnect = (): void => {\n if (state === 'closed') return\n state = 'connecting'\n stopRetry()\n const interval = options.handshakeIntervalMs ?? 250\n retry = runFork(Effect.sync(connect).pipe(Effect.repeat(Schedule.spaced(interval)), Effect.delay(interval)))\n connect()\n }\n\n const receive = (event: PreviewMessageEvent): void => {\n const target = options.target()\n\n if (!target) return\n const message = receivePreviewMessage(event, target, options.targetOrigin, options.sessionId)\n\n if (!message || !isPreviewSiteMessage(message)) return\n\n if (message.type === 'preview:ready') {\n state = 'ready'\n stopRetry()\n }\n\n for (const listener of listeners) listener(message)\n }\n\n const close = (): void => {\n if (state === 'closed') return\n state = 'closed'\n stopRetry()\n options.view.removeEventListener('message', receive)\n listeners.clear()\n }\n\n options.view.addEventListener('message', receive)\n reconnect()\n\n return {\n close,\n reconnect,\n send: (payload: Exclude<StudioPreviewPayload, { readonly type: 'studio:connect' }>) =>\n state === 'ready' && post(payload),\n state: () => state,\n subscribe: (listener: (message: PreviewSitePayload) => void) => {\n listeners.add(listener)\n\n return () => listeners.delete(listener)\n },\n }\n}\n\nfunction createPreviewSiteBridge(options: PreviewSiteBridgeOptions) {\n const listeners = new Set<(message: StudioPreviewPayload) => void>()\n let active = false\n let closed = false\n\n const post = (payload: PreviewSitePayload): boolean => {\n if (!active || closed) return false\n options.parent.postMessage(envelope(options.sessionId, payload), options.parentOrigin)\n\n return true\n }\n\n const receive = (event: PreviewMessageEvent): void => {\n if (closed) return\n const message = receivePreviewMessage(event, options.parent, options.parentOrigin, options.sessionId)\n\n if (!message || !isStudioPreviewMessage(message)) return\n\n if (message.type === 'studio:connect') {\n active = true\n post(options.route)\n }\n\n for (const listener of listeners) listener(message)\n }\n\n const close = (): void => {\n if (closed) return\n closed = true\n active = false\n options.view.removeEventListener('message', receive)\n listeners.clear()\n }\n\n options.view.addEventListener('message', receive)\n\n return {\n close,\n send: post,\n subscribe: (listener: (message: StudioPreviewPayload) => void) => {\n listeners.add(listener)\n\n return () => listeners.delete(listener)\n },\n }\n}\n\nfunction envelope(\n sessionId: PreviewSessionId,\n payload: PreviewSitePayload | StudioPreviewPayload,\n): DocumentPreviewMessage {\n return Result.getOrThrow(\n Schema.decodeResult(Schema.toType(DocumentPreviewMessageSchema))({\n channel: DOCUMENT_PREVIEW_CHANNEL,\n sessionId,\n ...payload,\n }),\n )\n}\n\nfunction receivePreviewMessage(\n event: PreviewMessageEvent,\n source: PreviewMessageTarget,\n origin: PreviewOrigin,\n sessionId: PreviewSessionId,\n): DocumentPreviewMessage | undefined {\n if (event.origin !== origin || event.source !== source || !isDocumentPreviewMessage(event.data)) return undefined\n\n return event.data.sessionId === sessionId ? event.data : undefined\n}\n\nfunction isPreviewSiteMessage(\n message: DocumentPreviewMessage,\n): message is Extract<DocumentPreviewMessage, { readonly type: `preview:${string}` }> {\n return message.type.startsWith('preview:')\n}\n\nfunction isStudioPreviewMessage(\n message: DocumentPreviewMessage,\n): message is Extract<DocumentPreviewMessage, { readonly type: `studio:${string}` }> {\n return message.type.startsWith('studio:')\n}\n","import type { PublicPath } from '@internal/protocol/Documents'\nimport type { PreviewOrigin, PreviewSessionId, PreviewSiteBridge } from '@internal/protocol/Preview'\nimport { makePreviewSiteBridge } from '@internal/protocol/Preview'\nimport { Effect, Exit, Scope } from 'effect'\n\nexport interface SitePreviewConnection extends PreviewSiteBridge {\n readonly close: () => Promise<void>\n}\n\nexport async function openSitePreviewConnection(options: {\n readonly editorOrigin: PreviewOrigin\n readonly route: PublicPath\n readonly sessionId: PreviewSessionId\n}): Promise<SitePreviewConnection> {\n const scope = await Effect.runPromise(Scope.make())\n\n try {\n const bridge = await Effect.runPromise(\n makePreviewSiteBridge({\n parent: window.parent,\n parentOrigin: options.editorOrigin,\n route: { path: options.route, type: 'preview:ready' },\n sessionId: options.sessionId,\n view: window,\n }).pipe(Effect.provideService(Scope.Scope, scope)),\n )\n\n let closed = false\n\n return {\n async close() {\n if (closed) return\n closed = true\n await Effect.runPromise(Scope.close(scope, Exit.void))\n },\n send: bridge.send,\n subscribe: bridge.subscribe,\n }\n } catch (cause) {\n await Effect.runPromise(Scope.close(scope, Exit.fail(cause)))\n throw cause\n }\n}\n"],"mappings":";;;;AAuEA,SAAgB,sBACd,SACsD;CACtD,OAAO,OAAO,eACZ,OAAO,WAAW,wBAAwB,OAAO,CAAC,IACjD,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC,CAC9C,CAAC,CAAC,KAAK,OAAO,KAAK,EAAE,OAAO,QAAQ,GAAG,aAAa,MAAM,CAAC;AAC7D;AA4EA,SAAS,wBAAwB,SAAmC;CAClE,MAAM,4BAAY,IAAI,IAA6C;CACnE,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,MAAM,QAAQ,YAAyC;EACrD,IAAI,CAAC,UAAU,QAAQ,OAAO;EAC9B,QAAQ,OAAO,YAAY,SAAS,QAAQ,WAAW,OAAO,GAAG,QAAQ,YAAY;EAErF,OAAO;CACT;CAEA,MAAM,WAAW,UAAqC;EACpD,IAAI,QAAQ;EACZ,MAAM,UAAU,sBAAsB,OAAO,QAAQ,QAAQ,QAAQ,cAAc,QAAQ,SAAS;EAEpG,IAAI,CAAC,WAAW,CAAC,uBAAuB,OAAO,GAAG;EAElD,IAAI,QAAQ,SAAS,kBAAkB;GACrC,SAAS;GACT,KAAK,QAAQ,KAAK;EACpB;EAEA,KAAK,MAAM,YAAY,WAAW,SAAS,OAAO;CACpD;CAEA,MAAM,cAAoB;EACxB,IAAI,QAAQ;EACZ,SAAS;EACT,SAAS;EACT,QAAQ,KAAK,oBAAoB,WAAW,OAAO;EACnD,UAAU,MAAM;CAClB;CAEA,QAAQ,KAAK,iBAAiB,WAAW,OAAO;CAEhD,OAAO;EACL;EACA,MAAM;EACN,YAAY,aAAsD;GAChE,UAAU,IAAI,QAAQ;GAEtB,aAAa,UAAU,OAAO,QAAQ;EACxC;CACF;AACF;AAEA,SAAS,SACP,WACA,SACwB;CACxB,OAAO,OAAO,WACZ,OAAO,aAAa,OAAO,OAAOA,sBAA4B,CAAC,CAAC,CAAC;EAC/D,SAAS;EACT;EACA,GAAG;CACL,CAAC,CACH;AACF;AAEA,SAAS,sBACP,OACA,QACA,QACA,WACoC;CACpC,IAAI,MAAM,WAAW,UAAU,MAAM,WAAW,UAAU,CAAC,yBAAyB,MAAM,IAAI,GAAG,OAAO;CAExG,OAAO,MAAM,KAAK,cAAc,YAAY,MAAM,OAAO;AAC3D;AAQA,SAAS,uBACP,SACmF;CACnF,OAAO,QAAQ,KAAK,WAAW,SAAS;AAC1C;;;;AClOA,eAAsB,0BAA0B,SAIb;CACjC,MAAM,QAAQ,MAAM,OAAO,WAAW,MAAM,KAAK,CAAC;CAElD,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAC1B,sBAAsB;GACpB,QAAQ,OAAO;GACf,cAAc,QAAQ;GACtB,OAAO;IAAE,MAAM,QAAQ;IAAO,MAAM;GAAgB;GACpD,WAAW,QAAQ;GACnB,MAAM;EACR,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,MAAM,OAAO,KAAK,CAAC,CACnD;EAEA,IAAI,SAAS;EAEb,OAAO;GACL,MAAM,QAAQ;IACZ,IAAI,QAAQ;IACZ,SAAS;IACT,MAAM,OAAO,WAAW,MAAM,MAAM,OAAO,KAAK,IAAI,CAAC;GACvD;GACA,MAAM,OAAO;GACb,WAAW,OAAO;EACpB;CACF,SAAS,OAAO;EACd,MAAM,OAAO,WAAW,MAAM,MAAM,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC;EAC5D,MAAM;CACR;AACF"}
1
+ {"version":3,"file":"connection-D5S9Iph-.js","names":["DocumentPreviewMessageSchema"],"sources":["../../../../protocol/src/internal/preview/bridge.ts","../../../src/runtime/preview/connection.ts"],"sourcesContent":["import type {\n DocumentPreviewMessage,\n PreviewOrigin,\n PreviewSessionId,\n PreviewSitePayload,\n StudioPreviewPayload,\n} from './models'\nimport type { Scope } from 'effect'\nimport { Effect, Result, Schedule, Schema } from 'effect'\nimport {\n DOCUMENT_PREVIEW_CHANNEL,\n DocumentPreviewMessage as DocumentPreviewMessageSchema,\n isDocumentPreviewMessage,\n} from './models'\n\nexport interface PreviewMessageEvent {\n readonly data: unknown\n readonly origin: string\n readonly source: object | null\n}\n\nexport interface PreviewMessageSource {\n readonly addEventListener: (type: 'message', listener: (event: PreviewMessageEvent) => void) => void\n readonly removeEventListener: (type: 'message', listener: (event: PreviewMessageEvent) => void) => void\n}\n\nexport interface PreviewMessageTarget {\n readonly postMessage: (message: DocumentPreviewMessage, targetOrigin: string) => void\n}\n\nexport type PreviewBridgeState = 'closed' | 'connecting' | 'ready'\n\nexport interface PreviewHostBridge {\n readonly reconnect: () => void\n readonly send: (payload: Exclude<StudioPreviewPayload, { readonly type: 'studio:connect' }>) => boolean\n readonly state: () => PreviewBridgeState\n readonly subscribe: (listener: (message: PreviewSitePayload) => void) => () => void\n}\n\nexport interface PreviewSiteBridge {\n readonly send: (payload: Exclude<PreviewSitePayload, { readonly type: 'preview:ready' }>) => boolean\n readonly subscribe: (listener: (message: StudioPreviewPayload) => void) => () => void\n}\n\nexport interface PreviewHostBridgeOptions {\n readonly handshakeIntervalMs?: number\n readonly sessionId: PreviewSessionId\n readonly target: () => PreviewMessageTarget | undefined\n readonly targetOrigin: PreviewOrigin\n readonly view: PreviewMessageSource\n}\n\nexport interface PreviewSiteBridgeOptions {\n readonly parent: PreviewMessageTarget\n readonly parentOrigin: PreviewOrigin\n readonly route: PreviewSitePayload & { readonly type: 'preview:ready' }\n readonly sessionId: PreviewSessionId\n readonly view: PreviewMessageSource\n}\n\nexport function makePreviewHostBridge(\n options: PreviewHostBridgeOptions,\n): Effect.Effect<PreviewHostBridge, never, Scope.Scope> {\n return Effect.acquireRelease(\n Effect.context<never>().pipe(\n Effect.map((context) => createPreviewHostBridge(options, Effect.runForkWith(context))),\n ),\n (bridge) => Effect.sync(() => bridge.close()),\n ).pipe(Effect.map(({ close: _close, ...bridge }) => bridge))\n}\n\nexport function makePreviewSiteBridge(\n options: PreviewSiteBridgeOptions,\n): Effect.Effect<PreviewSiteBridge, never, Scope.Scope> {\n return Effect.acquireRelease(\n Effect.sync(() => createPreviewSiteBridge(options)),\n (bridge) => Effect.sync(() => bridge.close()),\n ).pipe(Effect.map(({ close: _close, ...bridge }) => bridge))\n}\n\nfunction createPreviewHostBridge(options: PreviewHostBridgeOptions, runFork: typeof Effect.runFork) {\n const listeners = new Set<(message: PreviewSitePayload) => void>()\n let retry: ReturnType<typeof Effect.runFork> | undefined\n let state: PreviewBridgeState = 'connecting'\n\n const stopRetry = (): void => {\n if (retry === undefined) return\n retry.interruptUnsafe()\n retry = undefined\n }\n\n const post = (payload: StudioPreviewPayload): boolean => {\n const target = options.target()\n\n if (!target) return false\n target.postMessage(envelope(options.sessionId, payload), options.targetOrigin)\n\n return true\n }\n\n const connect = (): void => {\n post({ type: 'studio:connect' })\n }\n\n const reconnect = (): void => {\n if (state === 'closed') return\n state = 'connecting'\n stopRetry()\n const interval = options.handshakeIntervalMs ?? 250\n retry = runFork(Effect.sync(connect).pipe(Effect.repeat(Schedule.spaced(interval)), Effect.delay(interval)))\n connect()\n }\n\n const receive = (event: PreviewMessageEvent): void => {\n const target = options.target()\n\n if (!target) return\n const message = receivePreviewMessage(event, target, options.targetOrigin, options.sessionId)\n\n if (!message || !isPreviewSiteMessage(message)) return\n\n if (message.type === 'preview:ready') {\n state = 'ready'\n stopRetry()\n }\n\n for (const listener of listeners) listener(message)\n }\n\n const close = (): void => {\n if (state === 'closed') return\n state = 'closed'\n stopRetry()\n options.view.removeEventListener('message', receive)\n listeners.clear()\n }\n\n options.view.addEventListener('message', receive)\n reconnect()\n\n return {\n close,\n reconnect,\n send: (payload: Exclude<StudioPreviewPayload, { readonly type: 'studio:connect' }>) =>\n state === 'ready' && post(payload),\n state: () => state,\n subscribe: (listener: (message: PreviewSitePayload) => void) => {\n listeners.add(listener)\n\n return () => listeners.delete(listener)\n },\n }\n}\n\nfunction createPreviewSiteBridge(options: PreviewSiteBridgeOptions) {\n const listeners = new Set<(message: StudioPreviewPayload) => void>()\n let active = false\n let closed = false\n\n const post = (payload: PreviewSitePayload): boolean => {\n if (!active || closed) return false\n options.parent.postMessage(envelope(options.sessionId, payload), options.parentOrigin)\n\n return true\n }\n\n const receive = (event: PreviewMessageEvent): void => {\n if (closed) return\n const message = receivePreviewMessage(event, options.parent, options.parentOrigin, options.sessionId)\n\n if (!message || !isStudioPreviewMessage(message)) return\n\n if (message.type === 'studio:connect') {\n active = true\n post(options.route)\n }\n\n for (const listener of listeners) listener(message)\n }\n\n const close = (): void => {\n if (closed) return\n closed = true\n active = false\n options.view.removeEventListener('message', receive)\n listeners.clear()\n }\n\n options.view.addEventListener('message', receive)\n\n return {\n close,\n send: post,\n subscribe: (listener: (message: StudioPreviewPayload) => void) => {\n listeners.add(listener)\n\n return () => listeners.delete(listener)\n },\n }\n}\n\nfunction envelope(\n sessionId: PreviewSessionId,\n payload: PreviewSitePayload | StudioPreviewPayload,\n): DocumentPreviewMessage {\n return Result.getOrThrow(\n Schema.decodeResult(Schema.toType(DocumentPreviewMessageSchema))({\n channel: DOCUMENT_PREVIEW_CHANNEL,\n sessionId,\n ...payload,\n }),\n )\n}\n\nfunction receivePreviewMessage(\n event: PreviewMessageEvent,\n source: PreviewMessageTarget,\n origin: PreviewOrigin,\n sessionId: PreviewSessionId,\n): DocumentPreviewMessage | undefined {\n if (event.origin !== origin || event.source !== source || !isDocumentPreviewMessage(event.data)) return undefined\n\n return event.data.sessionId === sessionId ? event.data : undefined\n}\n\nfunction isPreviewSiteMessage(\n message: DocumentPreviewMessage,\n): message is Extract<DocumentPreviewMessage, { readonly type: `preview:${string}` }> {\n return message.type.startsWith('preview:')\n}\n\nfunction isStudioPreviewMessage(\n message: DocumentPreviewMessage,\n): message is Extract<DocumentPreviewMessage, { readonly type: `studio:${string}` }> {\n return message.type.startsWith('studio:')\n}\n","import type { PublicPath } from '@internal/protocol/Documents'\nimport type { PreviewOrigin, PreviewSessionId, PreviewSiteBridge } from '@internal/protocol/Preview'\nimport { makePreviewSiteBridge } from '@internal/protocol/Preview'\nimport { Effect, Exit, Scope } from 'effect'\n\nexport interface SitePreviewConnection extends PreviewSiteBridge {\n readonly close: () => Promise<void>\n}\n\n// oxlint-disable-next-line effecttsgo/async-function -- This browser adapter exposes the Effect-managed preview bridge through a Promise API consumed by Nuxt.\nexport async function openSitePreviewConnection(options: {\n readonly editorOrigin: PreviewOrigin\n readonly route: PublicPath\n readonly sessionId: PreviewSessionId\n}): Promise<SitePreviewConnection> {\n const scope = await Effect.runPromise(Scope.make())\n\n try {\n const bridge = await Effect.runPromise(\n makePreviewSiteBridge({\n parent: window.parent,\n parentOrigin: options.editorOrigin,\n route: { path: options.route, type: 'preview:ready' },\n sessionId: options.sessionId,\n view: window,\n }).pipe(Effect.provideService(Scope.Scope, scope)),\n )\n\n let closed = false\n\n return {\n // oxlint-disable-next-line effecttsgo/async-function -- Closing adapts Effect scope finalization to the connection's Promise contract.\n async close() {\n if (closed) return\n closed = true\n await Effect.runPromise(Scope.close(scope, Exit.void))\n },\n send: bridge.send,\n subscribe: bridge.subscribe,\n }\n } catch (cause) {\n await Effect.runPromise(Scope.close(scope, Exit.fail(cause)))\n throw cause\n }\n}\n"],"mappings":";;;;AAuEA,SAAgB,sBACd,SACsD;CACtD,OAAO,OAAO,eACZ,OAAO,WAAW,wBAAwB,OAAO,CAAC,IACjD,WAAW,OAAO,WAAW,OAAO,MAAM,CAAC,CAC9C,CAAC,CAAC,KAAK,OAAO,KAAK,EAAE,OAAO,QAAQ,GAAG,aAAa,MAAM,CAAC;AAC7D;AA4EA,SAAS,wBAAwB,SAAmC;CAClE,MAAM,4BAAY,IAAI,IAA6C;CACnE,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,MAAM,QAAQ,YAAyC;EACrD,IAAI,CAAC,UAAU,QAAQ,OAAO;EAC9B,QAAQ,OAAO,YAAY,SAAS,QAAQ,WAAW,OAAO,GAAG,QAAQ,YAAY;EAErF,OAAO;CACT;CAEA,MAAM,WAAW,UAAqC;EACpD,IAAI,QAAQ;EACZ,MAAM,UAAU,sBAAsB,OAAO,QAAQ,QAAQ,QAAQ,cAAc,QAAQ,SAAS;EAEpG,IAAI,CAAC,WAAW,CAAC,uBAAuB,OAAO,GAAG;EAElD,IAAI,QAAQ,SAAS,kBAAkB;GACrC,SAAS;GACT,KAAK,QAAQ,KAAK;EACpB;EAEA,KAAK,MAAM,YAAY,WAAW,SAAS,OAAO;CACpD;CAEA,MAAM,cAAoB;EACxB,IAAI,QAAQ;EACZ,SAAS;EACT,SAAS;EACT,QAAQ,KAAK,oBAAoB,WAAW,OAAO;EACnD,UAAU,MAAM;CAClB;CAEA,QAAQ,KAAK,iBAAiB,WAAW,OAAO;CAEhD,OAAO;EACL;EACA,MAAM;EACN,YAAY,aAAsD;GAChE,UAAU,IAAI,QAAQ;GAEtB,aAAa,UAAU,OAAO,QAAQ;EACxC;CACF;AACF;AAEA,SAAS,SACP,WACA,SACwB;CACxB,OAAO,OAAO,WACZ,OAAO,aAAa,OAAO,OAAOA,sBAA4B,CAAC,CAAC,CAAC;EAC/D,SAAS;EACT;EACA,GAAG;CACL,CAAC,CACH;AACF;AAEA,SAAS,sBACP,OACA,QACA,QACA,WACoC;CACpC,IAAI,MAAM,WAAW,UAAU,MAAM,WAAW,UAAU,CAAC,yBAAyB,MAAM,IAAI,GAAG,OAAO;CAExG,OAAO,MAAM,KAAK,cAAc,YAAY,MAAM,OAAO;AAC3D;AAQA,SAAS,uBACP,SACmF;CACnF,OAAO,QAAQ,KAAK,WAAW,SAAS;AAC1C;;;;ACjOA,eAAsB,0BAA0B,SAIb;CACjC,MAAM,QAAQ,MAAM,OAAO,WAAW,MAAM,KAAK,CAAC;CAElD,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAC1B,sBAAsB;GACpB,QAAQ,OAAO;GACf,cAAc,QAAQ;GACtB,OAAO;IAAE,MAAM,QAAQ;IAAO,MAAM;GAAgB;GACpD,WAAW,QAAQ;GACnB,MAAM;EACR,CAAC,CAAC,CAAC,KAAK,OAAO,eAAe,MAAM,OAAO,KAAK,CAAC,CACnD;EAEA,IAAI,SAAS;EAEb,OAAO;GAEL,MAAM,QAAQ;IACZ,IAAI,QAAQ;IACZ,SAAS;IACT,MAAM,OAAO,WAAW,MAAM,MAAM,OAAO,KAAK,IAAI,CAAC;GACvD;GACA,MAAM,OAAO;GACb,WAAW,OAAO;EACpB;CACF,SAAS,OAAO;EACd,MAAM,OAAO,WAAW,MAAM,MAAM,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC;EAC5D,MAAM;CACR;AACF"}
@@ -1,16 +1,17 @@
1
1
  import { l as CMS_READINESS_PATH, p as STUDIO_COLLABORATION_PATH, u as STUDIO_API_PATH } from "./HttpPaths-c9nLZ9H2.js";
2
2
  import { createCmsContentClient, readCmsContentDirectory } from "./Content.js";
3
- import { t as AuthoringCatalog } from "./Configuration-CjVUBBwa.js";
3
+ import { t as AuthoringCatalog } from "./Configuration-BU-UyGoH.js";
4
4
  import { loadCmsConfiguration } from "./internal/cms-config.js";
5
- import { compileCmsConfiguration, resolveCmsInstallation } from "./internal/configuration.js";
6
- import { a as embeddedMigrations, i as createCmsBetterAuth, n as scopedDatabaseUrl, t as installCms } from "./installation-DsGst1BH.js";
7
- import { Effect, Layer, Result, Schema } from "effect";
5
+ import { cmsRuntimeConfigurationDefaults, cmsRuntimeConfigurationFromEnvironment, compileCmsConfiguration, resolveCmsInstallation } from "./internal/configuration.js";
6
+ import { a as embeddedMigrations, i as createCmsBetterAuth, n as scopedDatabaseUrl, t as installCms } from "./installation-fZtNxtoW.js";
7
+ import { Effect, Layer, References, Result, Schema } from "effect";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { resolve } from "node:path";
11
- import { loadNuxt } from "@nuxt/kit";
11
+ import { loadNuxt, loadNuxtConfig } from "@nuxt/kit";
12
12
  import { NodeCrypto, NodeFileSystem, NodePath } from "@effect/platform-node-shared";
13
13
  import { DeleteObjectCommand, HeadBucketCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
14
+ import { setupDotenv } from "c12";
14
15
  import { Pool } from "pg";
15
16
  import { existsSync } from "node:fs";
16
17
 
@@ -88,12 +89,22 @@ const reconcileLocalAdministrator = Effect.fn("LocalAdministrator.reconcile")(fu
88
89
  const nodeLayer = Layer.merge(NodeCrypto.layer, Layer.merge(NodeFileSystem.layer, NodePath.layer));
89
90
  const InitializationStatus = Schema.Struct({ needsAdministrator: Schema.optionalKey(Schema.Boolean) });
90
91
  const CauseCarrier = Schema.Struct({ cause: Schema.Unknown });
91
- async function doctorCmsDevelopment(rootDir, full = false) {
92
+ var CmsLifecycleFailed = class extends Schema.TaggedError()("CmsLifecycleFailed", {
93
+ cause: Schema.Defect(),
94
+ message: Schema.String,
95
+ phase: Schema.String
96
+ }) {};
97
+ const isCmsLifecycleFailed = Schema.is(CmsLifecycleFailed);
98
+ async function doctorCmsDevelopment(rootDir, full = false, envFile) {
92
99
  const checks = [];
93
100
  let configuration;
94
101
  try {
95
- configuration = (await loadConfiguration(rootDir)).configuration;
96
- compileCmsConfiguration(configuration);
102
+ const project = await loadConfiguration(rootDir, envFile);
103
+ compileCmsConfiguration(project.authored.configuration);
104
+ configuration = resolveCmsInstallation(project.authored.configuration, project.runtime, {
105
+ revision: "doctor",
106
+ components: []
107
+ });
97
108
  checks.push(pass("configuration", "Configuration loaded"));
98
109
  } catch (cause) {
99
110
  checks.push(fail("configuration", "Configuration loaded", message(cause)));
@@ -123,16 +134,17 @@ async function doctorCmsDevelopment(rootDir, full = false) {
123
134
  } finally {
124
135
  await pool.end();
125
136
  }
126
- if (configuration.media === false || configuration.media === void 0) checks.push(pass("s3", "Media storage disabled"));
137
+ if (configuration.media === void 0) checks.push(pass("s3", "Media storage disabled"));
127
138
  else {
128
- const storage = configuration.media.storage;
139
+ const storage = configuration.media;
129
140
  const client = s3Client(storage);
130
141
  let publicProbeKey;
131
142
  try {
132
143
  await client.send(new HeadBucketCommand({ Bucket: storage.bucket }));
133
144
  checks.push(pass("s3", "S3 bucket reachable"));
134
145
  if (full) {
135
- const key = `${storage.prefix}.doctor/${randomUUID()}`;
146
+ const relativeKey = `.doctor/${randomUUID()}`;
147
+ const key = `${storage.prefix}${relativeKey}`;
136
148
  await client.send(new PutObjectCommand({
137
149
  Bucket: storage.bucket,
138
150
  Key: key,
@@ -147,7 +159,7 @@ async function doctorCmsDevelopment(rootDir, full = false) {
147
159
  try {
148
160
  if (full && !publicProbeKey) checks.push(notVerified("media-url", "Public media URL reachable", "The transient S3 upload failed."));
149
161
  else {
150
- const url = publicProbeKey ? publicObjectUrl(configuration.media.publicBaseUrl, publicProbeKey) : new URL(configuration.media.publicBaseUrl);
162
+ const url = publicProbeKey ? publicObjectUrl(storage.publicBaseUrl, publicProbeKey.slice(storage.prefix.length)) : new URL(storage.publicBaseUrl);
151
163
  const response = await fetch(url, { method: "HEAD" });
152
164
  checks.push(response.ok || !full && response.status === 404 ? pass("media-url", "Public media URL reachable") : fail("media-url", "Public media URL reachable", `${url.href} returned HTTP ${response.status}.`));
153
165
  }
@@ -187,36 +199,45 @@ async function doctorCmsDevelopment(rootDir, full = false) {
187
199
  }
188
200
  async function bootstrapCmsDevelopment(options) {
189
201
  if (!options.email || !options.password) throw new Error("CMS_EMAIL and CMS_PASSWORD are required for bootstrap.");
190
- const { configuration } = await loadConfiguration(options.rootDir);
191
- const authoring = await prepareAuthoring(options.rootDir);
192
- const resolved = resolveCmsInstallation(configuration, authoring);
193
- const installation = await Effect.runPromise(installCms(resolved));
194
- const localFetch = (input, init) => installation.fetch(input instanceof Request ? input : new Request(input, init));
202
+ const project = await inPhase("bootstrap.configuration", () => loadConfiguration(options.rootDir, options.envFile));
203
+ const authoring = await inPhase("bootstrap.authoring", () => prepareAuthoring(options.rootDir, options.verbose));
204
+ const resolved = inPhaseSync("bootstrap.configuration", () => resolveCmsInstallation(project.authored.configuration, project.runtime, authoring));
205
+ const installation = await inPhase("bootstrap.installation", () => Effect.runPromise(installCms(resolved).pipe(Effect.provideService(References.MinimumLogLevel, options.verbose ? "Info" : "None"))));
206
+ const localFetch = (input, init) => {
207
+ const request = input instanceof Request ? new Request(input, init) : new Request(input, init);
208
+ const headers = new Headers(request.headers);
209
+ headers.set("x-forwarded-for", "127.0.0.1");
210
+ return installation.fetch(new Request(request, { headers }));
211
+ };
195
212
  try {
196
- const status = await localFetch(new URL(`${STUDIO_API_PATH}/initialization`, configuration.baseUrl));
197
- const state = Result.getOrThrow(Schema.decodeUnknownResult(InitializationStatus)(await status.json()));
213
+ const status = await inPhase("bootstrap.initialization", () => localFetch(new URL(`${STUDIO_API_PATH}/initialization`, resolved.baseUrl)));
214
+ const state = await inPhase("bootstrap.initialization", async () => Result.getOrThrow(Schema.decodeUnknownResult(InitializationStatus)(await status.json())));
198
215
  let initialized = false;
199
216
  if (state.needsAdministrator) {
200
- const claimed = await localFetch(new URL(`${STUDIO_API_PATH}/initialization`, configuration.baseUrl), {
217
+ const claimed = await inPhase("bootstrap.initialization", () => localFetch(new URL(`${STUDIO_API_PATH}/initialization`, resolved.baseUrl), {
201
218
  method: "POST",
202
219
  headers: { "content-type": "application/json" },
203
220
  body: JSON.stringify({
204
221
  email: options.email,
205
- initializationToken: configuration.initializationToken,
222
+ initializationToken: resolved.initializationToken,
206
223
  name: options.name,
207
224
  password: options.password
208
225
  })
226
+ }));
227
+ if (!claimed.ok) throw new CmsLifecycleFailed({
228
+ cause: /* @__PURE__ */ new Error(`Administrator initialization failed (${claimed.status}).`),
229
+ message: `Administrator initialization failed (${claimed.status}).`,
230
+ phase: "bootstrap.initialization"
209
231
  });
210
- if (!claimed.ok) throw new Error(`Administrator initialization failed (${claimed.status}).`);
211
232
  initialized = true;
212
233
  }
213
- const administrator = initialized ? "created" : await reconcileAdministrator(configuration, {
234
+ const administrator = initialized ? "created" : await inPhase("bootstrap.administrator", () => reconcileAdministrator(resolved, {
214
235
  email: options.email,
215
236
  name: options.name,
216
237
  password: options.password
217
- });
238
+ }));
218
239
  const content = createCmsContentClient({
219
- baseUrl: configuration.baseUrl,
240
+ baseUrl: resolved.baseUrl,
220
241
  credentials: {
221
242
  email: options.email,
222
243
  password: options.password
@@ -224,19 +245,27 @@ async function bootstrapCmsDevelopment(options) {
224
245
  fetch: localFetch
225
246
  });
226
247
  if (!options.content) {
227
- await content.diff({ sources: [] });
248
+ await inPhase("bootstrap.content", () => content.diff({ sources: [] }));
228
249
  return {
229
250
  administrator,
230
251
  contentApplied: false
231
252
  };
232
253
  }
233
- const source = await readCmsContentDirectory(options.content);
234
- const plan = await content.plan(source, {
254
+ const contentDirectory = options.content;
255
+ const source = await inPhase("bootstrap.content", () => readCmsContentDirectory(contentDirectory));
256
+ const plan = await inPhase("bootstrap.content", () => content.plan(source, {
235
257
  overwrite: true,
236
258
  publish: options.publish
237
- });
238
- if (!plan.valid) throw new Error("Bootstrap content has conflicts or validation errors.");
239
- await content.apply(plan);
259
+ }));
260
+ if (!plan.valid) {
261
+ const details = [...plan.conflicts.map((entry) => `${entry.sourcePath}: conflicts with the current CMS content`), ...plan.issues.map((issue) => `${issue.sourcePath ?? issue.key ?? "content"}${issue.field ? ` (${issue.field})` : ""}: ${issue.message}`)];
262
+ throw new CmsLifecycleFailed({
263
+ cause: /* @__PURE__ */ new Error(`Bootstrap content has conflicts or validation errors. ${details.join(" ")}`),
264
+ message: `Bootstrap content has conflicts or validation errors. ${details.join(" ")}`,
265
+ phase: "bootstrap.content"
266
+ });
267
+ }
268
+ await inPhase("bootstrap.content", () => content.apply(plan));
240
269
  return {
241
270
  administrator,
242
271
  contentApplied: true
@@ -261,18 +290,25 @@ async function reconcileAdministrator(configuration, administrator) {
261
290
  }
262
291
  }
263
292
  async function resetCmsDevelopment(options) {
264
- const { configuration } = await loadConfiguration(options.rootDir);
265
- const targets = [...options.database ? [`PostgreSQL schema ${configuration.database.schema}`] : [], ...options.media && configuration.media !== false && configuration.media !== void 0 ? [`S3 prefix ${configuration.media.storage.bucket}/${configuration.media.storage.prefix}`] : []];
293
+ const project = await inPhase("reset.configuration", () => loadConfiguration(options.rootDir, options.envFile));
294
+ const configuration = inPhaseSync("reset.configuration", () => resolveCmsInstallation(project.authored.configuration, project.runtime, {
295
+ revision: "reset",
296
+ components: []
297
+ }));
298
+ const targets = [...options.database ? [`PostgreSQL schema ${configuration.database.schema}`] : [], ...options.media && configuration.media !== void 0 ? [`S3 prefix ${configuration.media.bucket}/${configuration.media.prefix}`] : []];
266
299
  if (!options.yes) return {
267
300
  executed: false,
268
301
  targets,
269
302
  bootstrapped: false
270
303
  };
271
- await refuseRunningInstallation(configuration.baseUrl);
272
- if (options.database) await resetDatabase(configuration.database);
273
- if (options.media && configuration.media !== false && configuration.media !== void 0) await resetMedia(configuration.media.storage);
304
+ await inPhase("reset.safety", () => refuseRunningInstallation(configuration.baseUrl));
305
+ if (options.database) await inPhase("reset.database", () => resetDatabase(configuration.database));
306
+ const media = configuration.media;
307
+ if (options.media && media !== void 0) await inPhase("reset.media", () => resetMedia(media));
274
308
  if (options.bootstrap) await bootstrapCmsDevelopment({
275
309
  rootDir: options.rootDir,
310
+ envFile: options.envFile,
311
+ verbose: options.verbose,
276
312
  ...options.bootstrap
277
313
  });
278
314
  return {
@@ -281,14 +317,38 @@ async function resetCmsDevelopment(options) {
281
317
  bootstrapped: options.bootstrap !== void 0
282
318
  };
283
319
  }
284
- async function loadConfiguration(rootDir) {
285
- return Effect.runPromise(loadCmsConfiguration(resolve(rootDir)).pipe(Effect.provide(nodeLayer)));
320
+ async function loadCmsProjectEnvironment(rootDir, envFile) {
321
+ await setupDotenv({
322
+ cwd: resolve(rootDir),
323
+ fileName: envFile ? [".env", envFile] : ".env"
324
+ });
286
325
  }
287
- async function prepareAuthoring(rootDir) {
326
+ async function loadConfiguration(rootDir, envFile) {
327
+ const absoluteRoot = resolve(rootDir);
328
+ await loadCmsProjectEnvironment(absoluteRoot, envFile);
329
+ const authored = await Effect.runPromise(loadCmsConfiguration(absoluteRoot).pipe(Effect.provide(nodeLayer)));
330
+ const nuxt = await loadNuxtConfig({
331
+ cwd: absoluteRoot,
332
+ dotenv: false
333
+ });
334
+ const defaults = cmsRuntimeConfigurationDefaults(authored.configuration, nuxt.runtimeConfig.cms);
335
+ return {
336
+ authored,
337
+ runtime: await Effect.runPromise(cmsRuntimeConfigurationFromEnvironment(defaults))
338
+ };
339
+ }
340
+ async function prepareAuthoring(rootDir, verbose = false) {
288
341
  const nuxt = await loadNuxt({
289
342
  cwd: rootDir,
290
343
  dev: true,
291
- ready: true
344
+ ready: true,
345
+ overrides: {
346
+ logLevel: verbose ? "verbose" : "silent",
347
+ ...verbose ? void 0 : {
348
+ devtools: { enabled: false },
349
+ seo: { metaDataFiles: false }
350
+ }
351
+ }
292
352
  });
293
353
  try {
294
354
  const source = (await readFile(resolve(nuxt.options.buildDir, "cms/authoring.mjs"), "utf8")).replace(/^export default /u, "").trim();
@@ -392,6 +452,7 @@ function pass(id, label) {
392
452
  return {
393
453
  id,
394
454
  label,
455
+ phase: `doctor.${id}`,
395
456
  status: "pass"
396
457
  };
397
458
  }
@@ -399,6 +460,7 @@ function fail(id, label, detail) {
399
460
  return {
400
461
  id,
401
462
  label,
463
+ phase: `doctor.${id}`,
402
464
  status: "fail",
403
465
  detail
404
466
  };
@@ -407,6 +469,7 @@ function notVerified(id, label, detail) {
407
469
  return {
408
470
  id,
409
471
  label,
472
+ phase: `doctor.${id}`,
410
473
  status: "not-verified",
411
474
  detail
412
475
  };
@@ -422,7 +485,31 @@ function message(cause) {
422
485
  if (Schema.is(CauseCarrier)(cause)) return message(cause.cause);
423
486
  return String(cause);
424
487
  }
488
+ async function inPhase(phase, operation) {
489
+ try {
490
+ return await operation();
491
+ } catch (cause) {
492
+ if (isCmsLifecycleFailed(cause)) throw cause;
493
+ throw new CmsLifecycleFailed({
494
+ cause,
495
+ message: message(cause),
496
+ phase
497
+ });
498
+ }
499
+ }
500
+ function inPhaseSync(phase, operation) {
501
+ try {
502
+ return operation();
503
+ } catch (cause) {
504
+ if (isCmsLifecycleFailed(cause)) throw cause;
505
+ throw new CmsLifecycleFailed({
506
+ cause,
507
+ message: message(cause),
508
+ phase
509
+ });
510
+ }
511
+ }
425
512
 
426
513
  //#endregion
427
- export { doctorCmsDevelopment as n, resetCmsDevelopment as r, bootstrapCmsDevelopment as t };
428
- //# sourceMappingURL=dev-lifecycle-vQLxwCFq.js.map
514
+ export { resetCmsDevelopment as i, doctorCmsDevelopment as n, loadCmsProjectEnvironment as r, bootstrapCmsDevelopment as t };
515
+ //# sourceMappingURL=dev-lifecycle-D3f9-upa.js.map