@graftland/cli 0.4.2 → 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.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { CommandConfigError } from "./Commands.js";
2
2
  import { Config, Effect, FileSystem, Path } from "effect";
3
+ import { CliError } from "effect/cli";
3
4
  import * as PackageManager from "@graftland/package-manager";
4
5
  declare namespace Manage_d_exports {
5
6
  export { add, addPlugin, remove };
6
7
  }
7
- export declare const add: (specs: readonly string[]) => Effect.Effect<void, Config.ConfigError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path>;
8
- export declare const remove: (names: ReadonlyArray<string>) => Effect.Effect<void, Config.ConfigError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path>;
8
+ export declare const add: (specs: readonly string[]) => Effect.Effect<void, Config.ConfigError | CliError.UserError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("effect/crypto/SecureRandom").SecureRandom>;
9
+ export declare const remove: (names: ReadonlyArray<string>) => Effect.Effect<void, Config.ConfigError | CliError.UserError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("effect/crypto/SecureRandom").SecureRandom>;
9
10
  /** Install a command plugin and explicitly enable its /cli entry. */
10
- export declare const addPlugin: (name: string, global?: boolean | undefined) => Effect.Effect<void, CommandConfigError | Config.ConfigError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path>;
11
+ export declare const addPlugin: (name: string, global?: boolean | undefined) => Effect.Effect<void, CommandConfigError | Config.ConfigError | CliError.UserError | PackageManager.Error, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | import("effect/crypto/SecureRandom").SecureRandom>;
11
12
  export { Manage_d_exports };
12
13
  //# sourceMappingURL=Manage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Manage.d.ts","names":[],"sources":["../src/Manage.ts"],"mappings":";;;;;;qBAoEa,MAAG,6BAAA,OAAA,aAAA,OAAA,cAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA;qBAgBH,SAAM,OAAW,0BAAqB,OAAA,aAAA,OAAA,cAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA;;qBAuDtC,YAAS,cAAA,iCAAA,OAAA,aAAA,qBAAA,OAAA,cAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA"}
1
+ {"version":3,"file":"Manage.d.ts","names":[],"sources":["../src/Manage.ts"],"mappings":";;;;;;;qBA8Fa,MAAG,6BAAA,OAAA,aAAA,OAAA,cAAA,SAAA,YAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA,4CAAA;qBAgBH,SAAM,OAAW,0BAAqB,OAAA,aAAA,OAAA,cAAA,SAAA,YAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA,4CAAA;;qBAuDtC,YAAS,cAAA,iCAAA,OAAA,aAAA,qBAAA,OAAA,cAAA,SAAA,YAAA,eAAA,oDAAA,sBAAA,WAAA,aAAA,KAAA,4CAAA"}
package/dist/Manage.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
2
  import { CommandConfigError } from "./Commands.js";
3
3
  import { home } from "./internal/commandConfig.js";
4
+ import { schemaFileName } from "./internal/schemaFileName.js";
4
5
  import { Array, Config, Effect, FileSystem, Option, Path, Predicate, Record, Schema } from "effect";
6
+ import { CliError } from "effect/cli";
5
7
  import * as Create from "@graftland/create";
6
8
  import * as PackageManager from "@graftland/package-manager";
7
9
  import { parseDocument } from "yaml";
@@ -28,6 +30,21 @@ const Spec = Schema.Union([Schema.TemplateLiteralParser([
28
30
  Schema.String
29
31
  ]), PackageManager.PackageName]);
30
32
  const decodeSpecs = Schema.decodeEffect(Schema.Array(Spec));
33
+ /**
34
+ * Regenerates the project's editor schema, but only when it has one, so that
35
+ * the plugins just added or removed are checked. Imports no plugin.
36
+ */
37
+ const refreshSchema = Effect.fnUntraced(function* (directory) {
38
+ const fs = yield* FileSystem.FileSystem;
39
+ const path = yield* Path.Path;
40
+ const root = path.resolve(directory);
41
+ const file = path.join(root, schemaFileName);
42
+ if (!(yield* fs.exists(file))) return;
43
+ yield* (yield* Effect.promise(() => import("./internal/schemaFile.js")).pipe(Effect.uninterruptible)).write(root, file).pipe(Effect.mapError((cause) => CliError.UserError.make({
44
+ cause,
45
+ userMessage: `Dependencies changed, but ${file} was not regenerated: ${cause.message}`
46
+ })));
47
+ });
31
48
  const install = Effect.fn("Manage.install")(function* (directory, changes) {
32
49
  const decoded = yield* Schema.decodeEffect(PackageManager.Changes)(changes);
33
50
  const userAgent = yield* Config.String("npm_config_user_agent").pipe(Config.withDefault(""));
@@ -36,6 +53,7 @@ const install = Effect.fn("Manage.install")(function* (directory, changes) {
36
53
  userAgent
37
54
  })).install(decoded);
38
55
  yield* report(result);
56
+ yield* refreshSchema(directory);
39
57
  });
40
58
  const add = Effect.fn("Manage.add")(function* (specs) {
41
59
  const decoded = yield* decodeSpecs(specs);
@@ -87,11 +105,18 @@ const addPlugin = Effect.fn("Manage.addPlugin")(function* (name, global = false)
87
105
  const path = yield* Path.Path;
88
106
  const fs = yield* FileSystem.FileSystem;
89
107
  const directory = global ? yield* home : path.resolve(".");
90
- 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));
91
117
  const module = `${packageName}/cli`;
92
118
  const file = path.join(directory, "graft.yml");
93
119
  const text = yield* enabledConfig(file, module);
94
- if (global) yield* prepareHome(directory);
95
120
  yield* install(directory, Record.singleton(packageName, "latest"));
96
121
  yield* fs.writeFileString(file, text).pipe(Effect.uninterruptible);
97
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\"\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\"\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\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})\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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"}
package/dist/bin.js CHANGED
@@ -2,10 +2,11 @@
2
2
  import { version } from "./package.js";
3
3
  import { platform } from "./Application.js";
4
4
  import { output, run } from "./internal/commandHost.js";
5
+ import { schemaFileName } from "./internal/schemaFileName.js";
5
6
  import { Array, Effect, Layer, Logger, Option, Stdio } from "effect";
7
+ import { Argument, CliError, Command, Flag } from "effect/cli";
6
8
  import { NodeRuntime } from "@effect/platform-node";
7
9
  import { Graft } from "@graftland/graft";
8
- import { Argument, CliError, Command, Flag } from "effect/cli";
9
10
  import { command } from "@graftland/create/Command";
10
11
  // src/bin.ts
11
12
  /**
@@ -34,6 +35,7 @@ const add = Command.make("add", {
34
35
  return yield* Option.isSome(plugin) ? Manage.addPlugin(plugin.value, global) : Manage.add(packages);
35
36
  })).pipe(Command.withDescription("Add dependencies, restoring the project if installing fails"));
36
37
  const remove = Command.make("remove", { packages: Argument.variadic(Argument.String("package")) }, ({ packages }) => Effect.promise(() => import("./Manage.js")).pipe(Effect.uninterruptible, Effect.flatMap((Manage) => Manage.remove(packages)))).pipe(Command.withDescription("Remove dependencies, restoring the project if installing fails"));
38
+ const schema = Command.make("schema", { output: Flag.String("output").pipe(Flag.withAlias("o"), Flag.withDescription("Path of the schema file"), Flag.withDefault(schemaFileName)) }, ({ output }) => Effect.promise(() => import("./internal/schemaFile.js")).pipe(Effect.uninterruptible, Effect.flatMap((EditorSchema) => EditorSchema.write(".", output)))).pipe(Command.withDescription("Write the JSON Schema of graft.yml, with the config of installed plugins"));
37
39
  const graft = Command.make("graft").pipe(Command.withDescription("Run and manage Graft applications"), Command.withSharedFlags({
38
40
  stdio: Flag.Boolean("stdio").pipe(Flag.withDefault(false)),
39
41
  noPlugins: Flag.Boolean("no-plugins").pipe(Flag.withDefault(false))
@@ -46,7 +48,8 @@ const program = Effect.gen(function* () {
46
48
  start,
47
49
  command,
48
50
  add,
49
- remove
51
+ remove,
52
+ schema
50
53
  ], graft, version, args).pipe(Effect.provideContext(context));
51
54
  })), args);
52
55
  });
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":["Host.output","Host.run","create"],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * The `graft` executable. Running this module starts the program.\n */\nimport { Array, Effect, Layer, Logger, Option, Stdio } from \"effect\"\nimport { Argument, CliError, Command, Flag } from \"effect/cli\"\nimport { NodeRuntime } from \"@effect/platform-node\"\n\nimport { command as create } from \"@graftland/create/Command\"\nimport { Graft } from \"@graftland/graft\"\n\nimport { version } from \"../package.json\" with { type: \"json\" }\nimport { platform } from \"./Application\"\nimport * as Host from \"./internal/commandHost\"\n\nconst start = Command.make(\n\t\"start\",\n\t{\n\t\tconfig: Argument.String(\"config\").pipe(\n\t\t\tArgument.withDescription(\"Path of the config file\"),\n\t\t\tArgument.withDefault(\"graft.yml\"),\n\t\t),\n\t\twatch: Flag.Boolean(\"watch\").pipe(\n\t\t\tFlag.withAlias(\"w\"),\n\t\t\tFlag.withDescription(\"Reload plugins and config when files change\"),\n\t\t\tFlag.withDefault(false),\n\t\t),\n\t\tenvFile: Flag.String(\"env-file\").pipe(\n\t\t\tFlag.withDescription(\n\t\t\t\t\"Read variables for !env from a .env file; the process environment takes precedence\",\n\t\t\t),\n\t\t\tFlag.optional,\n\t\t),\n\t\tenvDir: Flag.String(\"env-dir\").pipe(\n\t\t\tFlag.withDescription(\n\t\t\t\t\"Read variables for !env from a directory with one file per variable, such as /run/secrets\",\n\t\t\t),\n\t\t\tFlag.optional,\n\t\t),\n\t},\n\t(options) =>\n\t\tEffect.promise(() => import(\"./Start\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t\tEffect.flatMap((Start) =>\n\t\t\t\tStart.run({\n\t\t\t\t\t...options,\n\t\t\t\t\tenvFile: Option.getOrUndefined(options.envFile),\n\t\t\t\t\tenvDir: Option.getOrUndefined(options.envDir),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n).pipe(\n\tCommand.withDescription(\"Run the application described by a config file\"),\n)\n\nconst add = Command.make(\n\t\"add\",\n\t{\n\t\tpackages: Argument.variadic(Argument.String(\"package\")),\n\t\tplugin: Flag.String(\"plugin\").pipe(Flag.optional),\n\t\tglobal: Flag.Boolean(\"global\").pipe(Flag.withDefault(false)),\n\t},\n\tEffect.fnUntraced(function* ({ packages, plugin, global }) {\n\t\tif (\n\t\t\t(global && Option.isNone(plugin)) ||\n\t\t\t(Option.isSome(plugin) && packages.length > 0)\n\t\t)\n\t\t\treturn yield* CliError.UserError.make({\n\t\t\t\tcause: \"Conflicting install options\",\n\t\t\t\tuserMessage:\n\t\t\t\t\t\"Use add --plugin <name> [--global] without package arguments\",\n\t\t\t})\n\t\tconst Manage = yield* Effect.promise(() => import(\"./Manage\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t)\n\t\treturn yield* Option.isSome(plugin)\n\t\t\t? Manage.addPlugin(plugin.value, global)\n\t\t\t: Manage.add(packages)\n\t}),\n).pipe(\n\tCommand.withDescription(\n\t\t\"Add dependencies, restoring the project if installing fails\",\n\t),\n)\n\nconst remove = Command.make(\n\t\"remove\",\n\t{ packages: Argument.variadic(Argument.String(\"package\")) },\n\t({ packages }) =>\n\t\tEffect.promise(() => import(\"./Manage\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t\tEffect.flatMap((Manage) => Manage.remove(packages)),\n\t\t),\n).pipe(\n\tCommand.withDescription(\n\t\t\"Remove dependencies, restoring the project if installing fails\",\n\t),\n)\n\nconst graft = Command.make(\"graft\").pipe(\n\tCommand.withDescription(\"Run and manage Graft applications\"),\n\tCommand.withSharedFlags({\n\t\tstdio: Flag.Boolean(\"stdio\").pipe(Flag.withDefault(false)),\n\t\tnoPlugins: Flag.Boolean(\"no-plugins\").pipe(Flag.withDefault(false)),\n\t}),\n)\n\nconst program = Effect.gen(function* () {\n\tconst stdio = yield* Stdio.Stdio\n\tconst args = yield* stdio.args\n\treturn yield* Host.output(\n\t\tEffect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst context = yield* Layer.build(Graft.layer)\n\t\t\t\treturn yield* Host.run(\n\t\t\t\t\t[start, create, add, remove],\n\t\t\t\t\tgraft,\n\t\t\t\t\tversion,\n\t\t\t\t\targs,\n\t\t\t\t).pipe(Effect.provideContext(context))\n\t\t\t}),\n\t\t),\n\t\targs,\n\t)\n})\nNodeRuntime.runMain(\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst context = yield* Layer.build(platform)\n\t\t\treturn yield* program.pipe(Effect.provideContext(context))\n\t\t}),\n\t).pipe(\n\t\t(effect) =>\n\t\t\tEffect.withFiber((main) =>\n\t\t\t\teffect.pipe(\n\t\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\treason._tag === \"Interrupt\" && reason.fiberId === main.id,\n\t\t\t\t\t\t) ||\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\treason._tag === \"Fail\" && CliError.isCliError(reason.error),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t\t: Effect.logError(cause),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\tEffect.provideService(Logger.LogToStderr, true),\n\t),\n\t{ disableErrorReporting: true },\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"bin.js","names":["Host.output","Host.run","create"],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * The `graft` executable. Running this module starts the program.\n */\nimport { Array, Effect, Layer, Logger, Option, Stdio } from \"effect\"\nimport { Argument, CliError, Command, Flag } from \"effect/cli\"\nimport { NodeRuntime } from \"@effect/platform-node\"\n\nimport { command as create } from \"@graftland/create/Command\"\nimport { Graft } from \"@graftland/graft\"\n\nimport { version } from \"../package.json\" with { type: \"json\" }\nimport { platform } from \"./Application\"\nimport * as Host from \"./internal/commandHost\"\nimport { schemaFileName } from \"./internal/schemaFileName\"\n\nconst start = Command.make(\n\t\"start\",\n\t{\n\t\tconfig: Argument.String(\"config\").pipe(\n\t\t\tArgument.withDescription(\"Path of the config file\"),\n\t\t\tArgument.withDefault(\"graft.yml\"),\n\t\t),\n\t\twatch: Flag.Boolean(\"watch\").pipe(\n\t\t\tFlag.withAlias(\"w\"),\n\t\t\tFlag.withDescription(\"Reload plugins and config when files change\"),\n\t\t\tFlag.withDefault(false),\n\t\t),\n\t\tenvFile: Flag.String(\"env-file\").pipe(\n\t\t\tFlag.withDescription(\n\t\t\t\t\"Read variables for !env from a .env file; the process environment takes precedence\",\n\t\t\t),\n\t\t\tFlag.optional,\n\t\t),\n\t\tenvDir: Flag.String(\"env-dir\").pipe(\n\t\t\tFlag.withDescription(\n\t\t\t\t\"Read variables for !env from a directory with one file per variable, such as /run/secrets\",\n\t\t\t),\n\t\t\tFlag.optional,\n\t\t),\n\t},\n\t(options) =>\n\t\tEffect.promise(() => import(\"./Start\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t\tEffect.flatMap((Start) =>\n\t\t\t\tStart.run({\n\t\t\t\t\t...options,\n\t\t\t\t\tenvFile: Option.getOrUndefined(options.envFile),\n\t\t\t\t\tenvDir: Option.getOrUndefined(options.envDir),\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n).pipe(\n\tCommand.withDescription(\"Run the application described by a config file\"),\n)\n\nconst add = Command.make(\n\t\"add\",\n\t{\n\t\tpackages: Argument.variadic(Argument.String(\"package\")),\n\t\tplugin: Flag.String(\"plugin\").pipe(Flag.optional),\n\t\tglobal: Flag.Boolean(\"global\").pipe(Flag.withDefault(false)),\n\t},\n\tEffect.fnUntraced(function* ({ packages, plugin, global }) {\n\t\tif (\n\t\t\t(global && Option.isNone(plugin)) ||\n\t\t\t(Option.isSome(plugin) && packages.length > 0)\n\t\t)\n\t\t\treturn yield* CliError.UserError.make({\n\t\t\t\tcause: \"Conflicting install options\",\n\t\t\t\tuserMessage:\n\t\t\t\t\t\"Use add --plugin <name> [--global] without package arguments\",\n\t\t\t})\n\t\tconst Manage = yield* Effect.promise(() => import(\"./Manage\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t)\n\t\treturn yield* Option.isSome(plugin)\n\t\t\t? Manage.addPlugin(plugin.value, global)\n\t\t\t: Manage.add(packages)\n\t}),\n).pipe(\n\tCommand.withDescription(\n\t\t\"Add dependencies, restoring the project if installing fails\",\n\t),\n)\n\nconst remove = Command.make(\n\t\"remove\",\n\t{ packages: Argument.variadic(Argument.String(\"package\")) },\n\t({ packages }) =>\n\t\tEffect.promise(() => import(\"./Manage\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t\tEffect.flatMap((Manage) => Manage.remove(packages)),\n\t\t),\n).pipe(\n\tCommand.withDescription(\n\t\t\"Remove dependencies, restoring the project if installing fails\",\n\t),\n)\n\nconst schema = Command.make(\n\t\"schema\",\n\t{\n\t\toutput: Flag.String(\"output\").pipe(\n\t\t\tFlag.withAlias(\"o\"),\n\t\t\tFlag.withDescription(\"Path of the schema file\"),\n\t\t\tFlag.withDefault(schemaFileName),\n\t\t),\n\t},\n\t({ output }) =>\n\t\tEffect.promise(() => import(\"./internal/schemaFile\")).pipe(\n\t\t\tEffect.uninterruptible,\n\t\t\tEffect.flatMap((EditorSchema) => EditorSchema.write(\".\", output)),\n\t\t),\n).pipe(\n\tCommand.withDescription(\n\t\t\"Write the JSON Schema of graft.yml, with the config of installed plugins\",\n\t),\n)\n\nconst graft = Command.make(\"graft\").pipe(\n\tCommand.withDescription(\"Run and manage Graft applications\"),\n\tCommand.withSharedFlags({\n\t\tstdio: Flag.Boolean(\"stdio\").pipe(Flag.withDefault(false)),\n\t\tnoPlugins: Flag.Boolean(\"no-plugins\").pipe(Flag.withDefault(false)),\n\t}),\n)\n\nconst program = Effect.gen(function* () {\n\tconst stdio = yield* Stdio.Stdio\n\tconst args = yield* stdio.args\n\treturn yield* Host.output(\n\t\tEffect.scoped(\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst context = yield* Layer.build(Graft.layer)\n\t\t\t\treturn yield* Host.run(\n\t\t\t\t\t[start, create, add, remove, schema],\n\t\t\t\t\tgraft,\n\t\t\t\t\tversion,\n\t\t\t\t\targs,\n\t\t\t\t).pipe(Effect.provideContext(context))\n\t\t\t}),\n\t\t),\n\t\targs,\n\t)\n})\nNodeRuntime.runMain(\n\tEffect.scoped(\n\t\tEffect.gen(function* () {\n\t\t\tconst context = yield* Layer.build(platform)\n\t\t\treturn yield* program.pipe(Effect.provideContext(context))\n\t\t}),\n\t).pipe(\n\t\t(effect) =>\n\t\t\tEffect.withFiber((main) =>\n\t\t\t\teffect.pipe(\n\t\t\t\t\tEffect.tapCause((cause) =>\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\treason._tag === \"Interrupt\" && reason.fiberId === main.id,\n\t\t\t\t\t\t) ||\n\t\t\t\t\t\tArray.every(\n\t\t\t\t\t\t\tcause.reasons,\n\t\t\t\t\t\t\t(reason) =>\n\t\t\t\t\t\t\t\treason._tag === \"Fail\" && CliError.isCliError(reason.error),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\t? Effect.void\n\t\t\t\t\t\t\t: Effect.logError(cause),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\tEffect.provideService(Logger.LogToStderr, true),\n\t),\n\t{ disableErrorReporting: true },\n)\n"],"mappings":"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"}
@@ -5,8 +5,8 @@ import { erase, make } from "./commandRegistry.js";
5
5
  import { make as make$1 } from "./importer.js";
6
6
  import { reserve } from "./protocolOutput.js";
7
7
  import { Array, Cause, Console, Effect, Option, Path, Schema, Stream, String } from "effect";
8
- import { Graft, Plugin } from "@graftland/graft";
9
8
  import { CliError, CliOutput, Command } from "effect/cli";
9
+ import { Graft, Plugin } from "@graftland/graft";
10
10
  // src/internal/commandHost.ts
11
11
  var CommandPluginError = class extends Schema.TaggedError()("CommandPluginError", {
12
12
  plugin: Schema.String,
@@ -0,0 +1,115 @@
1
+ import { make } from "./configSchema.js";
2
+ import { make as make$1 } from "./importer.js";
3
+ import { Array, Effect, FileSystem, Option, Path, Predicate, Record, Schema } from "effect";
4
+ // src/internal/schemaFile.ts
5
+ /** Composes the editor schema of a project's config file from its installed plugins. */
6
+ /** A dependency's `./schema` export could not be read. */
7
+ var SchemaRegistrationError = class extends Schema.TaggedError()("SchemaRegistrationError", {
8
+ packageName: Schema.String,
9
+ detail: Schema.String,
10
+ cause: Schema.optionalKey(Schema.Unknown)
11
+ }) {
12
+ get message() {
13
+ return `${this.packageName}/schema: ${this.detail}`;
14
+ }
15
+ };
16
+ const Dependencies = Schema.optionalKey(Schema.Record(Schema.String, Schema.String));
17
+ const ProjectManifest = Schema.fromJsonString(Schema.Struct({
18
+ dependencies: Dependencies,
19
+ devDependencies: Dependencies,
20
+ optionalDependencies: Dependencies
21
+ }));
22
+ const InstalledManifest = Schema.fromJsonString(Schema.Struct({
23
+ exports: Schema.optionalKey(Schema.Unknown),
24
+ dependencies: Dependencies,
25
+ peerDependencies: Dependencies
26
+ }));
27
+ const Registrations = Schema.Array(Schema.Struct({
28
+ names: Schema.Array(Schema.String),
29
+ schema: Schema.Record(Schema.String, Schema.Unknown),
30
+ required: Schema.optionalKey(Schema.Boolean)
31
+ }));
32
+ /**
33
+ * The manifest of an installed package, looked up as Node does: in
34
+ * `node_modules` of the directory and then of each parent, so hoisted
35
+ * workspace installs are found.
36
+ */
37
+ const installedManifest = (directory, packageName) => Effect.gen(function* () {
38
+ const fs = yield* FileSystem.FileSystem;
39
+ const path = yield* Path.Path;
40
+ const file = path.join(directory, "node_modules", packageName, "package.json");
41
+ if (yield* fs.exists(file)) return Option.some(file);
42
+ const parent = path.dirname(directory);
43
+ return parent === directory ? Option.none() : yield* installedManifest(parent, packageName);
44
+ });
45
+ /**
46
+ * Whether an installed dependency is a plugin package that exports `./schema`,
47
+ * read without importing it. Other packages, such as `effect`, may export a
48
+ * `./schema` of their own.
49
+ */
50
+ const exportsSchema = Effect.fnUntraced(function* (directory, packageName) {
51
+ const fs = yield* FileSystem.FileSystem;
52
+ const file = yield* installedManifest(directory, packageName);
53
+ if (Option.isNone(file)) {
54
+ yield* Effect.logWarning(`${packageName} is not installed; its config is left open`);
55
+ return false;
56
+ }
57
+ const text = yield* fs.readFileString(file.value);
58
+ const manifest = yield* Schema.decodeEffect(InstalledManifest)(text);
59
+ return Predicate.hasProperty(manifest.exports, "./schema") && (Record.has(manifest.dependencies ?? {}, "@graftland/graft") || Record.has(manifest.peerDependencies ?? {}, "@graftland/graft"));
60
+ });
61
+ const load = Effect.fnUntraced(function* (importer, packageName) {
62
+ const failed = (detail) => (cause) => SchemaRegistrationError.make({
63
+ packageName,
64
+ detail,
65
+ cause
66
+ });
67
+ const module = yield* Effect.tryPromise({
68
+ try: () => importer(`${packageName}/schema`),
69
+ catch: failed("cannot be imported")
70
+ });
71
+ if (!Predicate.hasProperty(module, "entries") || !Predicate.isFunction(module.entries)) return yield* SchemaRegistrationError.make({
72
+ packageName,
73
+ detail: "does not export an entries function"
74
+ });
75
+ const entries = module.entries;
76
+ const value = yield* Effect.try({
77
+ try: () => entries(),
78
+ catch: failed("entries() threw")
79
+ });
80
+ return yield* Schema.decodeUnknownEffect(Registrations)(value).pipe(Effect.mapError(failed("entries() returned invalid registrations")));
81
+ });
82
+ /**
83
+ * The registrations exported as `./schema` by the dependencies in the
84
+ * project's package.json. Only these modules are imported, never the plugins.
85
+ */
86
+ const registrations = Effect.fn("EditorSchema.registrations")(function* (directory) {
87
+ const fs = yield* FileSystem.FileSystem;
88
+ const path = yield* Path.Path;
89
+ const text = yield* fs.readFileString(path.join(directory, "package.json"));
90
+ const manifest = yield* Schema.decodeEffect(ProjectManifest)(text);
91
+ const names = Array.dedupe(Array.flatMap([
92
+ manifest.dependencies,
93
+ manifest.devDependencies,
94
+ manifest.optionalDependencies
95
+ ], (dependencies) => Record.keys(dependencies ?? {})));
96
+ const plugins = yield* Effect.filter(names, (name) => exportsSchema(directory, name));
97
+ const importer = yield* make$1(directory);
98
+ const loaded = yield* Effect.forEach(plugins, (name) => load(importer, name));
99
+ return {
100
+ plugins,
101
+ entries: Array.flatten(loaded)
102
+ };
103
+ }, Effect.scoped);
104
+ const encode = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown, { space: " " }));
105
+ /** Writes the editor schema for the project in a directory. */
106
+ const write = Effect.fn("EditorSchema.write")(function* (directory, output) {
107
+ const fs = yield* FileSystem.FileSystem;
108
+ const { plugins, entries } = yield* registrations(directory);
109
+ const text = yield* encode(make(entries));
110
+ yield* fs.writeFileString(output, text + "\n");
111
+ yield* Effect.logInfo(plugins.length === 0 ? `Wrote ${output}; no dependency exports ./schema` : `Wrote ${output} with ${Array.join(plugins, ", ")}`);
112
+ });
113
+ export { SchemaRegistrationError, registrations, write };
114
+
115
+ //# sourceMappingURL=schemaFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaFile.js","names":["Importer.make"],"sources":["../../src/internal/schemaFile.ts"],"sourcesContent":["/** Composes the editor schema of a project's config file from its installed plugins. */\nimport {\n\tArray,\n\tEffect,\n\tFileSystem,\n\tOption,\n\tPath,\n\tPredicate,\n\tRecord,\n\tSchema,\n\ttype PlatformError,\n} from \"effect\"\n\nimport { make } from \"./configSchema\"\nimport * as Importer from \"./importer\"\n\n/** A dependency's `./schema` export could not be read. */\nexport class SchemaRegistrationError extends Schema.TaggedError<\n\tSchemaRegistrationError,\n\t{ readonly brand: unique symbol }\n>()(\"SchemaRegistrationError\", {\n\tpackageName: Schema.String,\n\tdetail: Schema.String,\n\tcause: Schema.optionalKey(Schema.Unknown),\n}) {\n\toverride get message() {\n\t\treturn `${this.packageName}/schema: ${this.detail}`\n\t}\n}\n\nconst Dependencies = Schema.optionalKey(\n\tSchema.Record(Schema.String, Schema.String),\n)\nconst ProjectManifest = Schema.fromJsonString(\n\tSchema.Struct({\n\t\tdependencies: Dependencies,\n\t\tdevDependencies: Dependencies,\n\t\toptionalDependencies: Dependencies,\n\t}),\n)\nconst InstalledManifest = Schema.fromJsonString(\n\tSchema.Struct({\n\t\texports: Schema.optionalKey(Schema.Unknown),\n\t\tdependencies: Dependencies,\n\t\tpeerDependencies: Dependencies,\n\t}),\n)\nconst Registrations = Schema.Array(\n\tSchema.Struct({\n\t\tnames: Schema.Array(Schema.String),\n\t\tschema: Schema.Record(Schema.String, Schema.Unknown),\n\t\trequired: Schema.optionalKey(Schema.Boolean),\n\t}),\n)\n\n/**\n * The manifest of an installed package, looked up as Node does: in\n * `node_modules` of the directory and then of each parent, so hoisted\n * workspace installs are found.\n */\nconst installedManifest = (\n\tdirectory: string,\n\tpackageName: string,\n): Effect.Effect<\n\tOption.Option<string>,\n\tPlatformError.PlatformError,\n\tFileSystem.FileSystem | Path.Path\n> =>\n\tEffect.gen(function* () {\n\t\tconst fs = yield* FileSystem.FileSystem\n\t\tconst path = yield* Path.Path\n\t\tconst file = path.join(\n\t\t\tdirectory,\n\t\t\t\"node_modules\",\n\t\t\tpackageName,\n\t\t\t\"package.json\",\n\t\t)\n\t\tif (yield* fs.exists(file)) return Option.some(file)\n\t\tconst parent = path.dirname(directory)\n\t\treturn parent === directory\n\t\t\t? Option.none()\n\t\t\t: yield* installedManifest(parent, packageName)\n\t})\n\n/**\n * Whether an installed dependency is a plugin package that exports `./schema`,\n * read without importing it. Other packages, such as `effect`, may export a\n * `./schema` of their own.\n */\nconst exportsSchema = Effect.fnUntraced(function* (\n\tdirectory: string,\n\tpackageName: string,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst file = yield* installedManifest(directory, packageName)\n\tif (Option.isNone(file)) {\n\t\tyield* Effect.logWarning(\n\t\t\t`${packageName} is not installed; its config is left open`,\n\t\t)\n\t\treturn false\n\t}\n\tconst text = yield* fs.readFileString(file.value)\n\tconst manifest = yield* Schema.decodeEffect(InstalledManifest)(text)\n\treturn (\n\t\tPredicate.hasProperty(manifest.exports, \"./schema\") &&\n\t\t(Record.has(manifest.dependencies ?? {}, \"@graftland/graft\") ||\n\t\t\tRecord.has(manifest.peerDependencies ?? {}, \"@graftland/graft\"))\n\t)\n})\n\nconst load = Effect.fnUntraced(function* (\n\timporter: (specifier: string) => Promise<unknown>,\n\tpackageName: string,\n) {\n\tconst failed = (detail: string) => (cause: unknown) =>\n\t\tSchemaRegistrationError.make({ packageName, detail, cause })\n\tconst module = yield* Effect.tryPromise({\n\t\ttry: () => importer(`${packageName}/schema`),\n\t\tcatch: failed(\"cannot be imported\"),\n\t})\n\tif (\n\t\t!Predicate.hasProperty(module, \"entries\") ||\n\t\t!Predicate.isFunction(module.entries)\n\t)\n\t\treturn yield* SchemaRegistrationError.make({\n\t\t\tpackageName,\n\t\t\tdetail: \"does not export an entries function\",\n\t\t})\n\tconst entries = module.entries\n\tconst value = yield* Effect.try({\n\t\ttry: () => entries(),\n\t\tcatch: failed(\"entries() threw\"),\n\t})\n\treturn yield* Schema.decodeUnknownEffect(Registrations)(value).pipe(\n\t\tEffect.mapError(failed(\"entries() returned invalid registrations\")),\n\t)\n})\n\n/**\n * The registrations exported as `./schema` by the dependencies in the\n * project's package.json. Only these modules are imported, never the plugins.\n */\nexport const registrations = Effect.fn(\"EditorSchema.registrations\")(function* (\n\tdirectory: string,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst path = yield* Path.Path\n\tconst text = yield* fs.readFileString(path.join(directory, \"package.json\"))\n\tconst manifest = yield* Schema.decodeEffect(ProjectManifest)(text)\n\tconst names = Array.dedupe(\n\t\tArray.flatMap(\n\t\t\t[\n\t\t\t\tmanifest.dependencies,\n\t\t\t\tmanifest.devDependencies,\n\t\t\t\tmanifest.optionalDependencies,\n\t\t\t],\n\t\t\t(dependencies) => Record.keys(dependencies ?? {}),\n\t\t),\n\t)\n\tconst plugins = yield* Effect.filter(names, (name) =>\n\t\texportsSchema(directory, name),\n\t)\n\tconst importer = yield* Importer.make(directory)\n\tconst loaded = yield* Effect.forEach(plugins, (name) => load(importer, name))\n\treturn { plugins, entries: Array.flatten(loaded) }\n}, Effect.scoped)\n\nconst encode = Schema.encodeEffect(\n\tSchema.fromJsonString(Schema.Unknown, { space: \"\\t\" }),\n)\n\n/** Writes the editor schema for the project in a directory. */\nexport const write = Effect.fn(\"EditorSchema.write\")(function* (\n\tdirectory: string,\n\toutput: string,\n) {\n\tconst fs = yield* FileSystem.FileSystem\n\tconst { plugins, entries } = yield* registrations(directory)\n\tconst text = yield* encode(make(entries))\n\tyield* fs.writeFileString(output, text + \"\\n\")\n\tyield* Effect.logInfo(\n\t\tplugins.length === 0\n\t\t\t? `Wrote ${output}; no dependency exports ./schema`\n\t\t\t: `Wrote ${output} with ${Array.join(plugins, \", \")}`,\n\t)\n})\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ // src/internal/schemaFileName.ts
2
+ /** The editor schema that `graft schema` writes, and that add, remove and create keep current. */
3
+ const schemaFileName = "graft.schema.json";
4
+ export { schemaFileName };
5
+
6
+ //# sourceMappingURL=schemaFileName.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaFileName.js","names":[],"sources":["../../src/internal/schemaFileName.ts"],"sourcesContent":["/** The editor schema that `graft schema` writes, and that add, remove and create keep current. */\nexport const schemaFileName = \"graft.schema.json\"\n"],"mappings":";;AACA,MAAa,iBAAiB"}
@@ -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.2",
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.2",
41
- "@graftland/loader": "0.4.2",
42
- "@graftland/hmr": "0.4.2",
43
- "@graftland/create": "0.4.2",
44
- "@graftland/package-manager": "0.4.2",
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
  },