@graftland/cli 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ English |
8
8
  The `graft` command: `start`, `create`, `add` and `remove`.
9
9
 
10
10
  ```sh
11
- npm install @graftland/cli effect
11
+ npm install @graftland/cli @graftland/graft @graftland/loader effect
12
12
  ```
13
13
 
14
14
  Create a project with `npm create @graftland@latest my-app`, which runs the same
package/README.zh-CN.md CHANGED
@@ -7,7 +7,7 @@
7
7
  `graft` 命令:`start`、`create`、`add` 和 `remove`。
8
8
 
9
9
  ```sh
10
- npm install @graftland/cli effect
10
+ npm install @graftland/cli @graftland/graft @graftland/loader effect
11
11
  ```
12
12
 
13
13
  用 `npm create @graftland@latest my-app` 创建项目,它运行的就是同一个 `create`
package/README.zh-TW.md CHANGED
@@ -7,7 +7,7 @@
7
7
  `graft` 命令:`start`、`create`、`add` 和 `remove`。
8
8
 
9
9
  ```sh
10
- npm install @graftland/cli effect
10
+ npm install @graftland/cli @graftland/graft @graftland/loader effect
11
11
  ```
12
12
 
13
13
  用 `npm create @graftland@latest my-app` 建立專案,它執行的是同一個 `create`
package/dist/Manage.js CHANGED
@@ -105,11 +105,18 @@ const addPlugin = Effect.fn("Manage.addPlugin")(function* (name, global = false)
105
105
  const path = yield* Path.Path;
106
106
  const fs = yield* FileSystem.FileSystem;
107
107
  const directory = global ? yield* home : path.resolve(".");
108
- const packageName = Create.pluginPackage(name);
108
+ if (global) yield* prepareHome(directory);
109
+ const userAgent = yield* Config.String("npm_config_user_agent").pipe(Config.withDefault(""));
110
+ const packageName = yield* Create.pluginPackage(name, {
111
+ directory,
112
+ userAgent
113
+ }).pipe(Effect.mapError((error) => error._tag === "PluginNotFound" || error._tag === "AmbiguousPluginName" ? CliError.UserError.make({
114
+ cause: error,
115
+ userMessage: error.message
116
+ }) : error));
109
117
  const module = `${packageName}/cli`;
110
118
  const file = path.join(directory, "graft.yml");
111
119
  const text = yield* enabledConfig(file, module);
112
- if (global) yield* prepareHome(directory);
113
120
  yield* install(directory, Record.singleton(packageName, "latest"));
114
121
  yield* fs.writeFileString(file, text).pipe(Effect.uninterruptible);
115
122
  yield* Effect.logInfo(`Enabled ${module} in ${file}`);
@@ -1 +1 @@
1
- {"version":3,"file":"Manage.js","names":[],"sources":["../src/Manage.ts"],"sourcesContent":["/**\n * Changing a project's dependencies.\n */\nimport {\n\tArray,\n\tConfig,\n\tEffect,\n\tFileSystem,\n\tOption,\n\tPath,\n\tPredicate,\n\tRecord,\n\tSchema,\n} from \"effect\"\nimport { CliError } from \"effect/cli\"\n\nimport * as Create from \"@graftland/create\"\nimport * as PackageManager from \"@graftland/package-manager\"\n\nimport { parseDocument, type Document } from \"yaml\"\n\nimport { CommandConfigError } from \"./Commands\"\nimport { home } from \"./internal/commandConfig\"\nimport { schemaFileName } from \"./internal/schemaFileName\"\n\nconst report = (result: PackageManager.InstallResult) =>\n\tEffect.logInfo(\n\t\t`${result.agent}: ${result.added.length} added, ${result.updated.length} updated, ${result.removed.length} removed`,\n\t).pipe(\n\t\tEffect.andThen(\n\t\t\tOption.match(result.snapshotCleanupFailure, {\n\t\t\t\tonNone: () => Effect.void,\n\t\t\t\tonSome: ({ cause, snapshotDirectory }) =>\n\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\"Installation committed; snapshot cleanup failed. Retry removing the retained snapshot.\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t\t{ snapshotDirectory },\n\t\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t)\n\n/**\n * `name@range`, split at the last `@` so a scope's `@` stays in the name. A\n * bare name means the latest version.\n */\nconst Spec = Schema.Union([\n\tSchema.TemplateLiteralParser([\n\t\tPackageManager.PackageName,\n\t\t\"@\",\n\t\tSchema.String,\n\t]),\n\tPackageManager.PackageName,\n])\nconst decodeSpecs = Schema.decodeEffect(Schema.Array(Spec))\n\n/**\n * Regenerates the project's editor schema, but only when it has one, so that\n * the plugins just added or removed are checked. Imports no plugin.\n */\nconst refreshSchema = Effect.fnUntraced(function* (directory: string) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst root = path.resolve(directory)\n\tconst file = path.join(root, schemaFileName)\n\tif (!(yield* fs.exists(file))) return\n\tconst EditorSchema = yield* Effect.promise(\n\t\t() => import(\"./internal/schemaFile\"),\n\t).pipe(Effect.uninterruptible)\n\tyield* EditorSchema.write(root, file).pipe(\n\t\tEffect.mapError((cause) =>\n\t\t\tCliError.UserError.make({\n\t\t\t\tcause,\n\t\t\t\tuserMessage: `Dependencies changed, but ${file} was not regenerated: ${cause.message}`,\n\t\t\t}),\n\t\t),\n\t)\n})\n\nconst install = Effect.fn(\"Manage.install\")(function* (\n\tdirectory: string,\n\tchanges: Readonly<Record<string, string | null>>,\n) {\n\tconst decoded = yield* Schema.decodeEffect(PackageManager.Changes)(changes)\n\t// Set by npm, pnpm and Yarn when they run graft, such as through `pnpm dlx`.\n\tconst userAgent = yield* Config.String(\"npm_config_user_agent\").pipe(\n\t\tConfig.withDefault(\"\"),\n\t)\n\tconst manager = yield* PackageManager.make({ directory, userAgent })\n\tconst result = yield* manager.install(decoded)\n\tyield* report(result)\n\tyield* refreshSchema(directory)\n})\n\nexport const add = Effect.fn(\"Manage.add\")(function* (\n\tspecs: ReadonlyArray<string>,\n) {\n\tconst decoded = yield* decodeSpecs(specs)\n\tyield* install(\n\t\t\".\",\n\t\tRecord.fromEntries(\n\t\t\tArray.map(decoded, (spec) =>\n\t\t\t\tPredicate.isString(spec)\n\t\t\t\t\t? ([spec, \"latest\"] as const)\n\t\t\t\t\t: ([spec[0], spec[2]] as const),\n\t\t\t),\n\t\t),\n\t)\n})\n\nexport const remove = (names: ReadonlyArray<string>) =>\n\tinstall(\n\t\t\".\",\n\t\tRecord.fromEntries(Array.map(names, (name) => [name, null] as const)),\n\t)\n\nconst enabledConfig = Effect.fnUntraced(function* (\n\tfile: string,\n\tmodule: string,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst text = (yield* fs.exists(file))\n\t\t? yield* fs.readFileString(file)\n\t\t: \"[]\\n\"\n\tconst document = yield* Effect.try({\n\t\ttry: () => {\n\t\t\tconst document: Document = parseDocument(text)\n\t\t\tconst error = document.errors[0]\n\t\t\tif (error !== undefined) throw error\n\t\t\tif (document.toJS() === null) document.contents = document.createNode([])\n\t\t\treturn document\n\t\t},\n\t\tcatch: (cause) => CommandConfigError.make({ file, cause }),\n\t})\n\tconst entries = yield* Schema.decodeUnknownEffect(\n\t\tSchema.Array(Schema.Unknown),\n\t)(document.toJS())\n\tconst existing = Array.findFirstIndex(\n\t\tentries,\n\t\t(entry) =>\n\t\t\tPredicate.hasProperty(entry, \"name\") &&\n\t\t\tentry.name === module &&\n\t\t\tPredicate.hasProperty(entry, \"cli\") &&\n\t\t\tentry.cli === true,\n\t)\n\treturn yield* Effect.try({\n\t\ttry: () => {\n\t\t\tif (Option.isSome(existing))\n\t\t\t\tdocument.setIn([existing.value, \"disabled\"], false)\n\t\t\telse document.add({ name: module, cli: true })\n\t\t\treturn document.toString()\n\t\t},\n\t\tcatch: (cause) => CommandConfigError.make({ file, cause }),\n\t})\n})\nconst prepareHome = Effect.fnUntraced(function* (directory: string) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tyield* fs.makeDirectory(directory, { recursive: true })\n\tconst manifest = path.join(directory, \"package.json\")\n\tif (!(yield* fs.exists(manifest)))\n\t\tyield* fs.writeFileString(manifest, '{\"private\":true,\"type\":\"module\"}\\n')\n}, Effect.uninterruptible)\n\n/** Install a command plugin and explicitly enable its /cli entry. */\nexport const addPlugin = Effect.fn(\"Manage.addPlugin\")(function* (\n\tname: string,\n\tglobal: boolean = false,\n) {\n\tconst path = yield* Path.Path\n\tconst fs = yield* FileSystem.FileSystem\n\tconst directory = global ? yield* home : path.resolve(\".\")\n\tconst packageName = Create.pluginPackage(name)\n\tconst module = `${packageName}/cli`\n\tconst file = path.join(directory, \"graft.yml\")\n\tconst text = yield* enabledConfig(file, module)\n\tif (global) yield* prepareHome(directory)\n\tyield* install(directory, Record.singleton(packageName, \"latest\"))\n\t// Once accepted, the native write must finish before command cleanup can proceed.\n\tyield* fs.writeFileString(file, text).pipe(Effect.uninterruptible)\n\tyield* Effect.logInfo(`Enabled ${module} in ${file}`)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"Manage.js","names":[],"sources":["../src/Manage.ts"],"sourcesContent":["/**\n * Changing a project's dependencies.\n */\nimport {\n\tArray,\n\tConfig,\n\tEffect,\n\tFileSystem,\n\tOption,\n\tPath,\n\tPredicate,\n\tRecord,\n\tSchema,\n} from \"effect\"\nimport { CliError } from \"effect/cli\"\n\nimport * as Create from \"@graftland/create\"\nimport * as PackageManager from \"@graftland/package-manager\"\n\nimport { parseDocument, type Document } from \"yaml\"\n\nimport { CommandConfigError } from \"./Commands\"\nimport { home } from \"./internal/commandConfig\"\nimport { schemaFileName } from \"./internal/schemaFileName\"\n\nconst report = (result: PackageManager.InstallResult) =>\n\tEffect.logInfo(\n\t\t`${result.agent}: ${result.added.length} added, ${result.updated.length} updated, ${result.removed.length} removed`,\n\t).pipe(\n\t\tEffect.andThen(\n\t\t\tOption.match(result.snapshotCleanupFailure, {\n\t\t\t\tonNone: () => Effect.void,\n\t\t\t\tonSome: ({ cause, snapshotDirectory }) =>\n\t\t\t\t\tEffect.logWarning(\n\t\t\t\t\t\t\"Installation committed; snapshot cleanup failed. Retry removing the retained snapshot.\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t\t{ snapshotDirectory },\n\t\t\t\t\t),\n\t\t\t}),\n\t\t),\n\t)\n\n/**\n * `name@range`, split at the last `@` so a scope's `@` stays in the name. A\n * bare name means the latest version.\n */\nconst Spec = Schema.Union([\n\tSchema.TemplateLiteralParser([\n\t\tPackageManager.PackageName,\n\t\t\"@\",\n\t\tSchema.String,\n\t]),\n\tPackageManager.PackageName,\n])\nconst decodeSpecs = Schema.decodeEffect(Schema.Array(Spec))\n\n/**\n * Regenerates the project's editor schema, but only when it has one, so that\n * the plugins just added or removed are checked. Imports no plugin.\n */\nconst refreshSchema = Effect.fnUntraced(function* (directory: string) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst root = path.resolve(directory)\n\tconst file = path.join(root, schemaFileName)\n\tif (!(yield* fs.exists(file))) return\n\tconst EditorSchema = yield* Effect.promise(\n\t\t() => import(\"./internal/schemaFile\"),\n\t).pipe(Effect.uninterruptible)\n\tyield* EditorSchema.write(root, file).pipe(\n\t\tEffect.mapError((cause) =>\n\t\t\tCliError.UserError.make({\n\t\t\t\tcause,\n\t\t\t\tuserMessage: `Dependencies changed, but ${file} was not regenerated: ${cause.message}`,\n\t\t\t}),\n\t\t),\n\t)\n})\n\nconst install = Effect.fn(\"Manage.install\")(function* (\n\tdirectory: string,\n\tchanges: Readonly<Record<string, string | null>>,\n) {\n\tconst decoded = yield* Schema.decodeEffect(PackageManager.Changes)(changes)\n\t// Set by npm, pnpm and Yarn when they run graft, such as through `pnpm dlx`.\n\tconst userAgent = yield* Config.String(\"npm_config_user_agent\").pipe(\n\t\tConfig.withDefault(\"\"),\n\t)\n\tconst manager = yield* PackageManager.make({ directory, userAgent })\n\tconst result = yield* manager.install(decoded)\n\tyield* report(result)\n\tyield* refreshSchema(directory)\n})\n\nexport const add = Effect.fn(\"Manage.add\")(function* (\n\tspecs: ReadonlyArray<string>,\n) {\n\tconst decoded = yield* decodeSpecs(specs)\n\tyield* install(\n\t\t\".\",\n\t\tRecord.fromEntries(\n\t\t\tArray.map(decoded, (spec) =>\n\t\t\t\tPredicate.isString(spec)\n\t\t\t\t\t? ([spec, \"latest\"] as const)\n\t\t\t\t\t: ([spec[0], spec[2]] as const),\n\t\t\t),\n\t\t),\n\t)\n})\n\nexport const remove = (names: ReadonlyArray<string>) =>\n\tinstall(\n\t\t\".\",\n\t\tRecord.fromEntries(Array.map(names, (name) => [name, null] as const)),\n\t)\n\nconst enabledConfig = Effect.fnUntraced(function* (\n\tfile: string,\n\tmodule: string,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst text = (yield* fs.exists(file))\n\t\t? yield* fs.readFileString(file)\n\t\t: \"[]\\n\"\n\tconst document = yield* Effect.try({\n\t\ttry: () => {\n\t\t\tconst document: Document = parseDocument(text)\n\t\t\tconst error = document.errors[0]\n\t\t\tif (error !== undefined) throw error\n\t\t\tif (document.toJS() === null) document.contents = document.createNode([])\n\t\t\treturn document\n\t\t},\n\t\tcatch: (cause) => CommandConfigError.make({ file, cause }),\n\t})\n\tconst entries = yield* Schema.decodeUnknownEffect(\n\t\tSchema.Array(Schema.Unknown),\n\t)(document.toJS())\n\tconst existing = Array.findFirstIndex(\n\t\tentries,\n\t\t(entry) =>\n\t\t\tPredicate.hasProperty(entry, \"name\") &&\n\t\t\tentry.name === module &&\n\t\t\tPredicate.hasProperty(entry, \"cli\") &&\n\t\t\tentry.cli === true,\n\t)\n\treturn yield* Effect.try({\n\t\ttry: () => {\n\t\t\tif (Option.isSome(existing))\n\t\t\t\tdocument.setIn([existing.value, \"disabled\"], false)\n\t\t\telse document.add({ name: module, cli: true })\n\t\t\treturn document.toString()\n\t\t},\n\t\tcatch: (cause) => CommandConfigError.make({ file, cause }),\n\t})\n})\nconst prepareHome = Effect.fnUntraced(function* (directory: string) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tyield* fs.makeDirectory(directory, { recursive: true })\n\tconst manifest = path.join(directory, \"package.json\")\n\tif (!(yield* fs.exists(manifest)))\n\t\tyield* fs.writeFileString(manifest, '{\"private\":true,\"type\":\"module\"}\\n')\n}, Effect.uninterruptible)\n\n/** Install a command plugin and explicitly enable its /cli entry. */\nexport const addPlugin = Effect.fn(\"Manage.addPlugin\")(function* (\n\tname: string,\n\tglobal: boolean = false,\n) {\n\tconst path = yield* Path.Path\n\tconst fs = yield* FileSystem.FileSystem\n\tconst directory = global ? yield* home : path.resolve(\".\")\n\tif (global) yield* prepareHome(directory)\n\tconst userAgent = yield* Config.String(\"npm_config_user_agent\").pipe(\n\t\tConfig.withDefault(\"\"),\n\t)\n\tconst packageName = yield* Create.pluginPackage(name, {\n\t\tdirectory,\n\t\tuserAgent,\n\t}).pipe(\n\t\tEffect.mapError((error) =>\n\t\t\terror._tag === \"PluginNotFound\" || error._tag === \"AmbiguousPluginName\"\n\t\t\t\t? CliError.UserError.make({ cause: error, userMessage: error.message })\n\t\t\t\t: error,\n\t\t),\n\t)\n\tconst module = `${packageName}/cli`\n\tconst file = path.join(directory, \"graft.yml\")\n\tconst text = yield* enabledConfig(file, module)\n\tyield* install(directory, Record.singleton(packageName, \"latest\"))\n\t// Once accepted, the native write must finish before command cleanup can proceed.\n\tyield* fs.writeFileString(file, text).pipe(Effect.uninterruptible)\n\tyield* Effect.logInfo(`Enabled ${module} in ${file}`)\n})\n"],"mappings":"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"}
package/dist/Start.js CHANGED
@@ -61,7 +61,7 @@ const run = (options) => Effect.gen(function* () {
61
61
  const { reload: reloadDir } = yield* EnvDir;
62
62
  const { envFile, envDir } = options;
63
63
  const reloader = Plugin.make({
64
- name: "reloader",
64
+ name: "@graftland/cli/Start/reloader",
65
65
  inject: [
66
66
  Loader.Loader,
67
67
  FileSystem.FileSystem,
package/dist/Start.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Start.js","names":["intercepts","env"],"sources":["../src/Start.ts"],"sourcesContent":["/**\n * Runs a Graft application from its config file.\n */\nimport {\n\tArray,\n\tCause,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tLayer,\n\tLogger,\n\tOption,\n\tPath,\n\tScope,\n\tSemaphore,\n\ttype PlatformError,\n\ttype Schema,\n} from \"effect\"\nimport { NodeServices } from \"@effect/platform-node\"\n\nimport { Graft, Plugin } from \"@graftland/graft\"\nimport * as Hmr from \"@graftland/hmr\"\nimport {\n\tEntry,\n\tInclude,\n\tLoader,\n\tModuleResolver,\n\ttype Tree,\n} from \"@graftland/loader\"\n\nimport { Application, ApplicationError, platform } from \"./Application\"\nimport {\n\tlayer as env,\n\tEnvDir,\n\tEnvFile,\n\twatchEnvDir,\n\twatchEnvFile,\n} from \"./internal/env\"\nimport { layer as intercepts } from \"./internal/intercepts\"\nimport { watchModules } from \"./internal/watch\"\n\nexport interface Options {\n\t/** Path of the config file. */\n\treadonly config: string\n\t/** Reload plugin modules and the config file when they change. */\n\treadonly watch: boolean\n\t/**\n\t * A `.env` file that `!env` reads when the process environment does not set\n\t * a variable. With `watch`, changes to it are read again.\n\t */\n\treadonly envFile?: string | undefined\n\t/**\n\t * A directory of secret files that `!env` reads when neither the process\n\t * environment nor `envFile` sets a variable. Each regular file is one\n\t * variable, named after the file. With `watch`, changes are read again.\n\t */\n\treadonly envDir?: string | undefined\n}\n\n/**\n * Loads the entries of the config file and runs until interrupted. Plugin\n * modules are imported through the hot reloader, so bare specifiers resolve\n * from the application directory whether or not `watch` is on.\n */\nexport const run = (\n\toptions: Options,\n): Effect.Effect<\n\tvoid,\n\t| ApplicationError\n\t| Graft.ServiceConflict\n\t| PlatformError.BadArgument\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\n\t| Loader.StoreError\n\t| Tree.InvalidOperation\n> =>\n\tEffect.gen(function* () {\n\t\tconst scope = yield* Effect.scope\n\t\tconst done = yield* Deferred.make<void, ApplicationError>()\n\t\tconst protocol = yield* Semaphore.make(1)\n\t\tyield* Graft.provide(Application, {\n\t\t\tclaimProtocol: Effect.acquireRelease(\n\t\t\t\tprotocol.takeIfAvailable(1).pipe(\n\t\t\t\t\tEffect.flatMap((acquired) =>\n\t\t\t\t\t\tacquired\n\t\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\t\t\tApplicationError.make({\n\t\t\t\t\t\t\t\t\t\tcause: Cause.fail(\n\t\t\t\t\t\t\t\t\t\t\t\"Only one runner may own the process stdio channel\",\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\t() => protocol.release(1),\n\t\t\t),\n\t\t\tfinish: (exit) =>\n\t\t\t\tDeferred.done(\n\t\t\t\t\tdone,\n\t\t\t\t\tExit.isFailure(exit)\n\t\t\t\t\t\t? Exit.fail(ApplicationError.make({ cause: exit.cause }))\n\t\t\t\t\t\t: Exit.void,\n\t\t\t\t).pipe(Effect.asVoid),\n\t\t\tonExit: (release) => Scope.addFinalizer(scope, release),\n\t\t})\n\t\tconst path = yield* Path.Path\n\t\tconst file = path.resolve(options.config)\n\t\tconst directory = path.dirname(file)\n\t\tconst base = yield* path.toFileUrl(file)\n\t\tconst hmr = yield* Hmr.make({ base: base.href })\n\t\tconst resolver = yield* ModuleResolver.make({\n\t\t\tbase: directory,\n\t\t\timporter: hmr.importerScoped,\n\t\t})\n\t\tconst modules = yield* ModuleResolver.make({\n\t\t\tbase: directory,\n\t\t\timporter: options.watch ? hmr.importModuleScoped : hmr.importModule,\n\t\t})\n\t\tyield* Graft.provide(ModuleResolver.ModuleResolver, {\n\t\t\tresolve: resolver.resolve,\n\t\t\timportModule: modules.importModule,\n\t\t})\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst observe = hmr.observation.pipe(\n\t\t\tEffect.provideService(FileSystem.FileSystem, fs),\n\t\t\tEffect.map((source) => ({\n\t\t\t\t...source,\n\t\t\t\twatch: (files: ReadonlyArray<string>, activation: Scope.Scope) =>\n\t\t\t\t\tsource.watch(files, activation).pipe(\n\t\t\t\t\t\tEffect.mapError((cause) =>\n\t\t\t\t\t\t\tModuleResolver.ModuleInvalid.make({\n\t\t\t\t\t\t\t\tspecifier: Array.join(files, \", \"),\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t})),\n\t\t)\n\t\tconst included = yield* Graft.plugin(Include.Include, {\n\t\t\tpath: file,\n\t\t\twatch: options.watch,\n\t\t}).pipe(\n\t\t\tEffect.provideService(\n\t\t\t\tModuleResolver.Watch,\n\t\t\t\toptions.watch ? Option.some(observe) : Option.none(),\n\t\t\t),\n\t\t)\n\t\tyield* included.await\n\t\tif (options.watch) {\n\t\t\tconst { reload } = yield* EnvFile\n\t\t\tconst { reload: reloadDir } = yield* EnvDir\n\t\t\tconst { envFile, envDir } = options\n\t\t\tconst reloader = Plugin.make({\n\t\t\t\tname: \"reloader\",\n\t\t\t\tinject: [Loader.Loader, FileSystem.FileSystem, Path.Path],\n\t\t\t\tapply: () =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst loader = yield* Loader.Loader\n\t\t\t\t\t\tyield* watchModules(hmr, directory, loader.retry).pipe(\n\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (envFile !== undefined)\n\t\t\t\t\t\t\tyield* watchEnvFile(envFile, reload, loader.retry).pipe(\n\t\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\tif (envDir !== undefined)\n\t\t\t\t\t\t\tyield* watchEnvDir(envDir, reloadDir, loader.retry).pipe(\n\t\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t}),\n\t\t\t})\n\t\t\tyield* Graft.plugin(reloader)\n\t\t}\n\n\t\tyield* Effect.logInfo(\n\t\t\t`Running ${path.relative(process.cwd(), file) || file}`,\n\t\t)\n\t\treturn yield* Deferred.await(done)\n\t}).pipe(\n\t\tEffect.scoped,\n\t\tEffect.provideService(Entry.Select, (entry) => entry[\"cli\"] !== true),\n\t\t// @effect-diagnostics-next-line strictEffectProvide:off -- this runs the whole application, so it is an entry point\n\t\tEffect.provide(\n\t\t\tLayer.provideMerge(\n\t\t\t\tGraft.layer,\n\t\t\t\tLayer.mergeAll(\n\t\t\t\t\tplatform,\n\t\t\t\t\tintercepts,\n\t\t\t\t\tenv({ file: options.envFile, dir: options.envDir }).pipe(\n\t\t\t\t\t\tLayer.provide(NodeServices.layer),\n\t\t\t\t\t),\n\t\t\t\t\tLogger.layer([Logger.consolePretty()]),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t)\n"],"mappings":"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"}
1
+ {"version":3,"file":"Start.js","names":["intercepts","env"],"sources":["../src/Start.ts"],"sourcesContent":["/**\n * Runs a Graft application from its config file.\n */\nimport {\n\tArray,\n\tCause,\n\tDeferred,\n\tEffect,\n\tExit,\n\tFileSystem,\n\tLayer,\n\tLogger,\n\tOption,\n\tPath,\n\tScope,\n\tSemaphore,\n\ttype PlatformError,\n\ttype Schema,\n} from \"effect\"\nimport { NodeServices } from \"@effect/platform-node\"\n\nimport { Graft, Plugin } from \"@graftland/graft\"\nimport * as Hmr from \"@graftland/hmr\"\nimport {\n\tEntry,\n\tInclude,\n\tLoader,\n\tModuleResolver,\n\ttype Tree,\n} from \"@graftland/loader\"\n\nimport { Application, ApplicationError, platform } from \"./Application\"\nimport {\n\tlayer as env,\n\tEnvDir,\n\tEnvFile,\n\twatchEnvDir,\n\twatchEnvFile,\n} from \"./internal/env\"\nimport { layer as intercepts } from \"./internal/intercepts\"\nimport { watchModules } from \"./internal/watch\"\n\nexport interface Options {\n\t/** Path of the config file. */\n\treadonly config: string\n\t/** Reload plugin modules and the config file when they change. */\n\treadonly watch: boolean\n\t/**\n\t * A `.env` file that `!env` reads when the process environment does not set\n\t * a variable. With `watch`, changes to it are read again.\n\t */\n\treadonly envFile?: string | undefined\n\t/**\n\t * A directory of secret files that `!env` reads when neither the process\n\t * environment nor `envFile` sets a variable. Each regular file is one\n\t * variable, named after the file. With `watch`, changes are read again.\n\t */\n\treadonly envDir?: string | undefined\n}\n\n/**\n * Loads the entries of the config file and runs until interrupted. Plugin\n * modules are imported through the hot reloader, so bare specifiers resolve\n * from the application directory whether or not `watch` is on.\n */\nexport const run = (\n\toptions: Options,\n): Effect.Effect<\n\tvoid,\n\t| ApplicationError\n\t| Graft.ServiceConflict\n\t| PlatformError.BadArgument\n\t| PlatformError.PlatformError\n\t| Schema.SchemaError\n\t| Loader.StoreError\n\t| Tree.InvalidOperation\n> =>\n\tEffect.gen(function* () {\n\t\tconst scope = yield* Effect.scope\n\t\tconst done = yield* Deferred.make<void, ApplicationError>()\n\t\tconst protocol = yield* Semaphore.make(1)\n\t\tyield* Graft.provide(Application, {\n\t\t\tclaimProtocol: Effect.acquireRelease(\n\t\t\t\tprotocol.takeIfAvailable(1).pipe(\n\t\t\t\t\tEffect.flatMap((acquired) =>\n\t\t\t\t\t\tacquired\n\t\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t\t: Effect.fail(\n\t\t\t\t\t\t\t\t\tApplicationError.make({\n\t\t\t\t\t\t\t\t\t\tcause: Cause.fail(\n\t\t\t\t\t\t\t\t\t\t\t\"Only one runner may own the process stdio channel\",\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\t() => protocol.release(1),\n\t\t\t),\n\t\t\tfinish: (exit) =>\n\t\t\t\tDeferred.done(\n\t\t\t\t\tdone,\n\t\t\t\t\tExit.isFailure(exit)\n\t\t\t\t\t\t? Exit.fail(ApplicationError.make({ cause: exit.cause }))\n\t\t\t\t\t\t: Exit.void,\n\t\t\t\t).pipe(Effect.asVoid),\n\t\t\tonExit: (release) => Scope.addFinalizer(scope, release),\n\t\t})\n\t\tconst path = yield* Path.Path\n\t\tconst file = path.resolve(options.config)\n\t\tconst directory = path.dirname(file)\n\t\tconst base = yield* path.toFileUrl(file)\n\t\tconst hmr = yield* Hmr.make({ base: base.href })\n\t\tconst resolver = yield* ModuleResolver.make({\n\t\t\tbase: directory,\n\t\t\timporter: hmr.importerScoped,\n\t\t})\n\t\tconst modules = yield* ModuleResolver.make({\n\t\t\tbase: directory,\n\t\t\timporter: options.watch ? hmr.importModuleScoped : hmr.importModule,\n\t\t})\n\t\tyield* Graft.provide(ModuleResolver.ModuleResolver, {\n\t\t\tresolve: resolver.resolve,\n\t\t\timportModule: modules.importModule,\n\t\t})\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst observe = hmr.observation.pipe(\n\t\t\tEffect.provideService(FileSystem.FileSystem, fs),\n\t\t\tEffect.map((source) => ({\n\t\t\t\t...source,\n\t\t\t\twatch: (files: ReadonlyArray<string>, activation: Scope.Scope) =>\n\t\t\t\t\tsource.watch(files, activation).pipe(\n\t\t\t\t\t\tEffect.mapError((cause) =>\n\t\t\t\t\t\t\tModuleResolver.ModuleInvalid.make({\n\t\t\t\t\t\t\t\tspecifier: Array.join(files, \", \"),\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t})),\n\t\t)\n\t\tconst included = yield* Graft.plugin(Include.Include, {\n\t\t\tpath: file,\n\t\t\twatch: options.watch,\n\t\t}).pipe(\n\t\t\tEffect.provideService(\n\t\t\t\tModuleResolver.Watch,\n\t\t\t\toptions.watch ? Option.some(observe) : Option.none(),\n\t\t\t),\n\t\t)\n\t\tyield* included.await\n\t\tif (options.watch) {\n\t\t\tconst { reload } = yield* EnvFile\n\t\t\tconst { reload: reloadDir } = yield* EnvDir\n\t\t\tconst { envFile, envDir } = options\n\t\t\tconst reloader = Plugin.make({\n\t\t\t\tname: \"@graftland/cli/Start/reloader\",\n\t\t\t\tinject: [Loader.Loader, FileSystem.FileSystem, Path.Path],\n\t\t\t\tapply: () =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst loader = yield* Loader.Loader\n\t\t\t\t\t\tyield* watchModules(hmr, directory, loader.retry).pipe(\n\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (envFile !== undefined)\n\t\t\t\t\t\t\tyield* watchEnvFile(envFile, reload, loader.retry).pipe(\n\t\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\tif (envDir !== undefined)\n\t\t\t\t\t\t\tyield* watchEnvDir(envDir, reloadDir, loader.retry).pipe(\n\t\t\t\t\t\t\t\tEffect.forkScoped,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t}),\n\t\t\t})\n\t\t\tyield* Graft.plugin(reloader)\n\t\t}\n\n\t\tyield* Effect.logInfo(\n\t\t\t`Running ${path.relative(process.cwd(), file) || file}`,\n\t\t)\n\t\treturn yield* Deferred.await(done)\n\t}).pipe(\n\t\tEffect.scoped,\n\t\tEffect.provideService(Entry.Select, (entry) => entry[\"cli\"] !== true),\n\t\t// @effect-diagnostics-next-line strictEffectProvide:off -- this runs the whole application, so it is an entry point\n\t\tEffect.provide(\n\t\t\tLayer.provideMerge(\n\t\t\t\tGraft.layer,\n\t\t\t\tLayer.mergeAll(\n\t\t\t\t\tplatform,\n\t\t\t\t\tintercepts,\n\t\t\t\t\tenv({ file: options.envFile, dir: options.envDir }).pipe(\n\t\t\t\t\t\tLayer.provide(NodeServices.layer),\n\t\t\t\t\t),\n\t\t\t\t\tLogger.layer([Logger.consolePretty()]),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t)\n"],"mappings":"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"}
@@ -11,7 +11,12 @@ const moduleExtensions = [
11
11
  ];
12
12
  /** Whether a change to this file should reload plugin modules. */
13
13
  const isModule = (path) => !Array.contains(String.split(/[/\\]/)(path), "node_modules") && Array.some(moduleExtensions, (extension) => String.endsWith(extension)(path));
14
- const describe = (result) => result._tag === "Reloaded" ? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`) : Effect.logWarning(result.reason === "ambiguous-exports" ? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, ", ")}` : result.reason === "identity-conflict" ? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, ", ")}` : `Restart required to pick up ${Array.join(result.modules, ", ")}`);
14
+ /** One warning per failed or timed-out plugin; failures retain their full cause. */
15
+ const rolledBack = (failures) => Effect.forEach(failures, (failure) => (failure._tag === "Failed" ? Effect.logWarning(`Kept the previous code: plugin "${failure.plugin}" failed to start`, failure.cause) : Effect.logWarning(`Kept the previous code: plugin "${failure.plugin}" did not start in time`)).pipe(Effect.annotateLogs({
16
+ "graft.fork": failure.fork,
17
+ "graft.plugin": failure.plugin
18
+ })), { discard: true });
19
+ const describe = (result) => result._tag === "Reloaded" ? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`) : result._tag === "RolledBack" ? rolledBack(result.failures) : Effect.logWarning(result.reason === "ambiguous-exports" ? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, ", ")}` : result.reason === "identity-conflict" ? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, ", ")}` : `Restart required to pick up ${Array.join(result.modules, ", ")}`);
15
20
  /** Owns the HMR stream and retries failed loader entries after each reload. */
16
21
  const watchModules = (hmr, directory, retry) => hmr.watch([directory], {
17
22
  recursive: true,
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","names":[],"sources":["../../src/internal/watch.ts"],"sourcesContent":["import { Array, Cause, Effect, Exit, Stream, String } from \"effect\"\n\nimport type * as Hmr from \"@graftland/hmr\"\n\nimport { observeShutdown } from \"./observeShutdown\"\n\nconst moduleExtensions = [\".js\", \".mjs\", \".cjs\", \".ts\", \".mts\", \".cts\"]\n\n/** Whether a change to this file should reload plugin modules. */\nexport const isModule = (path: string) =>\n\t!Array.contains(String.split(/[/\\\\]/)(path), \"node_modules\") &&\n\tArray.some(moduleExtensions, (extension) => String.endsWith(extension)(path))\n\nconst describe = (result: Hmr.ReloadResult) =>\n\tresult._tag === \"Reloaded\"\n\t\t? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`)\n\t\t: Effect.logWarning(\n\t\t\t\tresult.reason === \"ambiguous-exports\"\n\t\t\t\t\t? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t: result.reason === \"identity-conflict\"\n\t\t\t\t\t\t? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t\t: `Restart required to pick up ${Array.join(result.modules, \", \")}`,\n\t\t\t)\n\n/** Owns the HMR stream and retries failed loader entries after each reload. */\nexport const watchModules = <E, R>(\n\thmr: Pick<Hmr.Hmr, \"watch\">,\n\tdirectory: string,\n\tretry: Effect.Effect<void, E, R>,\n) =>\n\thmr.watch([directory], { recursive: true, filter: isModule }).pipe(\n\t\tStream.runForEach(\n\t\t\tEffect.fnUntraced(\n\t\t\t\tfunction* (result: Hmr.WatchResult) {\n\t\t\t\t\tif (result._tag === \"Failed\")\n\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\tyield* describe(result)\n\t\t\t\t\tyield* retry\n\t\t\t\t},\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(\"Reloading plugin modules failed\", cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(\"Watching plugin modules failed\", cause),\n\t\t),\n\t\t// A backend failure ends the stream. Reinstall it after a delay rather than\n\t\t// leaving the application running without a watcher.\n\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\tEffect.forever,\n\t\t(effect) =>\n\t\t\tobserveShutdown(\n\t\t\t\teffect,\n\t\t\t\t\"Module watcher stopped with interrupted cleanup\",\n\t\t\t),\n\t\t// onExit runs uninterruptibly, after stream cleanup. Observe the entire\n\t\t// worker before forkScoped owns it, including mixed interruption causes.\n\t\tEffect.onExit((exit) =>\n\t\t\tExit.isFailure(exit) &&\n\t\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t\t? Effect.logError(\"Module watcher exited with a failure\", exit.cause)\n\t\t\t\t: Effect.void,\n\t\t),\n\t)\n"],"mappings":";;;AAMA,MAAM,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;;AAGtE,MAAa,YAAY,SACxB,CAAC,MAAM,SAAS,OAAO,MAAM,OAAO,CAAC,CAAC,IAAI,GAAG,cAAc,KAC3D,MAAM,KAAK,mBAAmB,cAAc,OAAO,SAAS,SAAS,CAAC,CAAC,IAAI,CAAC;AAE7E,MAAM,YAAY,WACjB,OAAO,SAAS,aACb,OAAO,QAAQ,YAAY,OAAO,QAAQ,OAAO,kBAAkB,IACnE,OAAO,WACP,OAAO,WAAW,sBACf,uDAAuD,MAAM,KAAK,OAAO,SAAS,IAAI,MACtF,OAAO,WAAW,sBACjB,+DAA+D,MAAM,KAAK,OAAO,SAAS,IAAI,MAC9F,+BAA+B,MAAM,KAAK,OAAO,SAAS,IAAI,GACnE;;AAGH,MAAa,gBACZ,KACA,WACA,UAEA,IAAI,MAAM,CAAC,SAAS,GAAG;CAAE,WAAW;CAAM,QAAQ;AAAS,CAAC,CAAC,CAAC,KAC7D,OAAO,WACN,OAAO,WACN,WAAW,QAAyB;CACnC,IAAI,OAAO,SAAS,UACnB,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;CAC5C,OAAO,SAAS,MAAM;CACtB,OAAO;AACR,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,mCAAmC,KAAK,CACpE,CACD,CACD,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,kCAAkC,KAAK,CACnE,GAGA,OAAO,QAAQ,OAAO,MAAM,UAAU,CAAC,GACvC,OAAO,UACN,WACA,gBACC,QACA,iDACD,GAGD,OAAO,QAAQ,SACd,KAAK,UAAU,IAAI,MAClB,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,KACpD,OAAO,SAAS,wCAAwC,KAAK,KAAK,IAClE,OAAO,IACX,CACD"}
1
+ {"version":3,"file":"watch.js","names":[],"sources":["../../src/internal/watch.ts"],"sourcesContent":["import { Array, Cause, Effect, Exit, Stream, String } from \"effect\"\n\nimport type { Graft } from \"@graftland/graft\"\nimport type * as Hmr from \"@graftland/hmr\"\n\nimport { observeShutdown } from \"./observeShutdown\"\n\nconst moduleExtensions = [\".js\", \".mjs\", \".cjs\", \".ts\", \".mts\", \".cts\"]\n\n/** Whether a change to this file should reload plugin modules. */\nexport const isModule = (path: string) =>\n\t!Array.contains(String.split(/[/\\\\]/)(path), \"node_modules\") &&\n\tArray.some(moduleExtensions, (extension) => String.endsWith(extension)(path))\n\n/** One warning per failed or timed-out plugin; failures retain their full cause. */\nconst rolledBack = (failures: ReadonlyArray<Graft.ReplacementFailure>) =>\n\tEffect.forEach(\n\t\tfailures,\n\t\t(failure) =>\n\t\t\t(failure._tag === \"Failed\"\n\t\t\t\t? Effect.logWarning(\n\t\t\t\t\t\t`Kept the previous code: plugin \"${failure.plugin}\" failed to start`,\n\t\t\t\t\t\tfailure.cause,\n\t\t\t\t\t)\n\t\t\t\t: Effect.logWarning(\n\t\t\t\t\t\t`Kept the previous code: plugin \"${failure.plugin}\" did not start in time`,\n\t\t\t\t\t)\n\t\t\t).pipe(\n\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\"graft.fork\": failure.fork,\n\t\t\t\t\t\"graft.plugin\": failure.plugin,\n\t\t\t\t}),\n\t\t\t),\n\t\t{ discard: true },\n\t)\n\nconst describe = (result: Hmr.ReloadResult) =>\n\tresult._tag === \"Reloaded\"\n\t\t? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`)\n\t\t: result._tag === \"RolledBack\"\n\t\t\t? rolledBack(result.failures)\n\t\t\t: Effect.logWarning(\n\t\t\t\t\tresult.reason === \"ambiguous-exports\"\n\t\t\t\t\t\t? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t\t: result.reason === \"identity-conflict\"\n\t\t\t\t\t\t\t? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, \", \")}`\n\t\t\t\t\t\t\t: `Restart required to pick up ${Array.join(result.modules, \", \")}`,\n\t\t\t\t)\n\n/** Owns the HMR stream and retries failed loader entries after each reload. */\nexport const watchModules = <E, R>(\n\thmr: Pick<Hmr.Hmr, \"watch\">,\n\tdirectory: string,\n\tretry: Effect.Effect<void, E, R>,\n) =>\n\thmr.watch([directory], { recursive: true, filter: isModule }).pipe(\n\t\tStream.runForEach(\n\t\t\tEffect.fnUntraced(\n\t\t\t\tfunction* (result: Hmr.WatchResult) {\n\t\t\t\t\tif (result._tag === \"Failed\")\n\t\t\t\t\t\treturn yield* Effect.failCause(result.cause)\n\t\t\t\t\tyield* describe(result)\n\t\t\t\t\tyield* retry\n\t\t\t\t},\n\t\t\t\tEffect.catchCauseIf(\n\t\t\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t\t\t(cause) => Effect.logError(\"Reloading plugin modules failed\", cause),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tEffect.catchCauseIf(\n\t\t\t(cause) => !Cause.hasInterrupts(cause),\n\t\t\t(cause) => Effect.logError(\"Watching plugin modules failed\", cause),\n\t\t),\n\t\t// A backend failure ends the stream. Reinstall it after a delay rather than\n\t\t// leaving the application running without a watcher.\n\t\tEffect.andThen(Effect.sleep(\"1 second\")),\n\t\tEffect.forever,\n\t\t(effect) =>\n\t\t\tobserveShutdown(\n\t\t\t\teffect,\n\t\t\t\t\"Module watcher stopped with interrupted cleanup\",\n\t\t\t),\n\t\t// onExit runs uninterruptibly, after stream cleanup. Observe the entire\n\t\t// worker before forkScoped owns it, including mixed interruption causes.\n\t\tEffect.onExit((exit) =>\n\t\t\tExit.isFailure(exit) &&\n\t\t\t(Cause.hasFails(exit.cause) || Cause.hasDies(exit.cause))\n\t\t\t\t? Effect.logError(\"Module watcher exited with a failure\", exit.cause)\n\t\t\t\t: Effect.void,\n\t\t),\n\t)\n"],"mappings":";;;AAOA,MAAM,mBAAmB;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM;;AAGtE,MAAa,YAAY,SACxB,CAAC,MAAM,SAAS,OAAO,MAAM,OAAO,CAAC,CAAC,IAAI,GAAG,cAAc,KAC3D,MAAM,KAAK,mBAAmB,cAAc,OAAO,SAAS,SAAS,CAAC,CAAC,IAAI,CAAC;;AAG7E,MAAM,cAAc,aACnB,OAAO,QACN,WACC,aACC,QAAQ,SAAS,WACf,OAAO,WACP,mCAAmC,QAAQ,OAAO,oBAClD,QAAQ,KACT,IACC,OAAO,WACP,mCAAmC,QAAQ,OAAO,wBACnD,EAAA,CACA,KACD,OAAO,aAAa;CACnB,cAAc,QAAQ;CACtB,gBAAgB,QAAQ;AACzB,CAAC,CACF,GACD,EAAE,SAAS,KAAK,CACjB;AAED,MAAM,YAAY,WACjB,OAAO,SAAS,aACb,OAAO,QAAQ,YAAY,OAAO,QAAQ,OAAO,kBAAkB,IACnE,OAAO,SAAS,eACf,WAAW,OAAO,QAAQ,IAC1B,OAAO,WACP,OAAO,WAAW,sBACf,uDAAuD,MAAM,KAAK,OAAO,SAAS,IAAI,MACtF,OAAO,WAAW,sBACjB,+DAA+D,MAAM,KAAK,OAAO,SAAS,IAAI,MAC9F,+BAA+B,MAAM,KAAK,OAAO,SAAS,IAAI,GACnE;;AAGJ,MAAa,gBACZ,KACA,WACA,UAEA,IAAI,MAAM,CAAC,SAAS,GAAG;CAAE,WAAW;CAAM,QAAQ;AAAS,CAAC,CAAC,CAAC,KAC7D,OAAO,WACN,OAAO,WACN,WAAW,QAAyB;CACnC,IAAI,OAAO,SAAS,UACnB,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK;CAC5C,OAAO,SAAS,MAAM;CACtB,OAAO;AACR,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,mCAAmC,KAAK,CACpE,CACD,CACD,GACA,OAAO,cACL,UAAU,CAAC,MAAM,cAAc,KAAK,IACpC,UAAU,OAAO,SAAS,kCAAkC,KAAK,CACnE,GAGA,OAAO,QAAQ,OAAO,MAAM,UAAU,CAAC,GACvC,OAAO,UACN,WACA,gBACC,QACA,iDACD,GAGD,OAAO,QAAQ,SACd,KAAK,UAAU,IAAI,MAClB,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,QAAQ,KAAK,KAAK,KACpD,OAAO,SAAS,wCAAwC,KAAK,KAAK,IAClE,OAAO,IACX,CACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graftland/cli",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.19.0"
@@ -29,19 +29,21 @@
29
29
  "./package.json": "./package.json"
30
30
  },
31
31
  "peerDependencies": {
32
- "effect": "4.1.*"
32
+ "effect": "4.1.*",
33
+ "@graftland/graft": "0.5.0",
34
+ "@graftland/loader": "0.5.0"
33
35
  },
34
36
  "devDependencies": {
35
37
  "@effect/vitest": "4.1.2",
36
38
  "effect": "4.1.2",
37
- "vitest": "5.1.0"
39
+ "vitest": "5.1.0",
40
+ "@graftland/loader": "0.5.0",
41
+ "@graftland/graft": "0.5.0"
38
42
  },
39
43
  "dependencies": {
40
- "@graftland/graft": "0.4.3",
41
- "@graftland/loader": "0.4.3",
42
- "@graftland/hmr": "0.4.3",
43
- "@graftland/create": "0.4.3",
44
- "@graftland/package-manager": "0.4.3",
44
+ "@graftland/hmr": "0.5.0",
45
+ "@graftland/create": "0.5.0",
46
+ "@graftland/package-manager": "0.5.0",
45
47
  "@effect/platform-node": "4.1.2",
46
48
  "yaml": "2.9.1"
47
49
  },
package/src/Manage.ts CHANGED
@@ -170,11 +170,23 @@ export const addPlugin = Effect.fn("Manage.addPlugin")(function* (
170
170
  const path = yield* Path.Path
171
171
  const fs = yield* FileSystem.FileSystem
172
172
  const directory = global ? yield* home : path.resolve(".")
173
- const packageName = Create.pluginPackage(name)
173
+ if (global) yield* prepareHome(directory)
174
+ const userAgent = yield* Config.String("npm_config_user_agent").pipe(
175
+ Config.withDefault(""),
176
+ )
177
+ const packageName = yield* Create.pluginPackage(name, {
178
+ directory,
179
+ userAgent,
180
+ }).pipe(
181
+ Effect.mapError((error) =>
182
+ error._tag === "PluginNotFound" || error._tag === "AmbiguousPluginName"
183
+ ? CliError.UserError.make({ cause: error, userMessage: error.message })
184
+ : error,
185
+ ),
186
+ )
174
187
  const module = `${packageName}/cli`
175
188
  const file = path.join(directory, "graft.yml")
176
189
  const text = yield* enabledConfig(file, module)
177
- if (global) yield* prepareHome(directory)
178
190
  yield* install(directory, Record.singleton(packageName, "latest"))
179
191
  // Once accepted, the native write must finish before command cleanup can proceed.
180
192
  yield* fs.writeFileString(file, text).pipe(Effect.uninterruptible)
package/src/Start.ts CHANGED
@@ -153,7 +153,7 @@ export const run = (
153
153
  const { reload: reloadDir } = yield* EnvDir
154
154
  const { envFile, envDir } = options
155
155
  const reloader = Plugin.make({
156
- name: "reloader",
156
+ name: "@graftland/cli/Start/reloader",
157
157
  inject: [Loader.Loader, FileSystem.FileSystem, Path.Path],
158
158
  apply: () =>
159
159
  Effect.gen(function* () {
@@ -1,5 +1,6 @@
1
1
  import { Array, Cause, Effect, Exit, Stream, String } from "effect"
2
2
 
3
+ import type { Graft } from "@graftland/graft"
3
4
  import type * as Hmr from "@graftland/hmr"
4
5
 
5
6
  import { observeShutdown } from "./observeShutdown"
@@ -11,16 +12,40 @@ export const isModule = (path: string) =>
11
12
  !Array.contains(String.split(/[/\\]/)(path), "node_modules") &&
12
13
  Array.some(moduleExtensions, (extension) => String.endsWith(extension)(path))
13
14
 
15
+ /** One warning per failed or timed-out plugin; failures retain their full cause. */
16
+ const rolledBack = (failures: ReadonlyArray<Graft.ReplacementFailure>) =>
17
+ Effect.forEach(
18
+ failures,
19
+ (failure) =>
20
+ (failure._tag === "Failed"
21
+ ? Effect.logWarning(
22
+ `Kept the previous code: plugin "${failure.plugin}" failed to start`,
23
+ failure.cause,
24
+ )
25
+ : Effect.logWarning(
26
+ `Kept the previous code: plugin "${failure.plugin}" did not start in time`,
27
+ )
28
+ ).pipe(
29
+ Effect.annotateLogs({
30
+ "graft.fork": failure.fork,
31
+ "graft.plugin": failure.plugin,
32
+ }),
33
+ ),
34
+ { discard: true },
35
+ )
36
+
14
37
  const describe = (result: Hmr.ReloadResult) =>
15
38
  result._tag === "Reloaded"
16
39
  ? Effect.logInfo(`Reloaded ${result.entries.length} plugin module(s)`)
17
- : Effect.logWarning(
18
- result.reason === "ambiguous-exports"
19
- ? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, ", ")}`
20
- : result.reason === "identity-conflict"
21
- ? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, ", ")}`
22
- : `Restart required to pick up ${Array.join(result.modules, ", ")}`,
23
- )
40
+ : result._tag === "RolledBack"
41
+ ? rolledBack(result.failures)
42
+ : Effect.logWarning(
43
+ result.reason === "ambiguous-exports"
44
+ ? `Restart required: shared plugin exports diverged in ${Array.join(result.modules, ", ")}`
45
+ : result.reason === "identity-conflict"
46
+ ? `Restart required: plugin replacement identities conflict in ${Array.join(result.modules, ", ")}`
47
+ : `Restart required to pick up ${Array.join(result.modules, ", ")}`,
48
+ )
24
49
 
25
50
  /** Owns the HMR stream and retries failed loader entries after each reload. */
26
51
  export const watchModules = <E, R>(