@gaganref/confect-cli 10.3.1-fork.1 → 10.3.1-fork.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Bundler.mjs.map +1 -1
- package/dist/CodegenError.mjs +1 -1
- package/dist/CodegenError.mjs.map +1 -1
- package/dist/ConfectDirectory.mjs.map +1 -1
- package/dist/ConvexConfig.mjs.map +1 -1
- package/dist/ConvexDirectory.mjs.map +1 -1
- package/dist/DirectoryOptions.mjs.map +1 -1
- package/dist/EntryPoint.mjs.map +1 -1
- package/dist/Evaluation.mjs.map +1 -1
- package/dist/EvaluationWorker.mjs.map +1 -1
- package/dist/FunctionModule.mjs.map +1 -1
- package/dist/FunctionPath.mjs.map +1 -1
- package/dist/FunctionPaths.mjs.map +1 -1
- package/dist/GenerationSnapshot.mjs.map +1 -1
- package/dist/GroupPaths.mjs.map +1 -1
- package/dist/ImplModulePath.mjs.map +1 -1
- package/dist/LeafModule.mjs +1 -1
- package/dist/LeafModule.mjs.map +1 -1
- package/dist/Remove.mjs.map +1 -1
- package/dist/Rollback.mjs.map +1 -1
- package/dist/SpecAssemblyNode.mjs.map +1 -1
- package/dist/SpecModulePath.mjs.map +1 -1
- package/dist/SyncState.mjs.map +1 -1
- package/dist/TableModule.mjs.map +1 -1
- package/dist/TableModulePath.mjs.map +1 -1
- package/dist/WatchBuild.mjs.map +1 -1
- package/dist/WatchTopology.mjs.map +1 -1
- package/dist/Watcher.mjs +49 -50
- package/dist/Watcher.mjs.map +1 -1
- package/dist/cliApp.mjs.map +1 -1
- package/dist/confect/codegen.mjs +31 -9
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/confect/declaration.mjs +4 -3
- package/dist/confect/declaration.mjs.map +1 -1
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/confect.mjs.map +1 -1
- package/dist/index.mjs +0 -0
- package/dist/index.mjs.map +1 -1
- package/dist/log.mjs.map +1 -1
- package/dist/templates.mjs +6 -6
- package/dist/templates.mjs.map +1 -1
- package/dist/utils.mjs +1 -1
- package/dist/utils.mjs.map +1 -1
- package/package.json +3 -3
package/dist/TableModule.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableModule.mjs","names":["Bundler.bundle","Bundler.withBatch"],"sources":["../src/TableModule.ts"],"sourcesContent":["import { Identifier } from \"@confect/core\";\nimport * as Table from \"@confect/core/Table\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Order from \"effect/Order\";\nimport { fromBundlerError } from \"./BuildError\";\nimport * as Bundler from \"./Bundler\";\nimport {\n DuplicateTableNameError,\n InvalidTableDefaultExportError,\n InvalidTableFilenameError,\n} from \"./CodegenError\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\n\nexport const TABLES_DIRNAME = \"tables\";\n\n/**\n * Discovered metadata for a single user-authored table module under\n * `confect/tables/`.\n *\n * - `relativePath`—path from `confect/` to the file (e.g. `tables/notes.ts`).\n * - `tableName`—the file basename (e.g. `notes`). This is also the import binding\n * used in generated files, and the table name surfaced to Convex.\n */\nexport interface TableModule {\n readonly relativePath: string;\n readonly tableName: string;\n}\n\nconst tableNameFromRelativePath = Effect.fnUntraced(function* (\n relativePath: string,\n) {\n const path = yield* Path.Path;\n const { name } = path.parse(relativePath);\n return name;\n});\n\nexport const discoverTableFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const tablesDirectory = path.join(confectDirectory, TABLES_DIRNAME);\n\n if (!(yield* fs.exists(tablesDirectory))) {\n return [] as ReadonlyArray<string>;\n }\n\n const allPaths = yield* fs.readDirectory(tablesDirectory, {\n recursive: true,\n });\n\n return pipe(\n allPaths,\n Array.filter((p) => p.endsWith(\".ts\") && !p.endsWith(\".test.ts\")),\n Array.map((p) => path.join(TABLES_DIRNAME, p)),\n );\n});\n\nconst byTableName = Order.mapInput(\n Order.String,\n (tableModule: TableModule) => tableModule.tableName,\n);\n\n/**\n * Discover every `confect/tables/**\\/*.ts` module by listing the directory.\n * Validates that each filename is a legal table identifier—the table name is\n * derived from the file basename, so the filename must be a valid JavaScript\n * identifier with no leading underscore (Convex reserves `_<name>` for system\n * tables).\n *\n * This step does _not_ bundle the table modules. It runs early in the codegen\n * pipeline so the `_generated/id.ts` constructor can be emitted _before_ any\n * user-authored table is bundled (those modules import from `_generated/id.ts`\n * for cross-table refs).\n *\n * A missing `confect/tables/` directory is allowed and produces an empty list.\n *\n * Fails with {@link InvalidTableFilenameError} if any filename is not a valid\n * table identifier, or {@link DuplicateTableNameError} if two files resolve to\n * the same table name (the directory is scanned recursively but names are\n * derived from the basename alone, so `tables/a/notes.ts` and\n * `tables/b/notes.ts` would collide).\n */\nexport const discover = Effect.gen(function* () {\n const relativePaths = yield* discoverTableFiles;\n\n const tableModules = yield* Effect.forEach(\n relativePaths,\n Effect.fnUntraced(function* (relativePath: string) {\n const tableName = yield* tableNameFromRelativePath(relativePath);\n yield* Effect.try({\n try: () => Identifier.validateConfectTableIdentifier(tableName),\n catch: (e) =>\n new InvalidTableFilenameError({\n tablePath: relativePath,\n reason: e instanceof Error ? e.message : String(e),\n }),\n });\n return { relativePath, tableName } satisfies TableModule;\n }),\n { concurrency: \"unbounded\" },\n );\n\n const sorted = pipe(tableModules, Array.sortBy(byTableName));\n\n const collisions = Object.entries(\n Array.groupBy(sorted, (tableModule) => tableModule.tableName),\n )\n .filter(([, group]) => group.length > 1)\n .map(([tableName, group]) => ({\n tableName,\n tablePaths: Array.map(group, (tableModule) => tableModule.relativePath),\n }));\n\n if (collisions.length > 0) {\n return yield* new DuplicateTableNameError({ collisions });\n }\n\n return sorted;\n});\n\n/**\n * Bundle every discovered table module and verify that its default export is an\n * {@link Table.UnnamedTable} (the result of `Table.make(...)` before a name has\n * been bound). Fails with {@link InvalidTableDefaultExportError} if any\n * module's default export is missing or has the wrong shape.\n *\n * Must run _after_ `_generated/id.ts` has been emitted, because user-authored\n * table modules typically `import { Id } from \"../_generated/id\"` for\n * cross-table references.\n */\nexport const validate = Effect.fnUntraced(function* (\n tableModules: ReadonlyArray<TableModule>,\n) {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const validateRelativePath = Effect.fnUntraced(function* (\n relativePath: string,\n ) {\n const absolutePath = path.resolve(confectDirectory, relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(relativePath, error)),\n );\n\n if (!Table.isUnnamedTable(module.default)) {\n return yield* new InvalidTableDefaultExportError({\n tablePath: relativePath,\n });\n }\n });\n\n const relativePaths = tableModules.map(({ relativePath }) => relativePath);\n yield* Bundler.withBatch(\n relativePaths.map((relativePath) =>\n path.resolve(confectDirectory, relativePath),\n ),\n Effect.forEach(relativePaths, validateRelativePath, { concurrency: 4 }),\n );\n});\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAa,iBAAiB;AAe9B,MAAM,4BAA4B,OAAO,WAAW,WAClD,cACA;CAEA,MAAM,EAAE,UAAS,OADG,KAAK,KAAA,CACH,MAAM,YAAY;CACxC,OAAO;AACT,CAAC;AAED,MAAa,qBAAqB,OAAO,IAAI,aAAa;CACxD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,kBAAkB,KAAK,KAAK,kBAAkB,cAAc;CAElE,IAAI,EAAE,OAAO,GAAG,OAAO,eAAe,IACpC,OAAO,CAAC;CAGV,MAAM,WAAW,OAAO,GAAG,cAAc,iBAAiB,EACxD,WAAW,KACb,CAAC;CAED,OAAO,KACL,UACA,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK,KAAK,CAAC,EAAE,SAAS,UAAU,CAAC,GAChE,MAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAC/C;AACF,CAAC;AAED,MAAM,cAAc,MAAM,SACxB,MAAM,SACL,gBAA6B,YAAY,SAC5C;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,WAAW,OAAO,IAAI,aAAa;CAC9C,MAAM,gBAAgB,OAAO;CAE7B,MAAM,eAAe,OAAO,OAAO,QACjC,eACA,OAAO,WAAW,WAAW,cAAsB;EACjD,MAAM,YAAY,OAAO,0BAA0B,YAAY;EAC/D,OAAO,OAAO,IAAI;GAChB,WAAW,WAAW,+BAA+B,SAAS;GAC9D,QAAQ,MACN,IAAI,0BAA0B;IAC5B,WAAW;IACX,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GACnD,CAAC;EACL,CAAC;EACD,OAAO;GAAE;GAAc;EAAU;CACnC,CAAC,GACD,EAAE,aAAa,YAAY,CAC7B;CAEA,MAAM,SAAS,KAAK,cAAc,MAAM,OAAO,WAAW,CAAC;CAE3D,MAAM,aAAa,OAAO,QACxB,MAAM,QAAQ,SAAS,gBAAgB,YAAY,SAAS,CAC9D,CAAC,CACE,QAAQ,GAAG,WAAW,MAAM,SAAS,CAAC,CAAC,CACvC,KAAK,CAAC,WAAW,YAAY;EAC5B;EACA,YAAY,MAAM,IAAI,QAAQ,gBAAgB,YAAY,YAAY;CACxE,EAAE;CAEJ,IAAI,WAAW,SAAS,GACtB,OAAO,OAAO,IAAI,wBAAwB,EAAE,WAAW,CAAC;CAG1D,OAAO;AACT,CAAC;;;;;;;;;;;AAYD,MAAa,WAAW,OAAO,WAAW,WACxC,cACA;CACA,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CAEjD,MAAM,uBAAuB,OAAO,WAAW,WAC7C,cACA;EACA,MAAM,eAAe,KAAK,QAAQ,kBAAkB,YAAY;EAChE,MAAM,EAAE,WAAW,OAAOA,OAAe,YAAY,CAAC,CAAC,KACrD,OAAO,UAAU,UAAU,iBAAiB,cAAc,KAAK,CAAC,CAClE;EAEA,IAAI,CAAC,MAAM,eAAe,OAAO,OAAO,GACtC,OAAO,OAAO,IAAI,+BAA+B,EAC/C,WAAW,aACb,CAAC;CAEL,CAAC;CAED,MAAM,gBAAgB,aAAa,KAAK,EAAE,mBAAmB,YAAY;CACzE,OAAOC,UACL,cAAc,KAAK,iBACjB,KAAK,QAAQ,kBAAkB,YAAY,CAC7C,GACA,OAAO,QAAQ,eAAe,sBAAsB,EAAE,aAAa,EAAE,CAAC,CACxE;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"TableModule.mjs","names":["Bundler.bundle","Bundler.withBatch"],"sources":["../src/TableModule.ts"],"sourcesContent":["import { Identifier } from \"@confect/core\";\nimport * as Table from \"@confect/core/Table\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Order from \"effect/Order\";\nimport { fromBundlerError } from \"./BuildError.js\";\nimport * as Bundler from \"./Bundler.js\";\nimport {\n DuplicateTableNameError,\n InvalidTableDefaultExportError,\n InvalidTableFilenameError,\n} from \"./CodegenError.js\";\nimport { ConfectDirectory } from \"./ConfectDirectory.js\";\n\nexport const TABLES_DIRNAME = \"tables\";\n\n/**\n * Discovered metadata for a single user-authored table module under\n * `confect/tables/`.\n *\n * - `relativePath`—path from `confect/` to the file (e.g. `tables/notes.ts`).\n * - `tableName`—the file basename (e.g. `notes`). This is also the import binding\n * used in generated files, and the table name surfaced to Convex.\n */\nexport interface TableModule {\n readonly relativePath: string;\n readonly tableName: string;\n}\n\nconst tableNameFromRelativePath = Effect.fnUntraced(function* (\n relativePath: string,\n) {\n const path = yield* Path.Path;\n const { name } = path.parse(relativePath);\n return name;\n});\n\nexport const discoverTableFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const tablesDirectory = path.join(confectDirectory, TABLES_DIRNAME);\n\n if (!(yield* fs.exists(tablesDirectory))) {\n return [] as ReadonlyArray<string>;\n }\n\n const allPaths = yield* fs.readDirectory(tablesDirectory, {\n recursive: true,\n });\n\n return pipe(\n allPaths,\n Array.filter((p) => p.endsWith(\".ts\") && !p.endsWith(\".test.ts\")),\n Array.map((p) => path.join(TABLES_DIRNAME, p)),\n );\n});\n\nconst byTableName = Order.mapInput(\n Order.String,\n (tableModule: TableModule) => tableModule.tableName,\n);\n\n/**\n * Discover every `confect/tables/**\\/*.ts` module by listing the directory.\n * Validates that each filename is a legal table identifier—the table name is\n * derived from the file basename, so the filename must be a valid JavaScript\n * identifier with no leading underscore (Convex reserves `_<name>` for system\n * tables).\n *\n * This step does _not_ bundle the table modules. It runs early in the codegen\n * pipeline so the `_generated/id.ts` constructor can be emitted _before_ any\n * user-authored table is bundled (those modules import from `_generated/id.ts`\n * for cross-table refs).\n *\n * A missing `confect/tables/` directory is allowed and produces an empty list.\n *\n * Fails with {@link InvalidTableFilenameError} if any filename is not a valid\n * table identifier, or {@link DuplicateTableNameError} if two files resolve to\n * the same table name (the directory is scanned recursively but names are\n * derived from the basename alone, so `tables/a/notes.ts` and\n * `tables/b/notes.ts` would collide).\n */\nexport const discover = Effect.gen(function* () {\n const relativePaths = yield* discoverTableFiles;\n\n const tableModules = yield* Effect.forEach(\n relativePaths,\n Effect.fnUntraced(function* (relativePath: string) {\n const tableName = yield* tableNameFromRelativePath(relativePath);\n yield* Effect.try({\n try: () => Identifier.validateConfectTableIdentifier(tableName),\n catch: (e) =>\n new InvalidTableFilenameError({\n tablePath: relativePath,\n reason: e instanceof Error ? e.message : String(e),\n }),\n });\n return { relativePath, tableName } satisfies TableModule;\n }),\n { concurrency: \"unbounded\" },\n );\n\n const sorted = pipe(tableModules, Array.sortBy(byTableName));\n\n const collisions = Object.entries(\n Array.groupBy(sorted, (tableModule) => tableModule.tableName),\n )\n .filter(([, group]) => group.length > 1)\n .map(([tableName, group]) => ({\n tableName,\n tablePaths: Array.map(group, (tableModule) => tableModule.relativePath),\n }));\n\n if (collisions.length > 0) {\n return yield* new DuplicateTableNameError({ collisions });\n }\n\n return sorted;\n});\n\n/**\n * Bundle every discovered table module and verify that its default export is an\n * {@link Table.UnnamedTable} (the result of `Table.make(...)` before a name has\n * been bound). Fails with {@link InvalidTableDefaultExportError} if any\n * module's default export is missing or has the wrong shape.\n *\n * Must run _after_ `_generated/id.ts` has been emitted, because user-authored\n * table modules typically `import { Id } from \"../_generated/id\"` for\n * cross-table references.\n */\nexport const validate = Effect.fnUntraced(function* (\n tableModules: ReadonlyArray<TableModule>,\n) {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const validateRelativePath = Effect.fnUntraced(function* (\n relativePath: string,\n ) {\n const absolutePath = path.resolve(confectDirectory, relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(relativePath, error)),\n );\n\n if (!Table.isUnnamedTable(module.default)) {\n return yield* new InvalidTableDefaultExportError({\n tablePath: relativePath,\n });\n }\n });\n\n const relativePaths = tableModules.map(({ relativePath }) => relativePath);\n yield* Bundler.withBatch(\n relativePaths.map((relativePath) =>\n path.resolve(confectDirectory, relativePath),\n ),\n Effect.forEach(relativePaths, validateRelativePath, { concurrency: 4 }),\n );\n});\n"],"mappings":";;;;;;;;;;;;;AAiBA,MAAa,iBAAiB;AAe9B,MAAM,4BAA4B,OAAO,WAAW,WAClD,cACA;CAEA,MAAM,EAAE,UAAS,OADG,KAAK,KAAA,CACH,MAAM,YAAY;CACxC,OAAO;AACT,CAAC;AAED,MAAa,qBAAqB,OAAO,IAAI,aAAa;CACxD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,kBAAkB,KAAK,KAAK,kBAAkB,cAAc;CAElE,IAAI,EAAE,OAAO,GAAG,OAAO,eAAe,IACpC,OAAO,CAAC;CAGV,MAAM,WAAW,OAAO,GAAG,cAAc,iBAAiB,EACxD,WAAW,KACb,CAAC;CAED,OAAO,KACL,UACA,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK,KAAK,CAAC,EAAE,SAAS,UAAU,CAAC,GAChE,MAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAC/C;AACF,CAAC;AAED,MAAM,cAAc,MAAM,SACxB,MAAM,SACL,gBAA6B,YAAY,SAC5C;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,WAAW,OAAO,IAAI,aAAa;CAC9C,MAAM,gBAAgB,OAAO;CAE7B,MAAM,eAAe,OAAO,OAAO,QACjC,eACA,OAAO,WAAW,WAAW,cAAsB;EACjD,MAAM,YAAY,OAAO,0BAA0B,YAAY;EAC/D,OAAO,OAAO,IAAI;GAChB,WAAW,WAAW,+BAA+B,SAAS;GAC9D,QAAQ,MACN,IAAI,0BAA0B;IAC5B,WAAW;IACX,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GACnD,CAAC;EACL,CAAC;EACD,OAAO;GAAE;GAAc;EAAU;CACnC,CAAC,GACD,EAAE,aAAa,YAAY,CAC7B;CAEA,MAAM,SAAS,KAAK,cAAc,MAAM,OAAO,WAAW,CAAC;CAE3D,MAAM,aAAa,OAAO,QACxB,MAAM,QAAQ,SAAS,gBAAgB,YAAY,SAAS,CAC9D,CAAC,CACE,QAAQ,GAAG,WAAW,MAAM,SAAS,CAAC,CAAC,CACvC,KAAK,CAAC,WAAW,YAAY;EAC5B;EACA,YAAY,MAAM,IAAI,QAAQ,gBAAgB,YAAY,YAAY;CACxE,EAAE;CAEJ,IAAI,WAAW,SAAS,GACtB,OAAO,OAAO,IAAI,wBAAwB,EAAE,WAAW,CAAC;CAG1D,OAAO;AACT,CAAC;;;;;;;;;;;AAYD,MAAa,WAAW,OAAO,WAAW,WACxC,cACA;CACA,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CAEjD,MAAM,uBAAuB,OAAO,WAAW,WAC7C,cACA;EACA,MAAM,eAAe,KAAK,QAAQ,kBAAkB,YAAY;EAChE,MAAM,EAAE,WAAW,OAAOA,OAAe,YAAY,CAAC,CAAC,KACrD,OAAO,UAAU,UAAU,iBAAiB,cAAc,KAAK,CAAC,CAClE;EAEA,IAAI,CAAC,MAAM,eAAe,OAAO,OAAO,GACtC,OAAO,OAAO,IAAI,+BAA+B,EAC/C,WAAW,aACb,CAAC;CAEL,CAAC;CAED,MAAM,gBAAgB,aAAa,KAAK,EAAE,mBAAmB,YAAY;CACzE,OAAOC,UACL,cAAc,KAAK,iBACjB,KAAK,QAAQ,kBAAkB,YAAY,CAC7C,GACA,OAAO,QAAQ,eAAe,sBAAsB,EAAE,aAAa,EAAE,CAAC,CACxE;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableModulePath.mjs","names":["RelativeModulePath.withSuffix"],"sources":["../src/TableModulePath.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\nimport * as RelativeModulePath from \"./RelativeModulePath\";\n\nexport const TableModulePath = RelativeModulePath.withSuffix(\".ts\")\n .check(Schema.makeFilter((value) => value.split(/[/\\\\]/)[0] === \"tables\"))\n .pipe(Schema.brand(\"@confect/cli/TableModulePath\"));\n\nexport type TableModulePath = typeof TableModulePath.Type;\n"],"mappings":";;;AAGA,MAAa,kBAAkBA,WAA8B,KAAK,CAAC,CAChE,MAAM,OAAO,YAAY,UAAU,MAAM,MAAM,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CACzE,KAAK,OAAO,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"TableModulePath.mjs","names":["RelativeModulePath.withSuffix"],"sources":["../src/TableModulePath.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\nimport * as RelativeModulePath from \"./RelativeModulePath.js\";\n\nexport const TableModulePath = RelativeModulePath.withSuffix(\".ts\")\n .check(Schema.makeFilter((value) => value.split(/[/\\\\]/)[0] === \"tables\"))\n .pipe(Schema.brand(\"@confect/cli/TableModulePath\"));\n\nexport type TableModulePath = typeof TableModulePath.Type;\n"],"mappings":";;;AAGA,MAAa,kBAAkBA,WAA8B,KAAK,CAAC,CAChE,MAAM,OAAO,YAAY,UAAU,MAAM,MAAM,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC,CAAC,CACzE,KAAK,OAAO,MAAM,8BAA8B,CAAC"}
|
package/dist/WatchBuild.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WatchBuild.mjs","names":["WatchGraph.affected","WatchGraph.make"],"sources":["../src/WatchBuild.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport * as Brand from \"effect/Brand\";\nimport type * as Crypto from \"effect/Crypto\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport type * as FileSystem from \"effect/FileSystem\";\nimport * as Equal from \"effect/Equal\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Match from \"effect/Match\";\nimport * as Hex from \"effect/encoding/Hex\";\nimport * as Option from \"effect/Option\";\nimport type { PlatformError } from \"effect/PlatformError\";\nimport * as Result from \"effect/Result\";\nimport type * as esbuild from \"esbuild\";\nimport * as WatchGraph from \"./WatchGraph\";\n\nexport type Failure = Data.TaggedEnum<{\n BuildFailed: {\n readonly errors: Array.NonEmptyReadonlyArray<esbuild.Message>;\n };\n InputCaptureFailed: { readonly file: string; readonly cause: PlatformError };\n OutputCaptureFailed: { readonly file: string; readonly cause: PlatformError };\n InvalidBuildResult: { readonly missing: \"metafile\" | \"outputFiles\" };\n}>;\nexport const Failure = Data.taggedEnum<Failure>();\nexport type Fingerprints = HashMap.HashMap<string, string>;\n\nexport interface Capture {\n readonly fingerprints: Fingerprints;\n readonly failures: HashMap.HashMap<string, Failure>;\n}\nexport const emptyCapture: Capture = {\n fingerprints: HashMap.empty(),\n failures: HashMap.empty(),\n};\n\nexport interface Snapshot {\n readonly fingerprints: Fingerprints;\n readonly graph: WatchGraph.WatchGraph;\n readonly outputHash: string;\n}\n\nexport type Outcome = Data.TaggedEnum<{\n Succeeded: {\n readonly snapshot: Snapshot;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n Failed: {\n readonly fingerprints: Fingerprints;\n readonly failures: Array.NonEmptyReadonlyArray<Failure>;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n}>;\nexport const Outcome = Data.taggedEnum<Outcome>();\n\nexport type Generation = number & Brand.Brand<\"WatchBuild.Generation\">;\nconst Generation = Brand.nominal<Generation>();\n\nexport type Barrier = number & Brand.Brand<\"WatchBuild.Barrier\">;\nconst Barrier = Brand.nominal<Barrier>();\n\nexport type RequestId = object & Brand.Brand<\"WatchBuild.RequestId\">;\nconst RequestId = Brand.nominal<RequestId>();\n\nexport const makeRequestId = (): RequestId => RequestId({});\n\nexport interface Completion {\n readonly generation: Generation;\n readonly outcome: Outcome;\n}\n\ntype Request = Data.TaggedEnum<{\n Idle: {};\n Pending: {};\n Joined: {};\n Completed: { readonly completion: Completion };\n}>;\nconst Request = Data.taggedEnum<Request>();\n\nexport interface Progress {\n readonly generation: Generation;\n readonly request: Request;\n readonly lastRequest: Option.Option<RequestId>;\n}\n\nexport const initialProgress: Progress = {\n generation: Generation(0),\n request: Request.Idle(),\n lastRequest: Option.none(),\n};\n\nexport const start = (progress: Progress): Progress => ({\n ...progress,\n generation: Generation(progress.generation + 1),\n});\n\nexport const captureBarrier = (progress: Progress): Barrier =>\n Barrier(progress.generation);\n\nexport const request = (progress: Progress, id: RequestId): Progress => ({\n ...progress,\n request:\n Option.isSome(progress.lastRequest) && progress.lastRequest.value === id\n ? Request.Joined()\n : Request.Pending(),\n lastRequest: Option.some(id),\n});\n\nexport const complete = (\n progress: Progress,\n completion: Completion,\n): Progress => ({\n ...progress,\n request: Request.$match(progress.request, {\n Idle: (idle) => idle,\n Pending: () => Request.Completed({ completion }),\n Joined: () => Request.Completed({ completion }),\n Completed: (completed) => completed,\n }),\n});\n\nexport const getCompletion = (progress: Progress): Option.Option<Completion> =>\n Request.$match(progress.request, {\n Idle: () => Option.none(),\n Pending: () => Option.none(),\n Joined: () => Option.none(),\n Completed: ({ completion }) => Option.some(completion),\n });\n\nexport const joinedWithoutCompletion = (progress: Progress): boolean =>\n Request.$is(\"Joined\")(progress.request);\n\nexport const endRequest = (progress: Progress): Progress => ({\n ...progress,\n request: Request.Idle(),\n});\n\nexport const isFresh = (barrier: Barrier, completion: Completion): boolean =>\n completion.generation > barrier;\n\nexport const verifyInputs = Effect.fn(\"WatchBuild.verifyInputs\")(function* (\n snapshot: Snapshot,\n fs: FileSystem.FileSystem,\n crypto: Crypto.Crypto,\n) {\n const matches = yield* Effect.forEach(\n snapshot.fingerprints,\n ([file, expected]) =>\n fs.readFile(file).pipe(\n Effect.flatMap((contents) => crypto.digest(\"SHA-256\", contents)),\n Effect.map((digest) => Hex.encode(digest) === expected),\n Effect.mapError((cause) => Failure.InputCaptureFailed({ file, cause })),\n ),\n { concurrency: 8 },\n );\n return Array.every(matches, (matchesInput) => matchesInput);\n});\n\nexport type State = Data.TaggedEnum<{\n Initial: {};\n Succeeded: {\n readonly snapshot: Snapshot;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n Failed: {\n readonly observation: Extract<Outcome, { readonly _tag: \"Failed\" }>;\n readonly lastSuccessful: Option.Option<Snapshot>;\n readonly discovered: HashSet.HashSet<string>;\n };\n}>;\nexport const State = Data.taggedEnum<State>();\n\nexport const lastSuccessful = (state: State): Option.Option<Snapshot> =>\n State.$match(state, {\n Initial: () => Option.none(),\n Succeeded: ({ snapshot }) => Option.some(snapshot),\n Failed: ({ lastSuccessful: snapshot }) => snapshot,\n });\n\nexport const observe = (previous: State, outcome: Outcome): State =>\n Outcome.$match(outcome, {\n Succeeded: ({ snapshot, warnings }) =>\n State.Succeeded({ snapshot, warnings }),\n Failed: (observation) =>\n State.Failed({\n observation,\n lastSuccessful: lastSuccessful(previous),\n discovered: State.$is(\"Initial\")(previous)\n ? HashSet.empty()\n : HashSet.union(\n State.$is(\"Failed\")(previous)\n ? previous.discovered\n : HashSet.empty(),\n HashSet.difference(\n HashSet.fromIterable(HashMap.keys(observation.fingerprints)),\n HashSet.fromIterable(HashMap.keys(fingerprints(previous))),\n ),\n ),\n }),\n });\n\nexport const fingerprints = (state: State): Fingerprints =>\n State.$match(state, {\n Initial: () => HashMap.empty(),\n Succeeded: ({ snapshot }) => snapshot.fingerprints,\n Failed: ({ observation }) => observation.fingerprints,\n });\n\nexport const outputHash = (state: State): Option.Option<string> =>\n State.$match(state, {\n Initial: () => Option.none(),\n Succeeded: ({ snapshot }) => Option.some(snapshot.outputHash),\n Failed: () => Option.none(),\n });\n\nexport type Change = Data.TaggedEnum<{\n Full: {};\n Affected: { readonly paths: HashSet.HashSet<string> };\n Recovered: { readonly paths: HashSet.HashSet<string> };\n}>;\nexport const Change = Data.taggedEnum<Change>();\n\nexport const diff = (\n previous: State,\n current: State,\n generatedDirectoryPrefix: string,\n): Change => {\n const previousFingerprints = fingerprints(previous);\n const currentFingerprints = fingerprints(current);\n const files = HashSet.union(\n HashSet.fromIterable(HashMap.keys(previousFingerprints)),\n HashSet.fromIterable(HashMap.keys(currentFingerprints)),\n );\n const changed = Array.fromIterable(files).filter(\n (file) =>\n !Equal.equals(\n HashMap.get(previousFingerprints, file),\n HashMap.get(currentFingerprints, file),\n ),\n );\n const sources = changed.filter(\n (file) => !file.startsWith(generatedDirectoryPrefix),\n );\n const recovered =\n State.$is(\"Failed\")(previous) && State.$is(\"Succeeded\")(current);\n const untrackedOutput = recovered\n ? Option.match(lastSuccessful(previous), {\n onNone: () => false,\n onSome: (baseline) =>\n Equal.equals(baseline.fingerprints, currentFingerprints) &&\n !Equal.equals(Option.some(baseline.outputHash), outputHash(current)),\n })\n : changed.length === 0 &&\n !Equal.equals(outputHash(previous), outputHash(current));\n if (\n State.$is(\"Initial\")(previous) ||\n (sources.some((file) => HashMap.has(previousFingerprints, file)) &&\n State.$is(\"Failed\")(current)) ||\n untrackedOutput\n )\n return Change.Full();\n\n const graph = Option.match(lastSuccessful(current), {\n onNone: () => HashMap.empty<string, HashSet.HashSet<string>>(),\n onSome: (snapshot) => snapshot.graph,\n });\n const previousGraph = Option.match(lastSuccessful(previous), {\n onNone: () => HashMap.empty<string, HashSet.HashSet<string>>(),\n onSome: (snapshot) => snapshot.graph,\n });\n const affectedSources =\n recovered && State.$is(\"Failed\")(previous)\n ? Array.fromIterable(\n HashSet.union(\n HashSet.fromIterable(sources),\n HashSet.filter(\n previous.discovered,\n (file) => !file.startsWith(generatedDirectoryPrefix),\n ),\n ),\n )\n : sources;\n const paths = HashSet.union(\n WatchGraph.affected(graph, affectedSources),\n WatchGraph.affected(previousGraph, affectedSources),\n );\n return recovered ? Change.Recovered({ paths }) : Change.Affected({ paths });\n};\n\nexport const parse = Effect.fn(\"WatchBuild.parse\")(function* (\n result: esbuild.BuildResult,\n capture: Capture,\n resolve: (file: string) => string,\n crypto: Crypto.Crypto,\n) {\n const failures = Array.fromIterable(HashMap.values(capture.failures));\n if (Array.isReadonlyArrayNonEmpty(result.errors))\n failures.push(Failure.BuildFailed({ errors: result.errors }));\n if (Array.isReadonlyArrayNonEmpty(failures))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures,\n warnings: result.warnings,\n });\n if (!result.metafile)\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [Failure.InvalidBuildResult({ missing: \"metafile\" })],\n warnings: result.warnings,\n });\n if (!result.outputFiles || !Array.isReadonlyArrayNonEmpty(result.outputFiles))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [Failure.InvalidBuildResult({ missing: \"outputFiles\" })],\n warnings: result.warnings,\n });\n const hashes = yield* Effect.forEach(result.outputFiles, (file) =>\n crypto.digest(\"SHA-256\", file.contents).pipe(\n Effect.map(Hex.encode),\n Effect.mapError((cause) =>\n Failure.OutputCaptureFailed({ file: file.path, cause }),\n ),\n ),\n ).pipe(Effect.result);\n if (Result.isFailure(hashes))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [hashes.failure],\n warnings: result.warnings,\n });\n return Outcome.Succeeded({\n snapshot: {\n fingerprints: capture.fingerprints,\n graph: WatchGraph.make(result.metafile, resolve),\n outputHash: hashes.success.join(\":\"),\n },\n warnings: result.warnings,\n });\n});\n\nexport const failureMessage = (failure: Failure): string =>\n Failure.$match(failure, {\n BuildFailed: ({ errors }) => errors.map((error) => error.text).join(\"\\n\"),\n InputCaptureFailed: ({ file, cause }) =>\n `Could not fingerprint input ${file}: ${cause.message}`,\n OutputCaptureFailed: ({ file, cause }) =>\n `Could not fingerprint output ${file}: ${cause.message}`,\n InvalidBuildResult: ({ missing }) => `esbuild completed without ${missing}`,\n });\n\nexport const messages = (outcome: Outcome): ReadonlyArray<esbuild.Message> =>\n Outcome.$match(outcome, {\n Succeeded: () => [],\n Failed: ({ failures }) =>\n failures.flatMap((failure) =>\n Match.value(failure).pipe(\n Match.tag(\"BuildFailed\", ({ errors }) => errors),\n Match.tag(\n \"InputCaptureFailed\",\n \"OutputCaptureFailed\",\n \"InvalidBuildResult\",\n (captureFailure) => [\n {\n id: \"confect-watch\",\n pluginName: \"confect\",\n text: failureMessage(captureFailure),\n location: null,\n notes: [],\n detail: captureFailure,\n },\n ],\n ),\n Match.exhaustive,\n ),\n ),\n });\n\nexport const diagnostics = (\n state: State,\n): {\n readonly errors: ReadonlyArray<esbuild.Message>;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n} =>\n State.$match(state, {\n Initial: () => ({ errors: [], warnings: [] }),\n Succeeded: ({ warnings }) => ({ errors: [], warnings }),\n Failed: ({ observation }) => ({\n errors: messages(observation),\n warnings: observation.warnings,\n }),\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WatchBuild.mjs","names":["WatchGraph.affected","WatchGraph.make"],"sources":["../src/WatchBuild.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport * as Brand from \"effect/Brand\";\nimport type * as Crypto from \"effect/Crypto\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport type * as FileSystem from \"effect/FileSystem\";\nimport * as Equal from \"effect/Equal\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Match from \"effect/Match\";\nimport * as Hex from \"effect/encoding/Hex\";\nimport * as Option from \"effect/Option\";\nimport type { PlatformError } from \"effect/PlatformError\";\nimport * as Result from \"effect/Result\";\nimport type * as esbuild from \"esbuild\";\nimport * as WatchGraph from \"./WatchGraph.js\";\n\nexport type Failure = Data.TaggedEnum<{\n BuildFailed: {\n readonly errors: Array.NonEmptyReadonlyArray<esbuild.Message>;\n };\n InputCaptureFailed: { readonly file: string; readonly cause: PlatformError };\n OutputCaptureFailed: { readonly file: string; readonly cause: PlatformError };\n InvalidBuildResult: { readonly missing: \"metafile\" | \"outputFiles\" };\n}>;\nexport const Failure = Data.taggedEnum<Failure>();\nexport type Fingerprints = HashMap.HashMap<string, string>;\n\nexport interface Capture {\n readonly fingerprints: Fingerprints;\n readonly failures: HashMap.HashMap<string, Failure>;\n}\nexport const emptyCapture: Capture = {\n fingerprints: HashMap.empty(),\n failures: HashMap.empty(),\n};\n\nexport interface Snapshot {\n readonly fingerprints: Fingerprints;\n readonly graph: WatchGraph.WatchGraph;\n readonly outputHash: string;\n}\n\nexport type Outcome = Data.TaggedEnum<{\n Succeeded: {\n readonly snapshot: Snapshot;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n Failed: {\n readonly fingerprints: Fingerprints;\n readonly failures: Array.NonEmptyReadonlyArray<Failure>;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n}>;\nexport const Outcome = Data.taggedEnum<Outcome>();\n\nexport type Generation = number & Brand.Brand<\"WatchBuild.Generation\">;\nconst Generation = Brand.nominal<Generation>();\n\nexport type Barrier = number & Brand.Brand<\"WatchBuild.Barrier\">;\nconst Barrier = Brand.nominal<Barrier>();\n\nexport type RequestId = object & Brand.Brand<\"WatchBuild.RequestId\">;\nconst RequestId = Brand.nominal<RequestId>();\n\nexport const makeRequestId = (): RequestId => RequestId({});\n\nexport interface Completion {\n readonly generation: Generation;\n readonly outcome: Outcome;\n}\n\ntype Request = Data.TaggedEnum<{\n Idle: {};\n Pending: {};\n Joined: {};\n Completed: { readonly completion: Completion };\n}>;\nconst Request = Data.taggedEnum<Request>();\n\nexport interface Progress {\n readonly generation: Generation;\n readonly request: Request;\n readonly lastRequest: Option.Option<RequestId>;\n}\n\nexport const initialProgress: Progress = {\n generation: Generation(0),\n request: Request.Idle(),\n lastRequest: Option.none(),\n};\n\nexport const start = (progress: Progress): Progress => ({\n ...progress,\n generation: Generation(progress.generation + 1),\n});\n\nexport const captureBarrier = (progress: Progress): Barrier =>\n Barrier(progress.generation);\n\nexport const request = (progress: Progress, id: RequestId): Progress => ({\n ...progress,\n request:\n Option.isSome(progress.lastRequest) && progress.lastRequest.value === id\n ? Request.Joined()\n : Request.Pending(),\n lastRequest: Option.some(id),\n});\n\nexport const complete = (\n progress: Progress,\n completion: Completion,\n): Progress => ({\n ...progress,\n request: Request.$match(progress.request, {\n Idle: (idle) => idle,\n Pending: () => Request.Completed({ completion }),\n Joined: () => Request.Completed({ completion }),\n Completed: (completed) => completed,\n }),\n});\n\nexport const getCompletion = (progress: Progress): Option.Option<Completion> =>\n Request.$match(progress.request, {\n Idle: () => Option.none(),\n Pending: () => Option.none(),\n Joined: () => Option.none(),\n Completed: ({ completion }) => Option.some(completion),\n });\n\nexport const joinedWithoutCompletion = (progress: Progress): boolean =>\n Request.$is(\"Joined\")(progress.request);\n\nexport const endRequest = (progress: Progress): Progress => ({\n ...progress,\n request: Request.Idle(),\n});\n\nexport const isFresh = (barrier: Barrier, completion: Completion): boolean =>\n completion.generation > barrier;\n\nexport const verifyInputs = Effect.fn(\"WatchBuild.verifyInputs\")(function* (\n snapshot: Snapshot,\n fs: FileSystem.FileSystem,\n crypto: Crypto.Crypto,\n) {\n const matches = yield* Effect.forEach(\n snapshot.fingerprints,\n ([file, expected]) =>\n fs.readFile(file).pipe(\n Effect.flatMap((contents) => crypto.digest(\"SHA-256\", contents)),\n Effect.map((digest) => Hex.encode(digest) === expected),\n Effect.mapError((cause) => Failure.InputCaptureFailed({ file, cause })),\n ),\n { concurrency: 8 },\n );\n return Array.every(matches, (matchesInput) => matchesInput);\n});\n\nexport type State = Data.TaggedEnum<{\n Initial: {};\n Succeeded: {\n readonly snapshot: Snapshot;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n };\n Failed: {\n readonly observation: Extract<Outcome, { readonly _tag: \"Failed\" }>;\n readonly lastSuccessful: Option.Option<Snapshot>;\n readonly discovered: HashSet.HashSet<string>;\n };\n}>;\nexport const State = Data.taggedEnum<State>();\n\nexport const lastSuccessful = (state: State): Option.Option<Snapshot> =>\n State.$match(state, {\n Initial: () => Option.none(),\n Succeeded: ({ snapshot }) => Option.some(snapshot),\n Failed: ({ lastSuccessful: snapshot }) => snapshot,\n });\n\nexport const observe = (previous: State, outcome: Outcome): State =>\n Outcome.$match(outcome, {\n Succeeded: ({ snapshot, warnings }) =>\n State.Succeeded({ snapshot, warnings }),\n Failed: (observation) =>\n State.Failed({\n observation,\n lastSuccessful: lastSuccessful(previous),\n discovered: State.$is(\"Initial\")(previous)\n ? HashSet.empty()\n : HashSet.union(\n State.$is(\"Failed\")(previous)\n ? previous.discovered\n : HashSet.empty(),\n HashSet.difference(\n HashSet.fromIterable(HashMap.keys(observation.fingerprints)),\n HashSet.fromIterable(HashMap.keys(fingerprints(previous))),\n ),\n ),\n }),\n });\n\nexport const fingerprints = (state: State): Fingerprints =>\n State.$match(state, {\n Initial: () => HashMap.empty(),\n Succeeded: ({ snapshot }) => snapshot.fingerprints,\n Failed: ({ observation }) => observation.fingerprints,\n });\n\nexport const outputHash = (state: State): Option.Option<string> =>\n State.$match(state, {\n Initial: () => Option.none(),\n Succeeded: ({ snapshot }) => Option.some(snapshot.outputHash),\n Failed: () => Option.none(),\n });\n\nexport type Change = Data.TaggedEnum<{\n Full: {};\n Affected: { readonly paths: HashSet.HashSet<string> };\n Recovered: { readonly paths: HashSet.HashSet<string> };\n}>;\nexport const Change = Data.taggedEnum<Change>();\n\nexport const diff = (\n previous: State,\n current: State,\n generatedDirectoryPrefix: string,\n): Change => {\n const previousFingerprints = fingerprints(previous);\n const currentFingerprints = fingerprints(current);\n const files = HashSet.union(\n HashSet.fromIterable(HashMap.keys(previousFingerprints)),\n HashSet.fromIterable(HashMap.keys(currentFingerprints)),\n );\n const changed = Array.fromIterable(files).filter(\n (file) =>\n !Equal.equals(\n HashMap.get(previousFingerprints, file),\n HashMap.get(currentFingerprints, file),\n ),\n );\n const sources = changed.filter(\n (file) => !file.startsWith(generatedDirectoryPrefix),\n );\n const recovered =\n State.$is(\"Failed\")(previous) && State.$is(\"Succeeded\")(current);\n const untrackedOutput = recovered\n ? Option.match(lastSuccessful(previous), {\n onNone: () => false,\n onSome: (baseline) =>\n Equal.equals(baseline.fingerprints, currentFingerprints) &&\n !Equal.equals(Option.some(baseline.outputHash), outputHash(current)),\n })\n : changed.length === 0 &&\n !Equal.equals(outputHash(previous), outputHash(current));\n if (\n State.$is(\"Initial\")(previous) ||\n (sources.some((file) => HashMap.has(previousFingerprints, file)) &&\n State.$is(\"Failed\")(current)) ||\n untrackedOutput\n )\n return Change.Full();\n\n const graph = Option.match(lastSuccessful(current), {\n onNone: () => HashMap.empty<string, HashSet.HashSet<string>>(),\n onSome: (snapshot) => snapshot.graph,\n });\n const previousGraph = Option.match(lastSuccessful(previous), {\n onNone: () => HashMap.empty<string, HashSet.HashSet<string>>(),\n onSome: (snapshot) => snapshot.graph,\n });\n const affectedSources =\n recovered && State.$is(\"Failed\")(previous)\n ? Array.fromIterable(\n HashSet.union(\n HashSet.fromIterable(sources),\n HashSet.filter(\n previous.discovered,\n (file) => !file.startsWith(generatedDirectoryPrefix),\n ),\n ),\n )\n : sources;\n const paths = HashSet.union(\n WatchGraph.affected(graph, affectedSources),\n WatchGraph.affected(previousGraph, affectedSources),\n );\n return recovered ? Change.Recovered({ paths }) : Change.Affected({ paths });\n};\n\nexport const parse = Effect.fn(\"WatchBuild.parse\")(function* (\n result: esbuild.BuildResult,\n capture: Capture,\n resolve: (file: string) => string,\n crypto: Crypto.Crypto,\n) {\n const failures = Array.fromIterable(HashMap.values(capture.failures));\n if (Array.isReadonlyArrayNonEmpty(result.errors))\n failures.push(Failure.BuildFailed({ errors: result.errors }));\n if (Array.isReadonlyArrayNonEmpty(failures))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures,\n warnings: result.warnings,\n });\n if (!result.metafile)\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [Failure.InvalidBuildResult({ missing: \"metafile\" })],\n warnings: result.warnings,\n });\n if (!result.outputFiles || !Array.isReadonlyArrayNonEmpty(result.outputFiles))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [Failure.InvalidBuildResult({ missing: \"outputFiles\" })],\n warnings: result.warnings,\n });\n const hashes = yield* Effect.forEach(result.outputFiles, (file) =>\n crypto.digest(\"SHA-256\", file.contents).pipe(\n Effect.map(Hex.encode),\n Effect.mapError((cause) =>\n Failure.OutputCaptureFailed({ file: file.path, cause }),\n ),\n ),\n ).pipe(Effect.result);\n if (Result.isFailure(hashes))\n return Outcome.Failed({\n fingerprints: capture.fingerprints,\n failures: [hashes.failure],\n warnings: result.warnings,\n });\n return Outcome.Succeeded({\n snapshot: {\n fingerprints: capture.fingerprints,\n graph: WatchGraph.make(result.metafile, resolve),\n outputHash: hashes.success.join(\":\"),\n },\n warnings: result.warnings,\n });\n});\n\nexport const failureMessage = (failure: Failure): string =>\n Failure.$match(failure, {\n BuildFailed: ({ errors }) => errors.map((error) => error.text).join(\"\\n\"),\n InputCaptureFailed: ({ file, cause }) =>\n `Could not fingerprint input ${file}: ${cause.message}`,\n OutputCaptureFailed: ({ file, cause }) =>\n `Could not fingerprint output ${file}: ${cause.message}`,\n InvalidBuildResult: ({ missing }) => `esbuild completed without ${missing}`,\n });\n\nexport const messages = (outcome: Outcome): ReadonlyArray<esbuild.Message> =>\n Outcome.$match(outcome, {\n Succeeded: () => [],\n Failed: ({ failures }) =>\n failures.flatMap((failure) =>\n Match.value(failure).pipe(\n Match.tag(\"BuildFailed\", ({ errors }) => errors),\n Match.tag(\n \"InputCaptureFailed\",\n \"OutputCaptureFailed\",\n \"InvalidBuildResult\",\n (captureFailure) => [\n {\n id: \"confect-watch\",\n pluginName: \"confect\",\n text: failureMessage(captureFailure),\n location: null,\n notes: [],\n detail: captureFailure,\n },\n ],\n ),\n Match.exhaustive,\n ),\n ),\n });\n\nexport const diagnostics = (\n state: State,\n): {\n readonly errors: ReadonlyArray<esbuild.Message>;\n readonly warnings: ReadonlyArray<esbuild.Message>;\n} =>\n State.$match(state, {\n Initial: () => ({ errors: [], warnings: [] }),\n Succeeded: ({ warnings }) => ({ errors: [], warnings }),\n Failed: ({ observation }) => ({\n errors: messages(observation),\n warnings: observation.warnings,\n }),\n });\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WatchTopology.mjs","names":["SyncState.full","SyncState.add","SyncState.empty"],"sources":["../src/WatchTopology.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport type { Kind } from \"./EntryPoint\";\nimport * as SyncState from \"./SyncState\";\n\ntype State = Data.TaggedEnum<{\n Reconciling: { readonly revision: number };\n Observing: {\n readonly revision: number;\n readonly initial: Deferred.Deferred<void>;\n };\n}>;\nconst State = Data.taggedEnum<State>();\n\ntype Decision = Data.TaggedEnum<{\n Take: { readonly pending: SyncState.Pending };\n Wait: { readonly ready: Effect.Effect<void> };\n}>;\nconst Decision = Data.taggedEnum<Decision>();\n\nexport const make = Effect.fn(\"WatchTopology.make\")(function* (\n pending: Ref.Ref<SyncState.Pending>,\n) {\n const state = yield* Ref.make<State>(State.Reconciling({ revision: 0 }));\n const changed = yield* Ref.make(yield* Deferred.make<void>());\n const lock = yield* Semaphore.make(1);\n const notify = Effect.gen(function* () {\n const previous = yield* Ref.getAndSet(\n changed,\n yield* Deferred.make<void>(),\n );\n yield* Deferred.succeed(previous, undefined);\n });\n\n const invalidate = Effect.fn(\"WatchTopology.invalidate\")(\n function* (update: (pending: SyncState.Pending) => SyncState.Pending) {\n yield* Ref.update(state, ({ revision }) =>\n State.Reconciling({ revision: revision + 1 }),\n );\n yield* Ref.update(pending, update);\n yield* notify;\n },\n (effect) => lock.withPermit(effect),\n );\n\n return {\n revision: Ref.get(state).pipe(Effect.map((current) => current.revision)),\n invalidate: invalidate(SyncState.full),\n invalidateEntry: (entry: Kind) =>\n invalidate((current) => SyncState.add(current, [entry])),\n reconcile: Effect.fn(\"WatchTopology.reconcile\")(\n function* (revision: number, initial: Deferred.Deferred<void>) {\n const current = yield* Ref.get(state);\n if (current.revision !== revision) return;\n yield* Ref.set(state, State.Observing({ revision, initial }));\n yield* notify;\n },\n (effect) => lock.withPermit(effect),\n ),\n takePending: lock\n .withPermit(\n Effect.gen(function* () {\n const current = yield* Ref.get(state);\n const notification = yield* Ref.get(changed);\n return yield* State.$match(current, {\n Reconciling: () =>\n Effect.succeed(\n Decision.Wait({ ready: Deferred.await(notification) }),\n ),\n Observing: ({ initial }) =>\n Effect.gen(function* () {\n if (yield* Deferred.isDone(initial))\n return Decision.Take({\n pending: yield* Ref.getAndSet(pending, SyncState.empty),\n });\n return Decision.Wait({\n ready: Effect.raceFirst(\n Deferred.await(notification),\n Deferred.await(initial),\n ),\n });\n }),\n });\n }),\n )\n .pipe(\n Effect.flatMap(\n Decision.$match({\n Take: ({ pending: taken }) => Effect.succeedSome(taken),\n Wait: ({ ready }) =>\n ready.pipe(Effect.as(Option.none<SyncState.Pending>())),\n }),\n ),\n Effect.repeat({ until: Option.isSome<SyncState.Pending> }),\n Effect.map((result) => result.value),\n ),\n };\n});\n\nexport type WatchTopology = Effect.Success<ReturnType<typeof make>>;\n"],"mappings":";;;;;;;;AAgBA,MAAM,QAAQ,KAAK,WAAkB;AAMrC,MAAM,WAAW,KAAK,WAAqB;AAE3C,MAAa,OAAO,OAAO,GAAG,oBAAoB,CAAC,CAAC,WAClD,SACA;CACA,MAAM,QAAQ,OAAO,IAAI,KAAY,MAAM,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;CACvE,MAAM,UAAU,OAAO,IAAI,KAAK,OAAO,SAAS,KAAW,CAAC;CAC5D,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;CACpC,MAAM,SAAS,OAAO,IAAI,aAAa;EACrC,MAAM,WAAW,OAAO,IAAI,UAC1B,SACA,OAAO,SAAS,KAAW,CAC7B;EACA,OAAO,SAAS,QAAQ,UAAU,KAAA,CAAS;CAC7C,CAAC;CAED,MAAM,aAAa,OAAO,GAAG,0BAA0B,CAAC,CACtD,WAAW,QAA2D;EACpE,OAAO,IAAI,OAAO,QAAQ,EAAE,eAC1B,MAAM,YAAY,EAAE,UAAU,WAAW,EAAE,CAAC,CAC9C;EACA,OAAO,IAAI,OAAO,SAAS,MAAM;EACjC,OAAO;CACT,IACC,WAAW,KAAK,WAAW,MAAM,CACpC;CAEA,OAAO;EACL,UAAU,IAAI,IAAI,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,YAAY,QAAQ,QAAQ,CAAC;EACvE,YAAY,WAAWA,IAAc;EACrC,kBAAkB,UAChB,YAAY,YAAYC,IAAc,SAAS,CAAC,KAAK,CAAC,CAAC;EACzD,WAAW,OAAO,GAAG,yBAAyB,CAAC,CAC7C,WAAW,UAAkB,SAAkC;GAE7D,KAAI,OADmB,IAAI,IAAI,KAAK,EAAA,CACxB,aAAa,UAAU;GACnC,OAAO,IAAI,IAAI,OAAO,MAAM,UAAU;IAAE;IAAU;GAAQ,CAAC,CAAC;GAC5D,OAAO;EACT,IACC,WAAW,KAAK,WAAW,MAAM,CACpC;EACA,aAAa,KACV,WACC,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,KAAK;GACpC,MAAM,eAAe,OAAO,IAAI,IAAI,OAAO;GAC3C,OAAO,OAAO,MAAM,OAAO,SAAS;IAClC,mBACE,OAAO,QACL,SAAS,KAAK,EAAE,OAAO,SAAS,MAAM,YAAY,EAAE,CAAC,CACvD;IACF,YAAY,EAAE,cACZ,OAAO,IAAI,aAAa;KACtB,IAAI,OAAO,SAAS,OAAO,OAAO,GAChC,OAAO,SAAS,KAAK,EACnB,SAAS,OAAO,IAAI,UAAU,SAASC,KAAe,EACxD,CAAC;KACH,OAAO,SAAS,KAAK,EACnB,OAAO,OAAO,UACZ,SAAS,MAAM,YAAY,GAC3B,SAAS,MAAM,OAAO,CACxB,EACF,CAAC;IACH,CAAC;GACL,CAAC;EACH,CAAC,CACH,CAAC,CACA,KACC,OAAO,QACL,SAAS,OAAO;GACd,OAAO,EAAE,SAAS,YAAY,OAAO,YAAY,KAAK;GACtD,OAAO,EAAE,YACP,MAAM,KAAK,OAAO,GAAG,OAAO,KAAwB,CAAC,CAAC;EAC1D,CAAC,CACH,GACA,OAAO,OAAO,EAAE,OAAO,OAAO,OAA0B,CAAC,GACzD,OAAO,KAAK,WAAW,OAAO,KAAK,CACrC;CACJ;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"WatchTopology.mjs","names":["SyncState.full","SyncState.add","SyncState.empty"],"sources":["../src/WatchTopology.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport type { Kind } from \"./EntryPoint.js\";\nimport * as SyncState from \"./SyncState.js\";\n\ntype State = Data.TaggedEnum<{\n Reconciling: { readonly revision: number };\n Observing: {\n readonly revision: number;\n readonly initial: Deferred.Deferred<void>;\n };\n}>;\nconst State = Data.taggedEnum<State>();\n\ntype Decision = Data.TaggedEnum<{\n Take: { readonly pending: SyncState.Pending };\n Wait: { readonly ready: Effect.Effect<void> };\n}>;\nconst Decision = Data.taggedEnum<Decision>();\n\nexport const make = Effect.fn(\"WatchTopology.make\")(function* (\n pending: Ref.Ref<SyncState.Pending>,\n) {\n const state = yield* Ref.make<State>(State.Reconciling({ revision: 0 }));\n const changed = yield* Ref.make(yield* Deferred.make<void>());\n const lock = yield* Semaphore.make(1);\n const notify = Effect.gen(function* () {\n const previous = yield* Ref.getAndSet(\n changed,\n yield* Deferred.make<void>(),\n );\n yield* Deferred.succeed(previous, undefined);\n });\n\n const invalidate = Effect.fn(\"WatchTopology.invalidate\")(\n function* (update: (pending: SyncState.Pending) => SyncState.Pending) {\n yield* Ref.update(state, ({ revision }) =>\n State.Reconciling({ revision: revision + 1 }),\n );\n yield* Ref.update(pending, update);\n yield* notify;\n },\n (effect) => lock.withPermit(effect),\n );\n\n return {\n revision: Ref.get(state).pipe(Effect.map((current) => current.revision)),\n invalidate: invalidate(SyncState.full),\n invalidateEntry: (entry: Kind) =>\n invalidate((current) => SyncState.add(current, [entry])),\n reconcile: Effect.fn(\"WatchTopology.reconcile\")(\n function* (revision: number, initial: Deferred.Deferred<void>) {\n const current = yield* Ref.get(state);\n if (current.revision !== revision) return;\n yield* Ref.set(state, State.Observing({ revision, initial }));\n yield* notify;\n },\n (effect) => lock.withPermit(effect),\n ),\n takePending: lock\n .withPermit(\n Effect.gen(function* () {\n const current = yield* Ref.get(state);\n const notification = yield* Ref.get(changed);\n return yield* State.$match(current, {\n Reconciling: () =>\n Effect.succeed(\n Decision.Wait({ ready: Deferred.await(notification) }),\n ),\n Observing: ({ initial }) =>\n Effect.gen(function* () {\n if (yield* Deferred.isDone(initial))\n return Decision.Take({\n pending: yield* Ref.getAndSet(pending, SyncState.empty),\n });\n return Decision.Wait({\n ready: Effect.raceFirst(\n Deferred.await(notification),\n Deferred.await(initial),\n ),\n });\n }),\n });\n }),\n )\n .pipe(\n Effect.flatMap(\n Decision.$match({\n Take: ({ pending: taken }) => Effect.succeedSome(taken),\n Wait: ({ ready }) =>\n ready.pipe(Effect.as(Option.none<SyncState.Pending>())),\n }),\n ),\n Effect.repeat({ until: Option.isSome<SyncState.Pending> }),\n Effect.map((result) => result.value),\n ),\n };\n});\n\nexport type WatchTopology = Effect.Success<ReturnType<typeof make>>;\n"],"mappings":";;;;;;;;AAgBA,MAAM,QAAQ,KAAK,WAAkB;AAMrC,MAAM,WAAW,KAAK,WAAqB;AAE3C,MAAa,OAAO,OAAO,GAAG,oBAAoB,CAAC,CAAC,WAClD,SACA;CACA,MAAM,QAAQ,OAAO,IAAI,KAAY,MAAM,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;CACvE,MAAM,UAAU,OAAO,IAAI,KAAK,OAAO,SAAS,KAAW,CAAC;CAC5D,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;CACpC,MAAM,SAAS,OAAO,IAAI,aAAa;EACrC,MAAM,WAAW,OAAO,IAAI,UAC1B,SACA,OAAO,SAAS,KAAW,CAC7B;EACA,OAAO,SAAS,QAAQ,UAAU,KAAA,CAAS;CAC7C,CAAC;CAED,MAAM,aAAa,OAAO,GAAG,0BAA0B,CAAC,CACtD,WAAW,QAA2D;EACpE,OAAO,IAAI,OAAO,QAAQ,EAAE,eAC1B,MAAM,YAAY,EAAE,UAAU,WAAW,EAAE,CAAC,CAC9C;EACA,OAAO,IAAI,OAAO,SAAS,MAAM;EACjC,OAAO;CACT,IACC,WAAW,KAAK,WAAW,MAAM,CACpC;CAEA,OAAO;EACL,UAAU,IAAI,IAAI,KAAK,CAAC,CAAC,KAAK,OAAO,KAAK,YAAY,QAAQ,QAAQ,CAAC;EACvE,YAAY,WAAWA,IAAc;EACrC,kBAAkB,UAChB,YAAY,YAAYC,IAAc,SAAS,CAAC,KAAK,CAAC,CAAC;EACzD,WAAW,OAAO,GAAG,yBAAyB,CAAC,CAC7C,WAAW,UAAkB,SAAkC;GAE7D,KAAI,OADmB,IAAI,IAAI,KAAK,EAAA,CACxB,aAAa,UAAU;GACnC,OAAO,IAAI,IAAI,OAAO,MAAM,UAAU;IAAE;IAAU;GAAQ,CAAC,CAAC;GAC5D,OAAO;EACT,IACC,WAAW,KAAK,WAAW,MAAM,CACpC;EACA,aAAa,KACV,WACC,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,KAAK;GACpC,MAAM,eAAe,OAAO,IAAI,IAAI,OAAO;GAC3C,OAAO,OAAO,MAAM,OAAO,SAAS;IAClC,mBACE,OAAO,QACL,SAAS,KAAK,EAAE,OAAO,SAAS,MAAM,YAAY,EAAE,CAAC,CACvD;IACF,YAAY,EAAE,cACZ,OAAO,IAAI,aAAa;KACtB,IAAI,OAAO,SAAS,OAAO,OAAO,GAChC,OAAO,SAAS,KAAK,EACnB,SAAS,OAAO,IAAI,UAAU,SAASC,KAAe,EACxD,CAAC;KACH,OAAO,SAAS,KAAK,EACnB,OAAO,OAAO,UACZ,SAAS,MAAM,YAAY,GAC3B,SAAS,MAAM,OAAO,CACxB,EACF,CAAC;IACH,CAAC;GACL,CAAC;EACH,CAAC,CACH,CAAC,CACA,KACC,OAAO,QACL,SAAS,OAAO;GACd,OAAO,EAAE,SAAS,YAAY,OAAO,YAAY,KAAK;GACtD,OAAO,EAAE,YACP,MAAM,KAAK,OAAO,GAAG,OAAO,KAAwB,CAAC,CAAC;EAC1D,CAAC,CACH,GACA,OAAO,OAAO,EAAE,OAAO,OAAO,OAA0B,CAAC,GACzD,OAAO,KAAK,WAAW,OAAO,KAAK,CACrC;CACJ;AACF,CAAC"}
|
package/dist/Watcher.mjs
CHANGED
|
@@ -193,6 +193,7 @@ const entryPointsWatcher = Effect.fnUntraced(function* (signal, pendingRef, rest
|
|
|
193
193
|
Empty: () => completedInitial,
|
|
194
194
|
Active: ({ initial: activeInitial }) => Effect.succeed(activeInitial)
|
|
195
195
|
});
|
|
196
|
+
yield* refreshCurrent(current).pipe(Effect.catchTag("WatcherOperationError", () => Ref.update(pendingRef, full)));
|
|
196
197
|
yield* watcher.topology.reconcile(revision, initial);
|
|
197
198
|
return;
|
|
198
199
|
}
|
|
@@ -263,59 +264,57 @@ const entryPointsWatcher = Effect.fnUntraced(function* (signal, pendingRef, rest
|
|
|
263
264
|
yield* sync;
|
|
264
265
|
return yield* Effect.forever(Queue.take(restartQueue).pipe(Effect.andThen(sync)));
|
|
265
266
|
}, Effect.provide(NodeCrypto.layer));
|
|
266
|
-
const
|
|
267
|
-
return yield*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
if (
|
|
293
|
-
const rejected = parseFailure(built.failure.cause);
|
|
294
|
-
if (Option.isNone(rejected)) return yield* built.failure;
|
|
295
|
-
if (joinedWithoutCompletion(currentProgress)) return Option.none();
|
|
296
|
-
if (Option.isNone(completed) || !Outcome.$is("Failed")(completed.value.outcome)) return yield* new OperationError({
|
|
297
|
-
operation: "rebuild",
|
|
298
|
-
cause: rejected.value.errors
|
|
299
|
-
});
|
|
300
|
-
}
|
|
267
|
+
const refreshCurrent = Effect.fnUntraced(function* (current) {
|
|
268
|
+
return yield* WatcherLifecycle.$match(current, {
|
|
269
|
+
Empty: () => Effect.succeed(RefreshOutcome.Empty()),
|
|
270
|
+
Uninitialized: () => Effect.fail(new OperationError({
|
|
271
|
+
operation: "refresh",
|
|
272
|
+
cause: "The dependency watcher is not initialized"
|
|
273
|
+
})),
|
|
274
|
+
Active: ({ context, identifyRequest, progress, verifyInputs }) => Effect.gen(function* () {
|
|
275
|
+
const runSync = Effect.runSyncWith(yield* Effect.context());
|
|
276
|
+
const barrier = captureBarrier(yield* Ref.get(progress));
|
|
277
|
+
const completed = yield* Effect.gen(function* () {
|
|
278
|
+
const built = yield* Effect.tryPromise({
|
|
279
|
+
try: () => {
|
|
280
|
+
const promise = context.rebuild();
|
|
281
|
+
runSync(Ref.update(progress, (currentProgress) => request(currentProgress, identifyRequest(promise))));
|
|
282
|
+
return promise;
|
|
283
|
+
},
|
|
284
|
+
catch: (cause) => new OperationError({
|
|
285
|
+
operation: "rebuild",
|
|
286
|
+
cause
|
|
287
|
+
})
|
|
288
|
+
}).pipe(Effect.result);
|
|
289
|
+
const currentProgress = yield* Ref.get(progress);
|
|
290
|
+
const completed = getCompletion(currentProgress);
|
|
291
|
+
if (Result.isFailure(built)) {
|
|
292
|
+
const rejected = parseFailure(built.failure.cause);
|
|
293
|
+
if (Option.isNone(rejected)) return yield* built.failure;
|
|
301
294
|
if (joinedWithoutCompletion(currentProgress)) return Option.none();
|
|
302
|
-
|
|
295
|
+
if (Option.isNone(completed) || !Outcome.$is("Failed")(completed.value.outcome)) return yield* new OperationError({
|
|
303
296
|
operation: "rebuild",
|
|
304
|
-
cause:
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
297
|
+
cause: rejected.value.errors
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (joinedWithoutCompletion(currentProgress)) return Option.none();
|
|
301
|
+
const observed = yield* Effect.fromOption(completed).pipe(Effect.mapError(() => new OperationError({
|
|
302
|
+
operation: "rebuild",
|
|
303
|
+
cause: "esbuild completed without a build observation"
|
|
304
|
+
})));
|
|
305
|
+
if (isFresh(barrier, observed) && Outcome.$is("Succeeded")(observed.outcome)) {
|
|
306
|
+
if (!(yield* verifyInputs(observed.outcome.snapshot).pipe(Effect.mapError(({ cause }) => new OperationError({
|
|
307
|
+
operation: "refresh",
|
|
308
|
+
cause
|
|
309
|
+
}))))) return Option.none();
|
|
310
|
+
}
|
|
311
|
+
return Option.some(observed);
|
|
312
|
+
}).pipe(Effect.ensuring(Ref.update(progress, endRequest))).pipe(Effect.repeat({ while: (observed) => Option.isNone(observed) || !isFresh(barrier, observed.value) }), Effect.flatMap((observed) => Effect.fromOption(observed).pipe(Effect.orDie)));
|
|
313
|
+
return RefreshOutcome.Observed({ outcome: completed.outcome });
|
|
314
|
+
})
|
|
315
|
+
});
|
|
318
316
|
});
|
|
317
|
+
const refresh = (watcher) => watcher.lock.withPermit(Ref.get(watcher.lifecycle).pipe(Effect.flatMap(refreshCurrent)));
|
|
319
318
|
const confectStructureWatcher = Effect.fnUntraced(function* (signal, pendingRef, restart, restartEntry) {
|
|
320
319
|
const fs = yield* FileSystem.FileSystem;
|
|
321
320
|
const path = yield* Path.Path;
|
package/dist/Watcher.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Watcher.mjs","names":["SyncState.empty","WatchTopology.make","RestoreWatcher.make","SyncState.full","WatchBuild.diagnostics","Bundler.bundleWorkspacePlugin","WatchBuild.start","WatchBuild.emptyCapture","WatchBuild.parse","WatchBuild.observe","WatchBuild.diff","SyncState.add","SyncState.reconcile","WatchBuild.complete","EntryPoint.discover","EntryPoint.isOptional","WatchBuild.initialProgress","WatchBuild.makeRequestId","WatchBuild.verifyInputs","WatchBuild.captureBarrier","WatchBuild.request","WatchBuild.getCompletion","WatchBuild.joinedWithoutCompletion","WatchBuild.isFresh","WatchBuild.endRequest","EntryPoint.parse"],"sources":["../src/Watcher.ts"],"sourcesContent":["import * as Clock from \"effect/Clock\";\nimport * as Duration from \"effect/Duration\";\nimport * as NodeCrypto from \"@effect/platform-node/NodeCrypto\";\nimport * as Crypto from \"effect/Crypto\";\nimport * as Hex from \"effect/encoding/Hex\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Data from \"effect/Data\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as Match from \"effect/Match\";\nimport * as Option from \"effect/Option\";\nimport * as Order from \"effect/Order\";\nimport * as Queue from \"effect/Queue\";\nimport * as Ref from \"effect/Ref\";\nimport * as Result from \"effect/Result\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Scope from \"effect/Scope\";\nimport * as Schema from \"effect/Schema\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as Stream from \"effect/Stream\";\nimport {\n externalPlugin,\n loadTsConfig,\n tsconfigPathsToRegExp,\n} from \"bundle-require\";\nimport * as esbuild from \"esbuild\";\nimport * as Bundler from \"./Bundler\";\nimport * as WatchBuild from \"./WatchBuild\";\nimport * as WatchTopology from \"./WatchTopology\";\nimport * as SyncState from \"./SyncState\";\nimport type { Pending } from \"./SyncState\";\nimport * as EntryPoint from \"./EntryPoint\";\nimport type { EntryPoint as Entry } from \"./EntryPoint\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\nimport { CONVEX_CONFIG_FILENAME } from \"./ConvexConfig\";\nimport { ConvexDirectory } from \"./ConvexDirectory\";\nimport { ProjectRoot } from \"./ProjectRoot\";\nimport * as RestoreWatcher from \"./RestoreWatcher\";\n\nconst COALESCE_QUIESCENCE = Duration.millis(300);\n\n// Upper bound on `drainUntilQuiescent` so a pathological infinite\n// signal stream can't pin the sync loop forever.\nconst COALESCE_MAX_WAIT = Duration.seconds(5);\n\ntype WatcherLifecycle = Data.TaggedEnum<{\n Uninitialized: {};\n Empty: {};\n Active: {\n readonly initial: Deferred.Deferred<void>;\n readonly key: string;\n readonly scope: Scope.Closeable;\n readonly entries: Array.NonEmptyReadonlyArray<Entry>;\n readonly context: esbuild.BuildContext;\n readonly identifyRequest: (\n promise: Promise<esbuild.BuildResult>,\n ) => WatchBuild.RequestId;\n readonly progress: Ref.Ref<WatchBuild.Progress>;\n readonly verifyInputs: (\n snapshot: WatchBuild.Snapshot,\n ) => ReturnType<typeof WatchBuild.verifyInputs>;\n };\n}>;\nconst WatcherLifecycle = Data.taggedEnum<WatcherLifecycle>();\n\ninterface State {\n readonly lifecycle: Ref.Ref<WatcherLifecycle>;\n readonly observation: Ref.Ref<WatchBuild.State>;\n readonly lock: Semaphore.Semaphore;\n readonly topology: WatchTopology.WatchTopology;\n // Whether the last discovery found `confect/app.ts`.\n readonly declared: Ref.Ref<boolean>;\n}\n\ntype RefreshOutcome = Data.TaggedEnum<{\n Empty: {};\n Observed: { readonly outcome: WatchBuild.Outcome };\n}>;\nconst RefreshOutcome = Data.taggedEnum<RefreshOutcome>();\n\nexport const isRefreshSuccessful = (outcome: RefreshOutcome): boolean =>\n RefreshOutcome.$match(outcome, {\n Empty: () => true,\n Observed: ({ outcome: observed }) =>\n WatchBuild.Outcome.$match(observed, {\n Succeeded: () => true,\n Failed: () => false,\n }),\n });\n\nexport class OperationError extends Schema.TaggedError<OperationError>()(\n \"WatcherOperationError\",\n {\n operation: Schema.Literals([\n \"create\",\n \"watch\",\n \"rebuild\",\n \"dispose\",\n \"refresh\",\n ]),\n cause: Schema.Unknown,\n },\n) {}\n\nconst ReportedBuildFailure = Schema.Struct({\n errors: Schema.NonEmptyArray(Schema.Struct({ text: Schema.String })),\n});\nexport const parseFailure = Schema.decodeUnknownOption(ReportedBuildFailure);\n\nexport const make = Effect.fnUntraced(function* () {\n const signal = yield* Queue.sliding<void>(1);\n const pendingRef = yield* Ref.make<Pending>(SyncState.empty);\n const restartQueue = yield* Queue.sliding<void>(1);\n const state: State = {\n lifecycle: yield* Ref.make<WatcherLifecycle>(\n WatcherLifecycle.Uninitialized(),\n ),\n observation: yield* Ref.make<WatchBuild.State>(WatchBuild.State.Initial()),\n lock: yield* Semaphore.make(1),\n topology: yield* WatchTopology.make(pendingRef),\n declared: yield* Ref.make(false),\n };\n const restartWith = (invalidation: Effect.Effect<void>) =>\n invalidation.pipe(\n Effect.andThen(Queue.offer(restartQueue, undefined)),\n Effect.andThen(Queue.offer(signal, undefined)),\n );\n const restart = restartWith(state.topology.invalidate);\n const restartEntry = (entry: EntryPoint.Kind) =>\n restartWith(state.topology.invalidateEntry(entry));\n const duringRestoreRef = yield* Ref.make<\n Effect.Effect<void, never, Scope.Scope>\n >(Effect.void);\n return {\n run: Effect.gen(function* () {\n const structure = yield* RestoreWatcher.make(\n confectStructureWatcher(signal, pendingRef, restart, restartEntry),\n );\n yield* Ref.set(duringRestoreRef, structure.duringRestore);\n yield* Effect.all(\n [\n Effect.scoped(\n entryPointsWatcher(signal, pendingRef, restartQueue, state),\n ),\n structure.failure,\n convexConfigStructureWatcher(restart, state.declared),\n ],\n { concurrency: \"unbounded\" },\n );\n }).pipe(Effect.scoped),\n duringRestore: Effect.flatten(Ref.get(duringRestoreRef)),\n // Restored output makes esbuild rebuild; observe that, then drop the work\n // it queued so the restore does not trigger another generation.\n discardRestoreEchoes: refresh(state).pipe(\n Effect.ignore,\n Effect.andThen(Ref.set(pendingRef, SyncState.empty)),\n ),\n refresh: refresh(state),\n next: Queue.take(signal).pipe(\n Effect.andThen(\n drainUntilQuiescent(signal, COALESCE_QUIESCENCE, COALESCE_MAX_WAIT),\n ),\n Effect.andThen(state.topology.takePending),\n Effect.onInterrupt(() => Queue.offer(signal, undefined)),\n ),\n pending: Ref.get(pendingRef),\n requestRetry: Ref.update(pendingRef, SyncState.full).pipe(\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.asVoid,\n ),\n diagnostics: Ref.get(state.observation).pipe(\n Effect.map(WatchBuild.diagnostics),\n ),\n } as const;\n});\n\nexport type Watcher = Effect.Success<ReturnType<typeof make>>;\n\nconst drainUntilQuiescent = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n quiescence: Duration.Duration,\n maxWait: Duration.Duration,\n) {\n const start = yield* Clock.currentTimeMillis;\n const maxMillis = Duration.toMillis(maxWait);\n const loop: Effect.Effect<void> = Effect.gen(function* () {\n yield* Effect.sleep(quiescence);\n const drained = yield* Queue.clear(signal);\n if (drained.length === 0) return;\n const now = yield* Clock.currentTimeMillis;\n if (now - start < maxMillis) {\n yield* loop;\n }\n });\n yield* loop;\n});\n\nconst esbuildOptions = (\n path: Path.Path,\n fs: FileSystem.FileSystem,\n crypto: Crypto.Crypto,\n projectRoot: string,\n generatedDirectory: string,\n entries: ReadonlyArray<Entry>,\n notExternal: ReadonlyArray<RegExp>,\n stateRef: Ref.Ref<WatchBuild.State>,\n captureRef: Ref.Ref<WatchBuild.Capture>,\n progressRef: Ref.Ref<WatchBuild.Progress>,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initial: Deferred.Deferred<void>,\n): esbuild.BuildOptions => {\n const encodeString = Schema.encodeSync(Schema.fromJsonString(Schema.String));\n return {\n absWorkingDir: projectRoot,\n stdin: {\n contents: entries\n .map((entry) => `import ${encodeString(entry.absolutePath)};`)\n .join(\"\\n\"),\n resolveDir: projectRoot,\n sourcefile: \"confect-watch.ts\",\n loader: \"ts\",\n },\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\",\n format: \"esm\",\n logLevel: \"silent\",\n plugins: [\n Bundler.bundleWorkspacePlugin(path, fs, notExternal),\n externalPlugin({ notExternal: [...notExternal] }),\n {\n name: \"notify-rebuild\",\n setup(build) {\n build.onStart(() =>\n Effect.runPromise(\n Effect.gen(function* () {\n yield* Ref.update(progressRef, WatchBuild.start);\n yield* Ref.set(captureRef, WatchBuild.emptyCapture);\n }).pipe(Effect.as(undefined)),\n ),\n );\n build.onLoad({ filter: /.*/ }, (args) =>\n Effect.runPromise(\n Effect.gen(function* () {\n const fingerprint = yield* fs.readFile(args.path).pipe(\n Effect.flatMap((contents) =>\n crypto.digest(\"SHA-256\", contents),\n ),\n Effect.map(Hex.encode),\n Effect.result,\n );\n yield* Ref.update(captureRef, (capture) =>\n Result.isSuccess(fingerprint)\n ? {\n ...capture,\n fingerprints: HashMap.set(\n capture.fingerprints,\n args.path,\n fingerprint.success,\n ),\n }\n : {\n ...capture,\n failures: HashMap.set(\n capture.failures,\n args.path,\n WatchBuild.Failure.InputCaptureFailed({\n file: args.path,\n cause: fingerprint.failure,\n }),\n ),\n },\n );\n }).pipe(Effect.as(undefined)),\n ),\n );\n build.onEnd((result) =>\n Effect.runPromise(\n Effect.gen(function* () {\n const { generation } = yield* Ref.get(progressRef);\n const previous = yield* Ref.get(stateRef);\n const outcome = yield* WatchBuild.parse(\n result,\n yield* Ref.get(captureRef),\n (file) => path.resolve(projectRoot, file),\n crypto,\n );\n const current = WatchBuild.observe(previous, outcome);\n yield* Ref.set(stateRef, current);\n const change = WatchBuild.diff(\n previous,\n current,\n generatedDirectory + path.sep,\n );\n yield* Ref.update(pendingRef, (pending) =>\n WatchBuild.Change.$match(change, {\n Full: () => SyncState.add(pending, entries, true),\n Affected: ({ paths }) =>\n SyncState.add(\n pending,\n entries.filter((entry) =>\n HashSet.has(paths, entry.inputPath),\n ),\n ),\n Recovered: ({ paths }) =>\n SyncState.reconcile(\n SyncState.add(\n pending,\n entries.filter((entry) =>\n HashSet.has(paths, entry.inputPath),\n ),\n ),\n ),\n }),\n );\n yield* Queue.offer(signal, undefined);\n yield* Ref.update(progressRef, (progress) =>\n WatchBuild.complete(progress, { generation, outcome }),\n );\n yield* Deferred.succeed(initial, undefined);\n }),\n ),\n );\n },\n },\n ],\n };\n};\n\nconst entryPointsWatcher = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n watcher: State,\n) {\n const parentScope = yield* Effect.scope;\n const path = yield* Path.Path;\n const fs = yield* FileSystem.FileSystem;\n const projectRoot = yield* ProjectRoot.get;\n const confectDirectory = yield* ConfectDirectory.get;\n const generatedDirectory = path.join(\n yield* fs.realPath(confectDirectory),\n \"_generated\",\n );\n const crypto = yield* Crypto.Crypto;\n const completedInitial = Deferred.make<void>().pipe(\n Effect.tap((initial) => Deferred.succeed(initial, undefined)),\n );\n\n const sync = Effect.gen(function* () {\n const revision = yield* watcher.topology.revision;\n const current = yield* Ref.get(watcher.lifecycle);\n const entries = yield* EntryPoint.discover;\n yield* Ref.set(watcher.declared, entries.some(EntryPoint.Kind.$is(\"App\")));\n const desiredPaths = HashSet.fromIterable(\n entries.map((entry) => entry.absolutePath),\n );\n const key = pipe(\n Array.fromIterable(desiredPaths),\n Array.sort(Order.String),\n Array.join(\"\\n\"),\n );\n const unchanged = WatcherLifecycle.$match(current, {\n Uninitialized: () => false,\n Empty: () => entries.length === 0,\n Active: ({ key: previousKey }) => previousKey === key,\n });\n if (unchanged) {\n const initial = yield* WatcherLifecycle.$match(current, {\n Uninitialized: () => completedInitial,\n Empty: () => completedInitial,\n Active: ({ initial: activeInitial }) => Effect.succeed(activeInitial),\n });\n yield* watcher.topology.reconcile(revision, initial);\n return;\n }\n yield* WatcherLifecycle.$match(current, {\n Uninitialized: () => Effect.void,\n Empty: () => Ref.update(pendingRef, SyncState.full),\n Active: ({ entries: previousEntries, scope }) =>\n Effect.gen(function* () {\n const removedOptional = previousEntries.filter(\n (entry) =>\n EntryPoint.isOptional(entry) &&\n !HashSet.has(desiredPaths, entry.absolutePath),\n );\n yield* Ref.update(pendingRef, (pending) =>\n SyncState.add(SyncState.full(pending), removedOptional),\n );\n yield* Scope.close(scope, Exit.void);\n }),\n });\n yield* Ref.set(watcher.lifecycle, WatcherLifecycle.Uninitialized());\n yield* Ref.set(watcher.observation, WatchBuild.State.Initial());\n if (!Array.isReadonlyArrayNonEmpty(entries)) {\n yield* Ref.set(watcher.lifecycle, WatcherLifecycle.Empty());\n yield* Ref.update(pendingRef, SyncState.full);\n yield* Queue.offer(signal, undefined);\n const initial = yield* completedInitial;\n yield* watcher.topology.reconcile(revision, initial);\n return;\n }\n\n const scope = yield* Scope.fork(parentScope, \"sequential\");\n const captureRef = yield* Ref.make(WatchBuild.emptyCapture);\n const progress = yield* Ref.make(WatchBuild.initialProgress);\n const initial = yield* Deferred.make<void>();\n const tsconfig = loadTsConfig(projectRoot);\n const notExternal = tsconfigPathsToRegExp(\n tsconfig?.data.compilerOptions?.paths ?? {},\n );\n const context = yield* Effect.acquireRelease(\n Effect.tryPromise({\n try: () =>\n esbuild.context(\n esbuildOptions(\n path,\n fs,\n crypto,\n projectRoot,\n generatedDirectory,\n entries,\n notExternal,\n watcher.observation,\n captureRef,\n progress,\n signal,\n pendingRef,\n initial,\n ),\n ),\n catch: (cause) => new OperationError({ operation: \"create\", cause }),\n }),\n (ctx) =>\n Effect.tryPromise({\n try: () => ctx.dispose(),\n catch: (cause) => new OperationError({ operation: \"dispose\", cause }),\n }).pipe(Effect.orDie),\n ).pipe(Scope.provide(scope));\n const requestIds = new WeakMap<\n Promise<esbuild.BuildResult>,\n WatchBuild.RequestId\n >();\n yield* Ref.set(\n watcher.lifecycle,\n WatcherLifecycle.Active({\n initial,\n key,\n scope,\n entries,\n context,\n identifyRequest: (promise) => {\n const existing = requestIds.get(promise);\n if (existing !== undefined) return existing;\n const id = WatchBuild.makeRequestId();\n requestIds.set(promise, id);\n return id;\n },\n progress,\n verifyInputs: (snapshot) =>\n WatchBuild.verifyInputs(snapshot, fs, crypto),\n }),\n );\n yield* Effect.tryPromise({\n try: () => context.watch(),\n catch: (cause) => new OperationError({ operation: \"watch\", cause }),\n });\n yield* watcher.topology.reconcile(revision, initial);\n }).pipe(Semaphore.withPermit(watcher.lock));\n\n yield* sync;\n return yield* Effect.forever(\n Queue.take(restartQueue).pipe(Effect.andThen(sync)),\n );\n}, Effect.provide(NodeCrypto.layer));\n\nconst refresh = Effect.fnUntraced(function* (watcher: State) {\n return yield* watcher.lock.withPermit(\n Effect.gen(function* () {\n const current = yield* Ref.get(watcher.lifecycle);\n return yield* WatcherLifecycle.$match(current, {\n Empty: () => Effect.succeed(RefreshOutcome.Empty()),\n Uninitialized: () =>\n Effect.fail(\n new OperationError({\n operation: \"refresh\",\n cause: \"The dependency watcher is not initialized\",\n }),\n ),\n Active: ({ context, identifyRequest, progress, verifyInputs }) =>\n Effect.gen(function* () {\n const runSync = Effect.runSyncWith(yield* Effect.context());\n const barrier = WatchBuild.captureBarrier(yield* Ref.get(progress));\n const rebuild = Effect.gen(function* () {\n const built = yield* Effect.tryPromise({\n try: () => {\n const promise = context.rebuild();\n runSync(\n Ref.update(progress, (currentProgress) =>\n WatchBuild.request(\n currentProgress,\n identifyRequest(promise),\n ),\n ),\n );\n return promise;\n },\n catch: (cause) =>\n new OperationError({ operation: \"rebuild\", cause }),\n }).pipe(Effect.result);\n const currentProgress = yield* Ref.get(progress);\n const completed = WatchBuild.getCompletion(currentProgress);\n if (Result.isFailure(built)) {\n const rejected = parseFailure(built.failure.cause);\n if (Option.isNone(rejected)) return yield* built.failure;\n if (WatchBuild.joinedWithoutCompletion(currentProgress))\n return Option.none<WatchBuild.Completion>();\n if (\n Option.isNone(completed) ||\n !WatchBuild.Outcome.$is(\"Failed\")(completed.value.outcome)\n )\n return yield* new OperationError({\n operation: \"rebuild\",\n cause: rejected.value.errors,\n });\n }\n if (WatchBuild.joinedWithoutCompletion(currentProgress))\n return Option.none<WatchBuild.Completion>();\n const observed = yield* Effect.fromOption(completed).pipe(\n Effect.mapError(\n () =>\n new OperationError({\n operation: \"rebuild\",\n cause: \"esbuild completed without a build observation\",\n }),\n ),\n );\n if (\n WatchBuild.isFresh(barrier, observed) &&\n WatchBuild.Outcome.$is(\"Succeeded\")(observed.outcome)\n ) {\n const unchanged = yield* verifyInputs(\n observed.outcome.snapshot,\n ).pipe(\n Effect.mapError(\n ({ cause }) =>\n new OperationError({ operation: \"refresh\", cause }),\n ),\n );\n if (!unchanged) return Option.none<WatchBuild.Completion>();\n }\n return Option.some(observed);\n }).pipe(\n Effect.ensuring(Ref.update(progress, WatchBuild.endRequest)),\n );\n const completed = yield* rebuild.pipe(\n Effect.repeat({\n while: (observed) =>\n Option.isNone(observed) ||\n !WatchBuild.isFresh(barrier, observed.value),\n }),\n Effect.flatMap((observed) =>\n Effect.fromOption(observed).pipe(Effect.orDie),\n ),\n );\n return RefreshOutcome.Observed({ outcome: completed.outcome });\n }),\n });\n }),\n );\n});\n\nconst confectStructureWatcher = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restart: Effect.Effect<unknown>,\n restartEntry: (entry: EntryPoint.Kind) => Effect.Effect<unknown>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const removedDirectory = Schema.Struct({\n code: Schema.Literal(\"ENOENT\"),\n syscall: Schema.Literal(\"scandir\"),\n path: Schema.String,\n });\n\n yield* pipe(\n fs.watch(confectDirectory, { recursive: true }),\n Stream.runForEach((event) =>\n handleConfectChange({\n relativePath: path.relative(\n confectDirectory,\n path.resolve(confectDirectory, event.path),\n ),\n eventTag: event._tag,\n signal,\n pendingRef,\n restart,\n restartEntry,\n }),\n ),\n Effect.retry({\n schedule: Schedule.spaced(\"10 millis\"),\n while: (error) =>\n Effect.gen(function* () {\n const removed = Schema.decodeUnknownOption(removedDirectory)(\n error.reason.cause,\n );\n if (Option.isNone(removed)) return false;\n const relative = path.relative(confectDirectory, removed.value.path);\n if (\n relative === \"\" ||\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n )\n return false;\n if (\n !(yield* fs\n .exists(confectDirectory)\n .pipe(Effect.orElseSucceed(() => false)))\n ) {\n return false;\n }\n yield* restart;\n return true;\n }),\n }),\n );\n});\n\n/**\n * Non-recursive `fs.watch` on the convex directory, reacting only to\n * `convex.config.ts`. Recursion is deliberately avoided: codegen (Confect's and\n * Convex's) writes into `convex/` and `convex/_generated/` constantly, and\n * reacting to those writes would loop. Create/Remove offers to `restartQueue`\n * so the entry-point watcher set picks up (or drops) the config's esbuild\n * watcher.\n */\nconst convexConfigStructureWatcher = Effect.fnUntraced(function* (\n restart: Effect.Effect<unknown>,\n declared: Ref.Ref<boolean>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* pipe(\n fs.watch(convexDirectory),\n Stream.runForEach((event) =>\n Effect.gen(function* () {\n // A declared backend's config is codegen output; reacting to its\n // writes would rerun codegen for nothing.\n if (\n path.relative(convexDirectory, event.path) !==\n CONVEX_CONFIG_FILENAME ||\n (yield* Ref.get(declared))\n )\n return;\n yield* Match.value(event).pipe(\n Match.tag(\"Update\", () => Effect.void),\n Match.tag(\"Create\", \"Remove\", () => restart),\n Match.exhaustive,\n );\n }),\n ),\n );\n});\n\nconst flipDirtyAndSignal = (\n pendingRef: Ref.Ref<Pending>,\n signal: Queue.Queue<void>,\n restart: Effect.Effect<unknown>,\n isLifecycleChange: boolean,\n) =>\n isLifecycleChange\n ? restart\n : Ref.update(pendingRef, SyncState.full).pipe(\n Effect.andThen(Queue.offer(signal, undefined)),\n );\n\nconst handleConfectChange = ({\n relativePath,\n eventTag,\n signal,\n pendingRef,\n restart,\n restartEntry,\n}: {\n relativePath: string;\n eventTag: \"Create\" | \"Update\" | \"Remove\";\n signal: Queue.Queue<void>;\n pendingRef: Ref.Ref<Pending>;\n restart: Effect.Effect<unknown>;\n restartEntry: (entry: EntryPoint.Kind) => Effect.Effect<unknown>;\n}) => {\n if (relativePath.split(/[/\\\\]/).includes(\"_generated\")) {\n return Effect.void;\n }\n\n if (!relativePath.endsWith(\".ts\")) {\n return Effect.void;\n }\n\n const isLifecycleChange = Match.value(eventTag).pipe(\n Match.when(\"Update\", () => false),\n Match.whenOr(\"Create\", \"Remove\", () => true),\n Match.exhaustive,\n );\n\n const entry = EntryPoint.parse(relativePath);\n if (Option.isSome(entry)) {\n return isLifecycleChange ? restartEntry(entry.value) : Effect.void;\n }\n\n // A stray `confect/schema.ts` (now codegen-owned at\n // `_generated/schema.ts`) shouldn't exist; flagging it here ensures the\n // next codegen pass surfaces the migration error\n // (`LegacySchemaFileError`) instead of silently ignoring the file.\n if (relativePath === \"schema.ts\") {\n return flipDirtyAndSignal(pendingRef, signal, restart, isLifecycleChange);\n }\n\n // Any other `.ts` under `confect/` (helpers like `tables/notes.ts`).\n // Updates to such files are handled by the esbuild watcher for whichever\n // entry point imports them—its onEnd flips the right dirty flag.\n // Creates are our safety net: when a previously-missing import is added,\n // esbuild may not have its parent directory on a poll path, so we\n // re-run codegen on Create here.\n return Match.value(eventTag).pipe(\n Match.when(\"Create\", () =>\n flipDirtyAndSignal(pendingRef, signal, restart, false),\n ),\n Match.whenOr(\"Update\", \"Remove\", () => Effect.void),\n Match.exhaustive,\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAM,sBAAsB,SAAS,OAAO,GAAG;AAI/C,MAAM,oBAAoB,SAAS,QAAQ,CAAC;AAoB5C,MAAM,mBAAmB,KAAK,WAA6B;AAe3D,MAAM,iBAAiB,KAAK,WAA2B;AAEvD,MAAa,uBAAuB,YAClC,eAAe,OAAO,SAAS;CAC7B,aAAa;CACb,WAAW,EAAE,SAAS,eAAA,QACD,OAAO,UAAU;EAClC,iBAAiB;EACjB,cAAc;CAChB,CAAC;AACL,CAAC;AAEH,IAAa,iBAAb,cAAoC,OAAO,YAA4B,CAAC,CACtE,yBACA;CACE,WAAW,OAAO,SAAS;EACzB;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,OAAO,OAAO;AAChB,CACF,CAAC,CAAC,CAAC;AAEH,MAAM,uBAAuB,OAAO,OAAO,EACzC,QAAQ,OAAO,cAAc,OAAO,OAAO,EAAE,MAAM,OAAO,OAAO,CAAC,CAAC,EACrE,CAAC;AACD,MAAa,eAAe,OAAO,oBAAoB,oBAAoB;AAE3E,MAAa,OAAO,OAAO,WAAW,aAAa;CACjD,MAAM,SAAS,OAAO,MAAM,QAAc,CAAC;CAC3C,MAAM,aAAa,OAAO,IAAI,KAAcA,KAAe;CAC3D,MAAM,eAAe,OAAO,MAAM,QAAc,CAAC;CACjD,MAAM,QAAe;EACnB,WAAW,OAAO,IAAI,KACpB,iBAAiB,cAAc,CACjC;EACA,aAAa,OAAO,IAAI,KAAA,MAAwC,QAAQ,CAAC;EACzE,MAAM,OAAO,UAAU,KAAK,CAAC;EAC7B,UAAU,OAAOC,OAAmB,UAAU;EAC9C,UAAU,OAAO,IAAI,KAAK,KAAK;CACjC;CACA,MAAM,eAAe,iBACnB,aAAa,KACX,OAAO,QAAQ,MAAM,MAAM,cAAc,KAAA,CAAS,CAAC,GACnD,OAAO,QAAQ,MAAM,MAAM,QAAQ,KAAA,CAAS,CAAC,CAC/C;CACF,MAAM,UAAU,YAAY,MAAM,SAAS,UAAU;CACrD,MAAM,gBAAgB,UACpB,YAAY,MAAM,SAAS,gBAAgB,KAAK,CAAC;CACnD,MAAM,mBAAmB,OAAO,IAAI,KAElC,OAAO,IAAI;CACb,OAAO;EACL,KAAK,OAAO,IAAI,aAAa;GAC3B,MAAM,YAAY,OAAOC,OACvB,wBAAwB,QAAQ,YAAY,SAAS,YAAY,CACnE;GACA,OAAO,IAAI,IAAI,kBAAkB,UAAU,aAAa;GACxD,OAAO,OAAO,IACZ;IACE,OAAO,OACL,mBAAmB,QAAQ,YAAY,cAAc,KAAK,CAC5D;IACA,UAAU;IACV,6BAA6B,SAAS,MAAM,QAAQ;GACtD,GACA,EAAE,aAAa,YAAY,CAC7B;EACF,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM;EACrB,eAAe,OAAO,QAAQ,IAAI,IAAI,gBAAgB,CAAC;EAGvD,sBAAsB,QAAQ,KAAK,CAAC,CAAC,KACnC,OAAO,QACP,OAAO,QAAQ,IAAI,IAAI,YAAYF,KAAe,CAAC,CACrD;EACA,SAAS,QAAQ,KAAK;EACtB,MAAM,MAAM,KAAK,MAAM,CAAC,CAAC,KACvB,OAAO,QACL,oBAAoB,QAAQ,qBAAqB,iBAAiB,CACpE,GACA,OAAO,QAAQ,MAAM,SAAS,WAAW,GACzC,OAAO,kBAAkB,MAAM,MAAM,QAAQ,KAAA,CAAS,CAAC,CACzD;EACA,SAAS,IAAI,IAAI,UAAU;EAC3B,cAAc,IAAI,OAAO,YAAYG,IAAc,CAAC,CAAC,KACnD,OAAO,QAAQ,MAAM,MAAM,QAAQ,KAAA,CAAS,CAAC,GAC7C,OAAO,MACT;EACA,aAAa,IAAI,IAAI,MAAM,WAAW,CAAC,CAAC,KACtC,OAAO,IAAIC,WAAsB,CACnC;CACF;AACF,CAAC;AAID,MAAM,sBAAsB,OAAO,WAAW,WAC5C,QACA,YACA,SACA;CACA,MAAM,QAAQ,OAAO,MAAM;CAC3B,MAAM,YAAY,SAAS,SAAS,OAAO;CAC3C,MAAM,OAA4B,OAAO,IAAI,aAAa;EACxD,OAAO,OAAO,MAAM,UAAU;EAE9B,KAAI,OADmB,MAAM,MAAM,MAAM,EAAA,CAC7B,WAAW,GAAG;EAE1B,KAAI,OADe,MAAM,qBACf,QAAQ,WAChB,OAAO;CAEX,CAAC;CACD,OAAO;AACT,CAAC;AAED,MAAM,kBACJ,MACA,IACA,QACA,aACA,oBACA,SACA,aACA,UACA,YACA,aACA,QACA,YACA,YACyB;CACzB,MAAM,eAAe,OAAO,WAAW,OAAO,eAAe,OAAO,MAAM,CAAC;CAC3E,OAAO;EACL,eAAe;EACf,OAAO;GACL,UAAU,QACP,KAAK,UAAU,UAAU,aAAa,MAAM,YAAY,EAAE,EAAE,CAAC,CAC7D,KAAK,IAAI;GACZ,YAAY;GACZ,YAAY;GACZ,QAAQ;EACV;EACA,QAAQ;EACR,OAAO;EACP,UAAU;EACV,UAAU;EACV,QAAQ;EACR,UAAU;EACV,SAAS;GACPC,sBAA8B,MAAM,IAAI,WAAW;GACnD,eAAe,EAAE,aAAa,CAAC,GAAG,WAAW,EAAE,CAAC;GAChD;IACE,MAAM;IACN,MAAM,OAAO;KACX,MAAM,cACJ,OAAO,WACL,OAAO,IAAI,aAAa;MACtB,OAAO,IAAI,OAAO,aAAaC,KAAgB;MAC/C,OAAO,IAAI,IAAI,YAAYC,YAAuB;KACpD,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,KAAA,CAAS,CAAC,CAC9B,CACF;KACA,MAAM,OAAO,EAAE,QAAQ,KAAK,IAAI,SAC9B,OAAO,WACL,OAAO,IAAI,aAAa;MACtB,MAAM,cAAc,OAAO,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,KAChD,OAAO,SAAS,aACd,OAAO,OAAO,WAAW,QAAQ,CACnC,GACA,OAAO,IAAI,IAAI,MAAM,GACrB,OAAO,MACT;MACA,OAAO,IAAI,OAAO,aAAa,YAC7B,OAAO,UAAU,WAAW,IACxB;OACE,GAAG;OACH,cAAc,QAAQ,IACpB,QAAQ,cACR,KAAK,MACL,YAAY,OACd;MACF,IACA;OACE,GAAG;OACH,UAAU,QAAQ,IAChB,QAAQ,UACR,KAAK,MAAA,QACc,mBAAmB;QACpC,MAAM,KAAK;QACX,OAAO,YAAY;OACrB,CAAC,CACH;MACF,CACN;KACF,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,KAAA,CAAS,CAAC,CAC9B,CACF;KACA,MAAM,OAAO,WACX,OAAO,WACL,OAAO,IAAI,aAAa;MACtB,MAAM,EAAE,eAAe,OAAO,IAAI,IAAI,WAAW;MACjD,MAAM,WAAW,OAAO,IAAI,IAAI,QAAQ;MACxC,MAAM,UAAU,OAAOC,QACrB,QACA,OAAO,IAAI,IAAI,UAAU,IACxB,SAAS,KAAK,QAAQ,aAAa,IAAI,GACxC,MACF;MACA,MAAM,UAAUC,QAAmB,UAAU,OAAO;MACpD,OAAO,IAAI,IAAI,UAAU,OAAO;MAChC,MAAM,SAASC,KACb,UACA,SACA,qBAAqB,KAAK,GAC5B;MACA,OAAO,IAAI,OAAO,aAAa,YAAA,OACX,OAAO,QAAQ;OAC/B,YAAYC,IAAc,SAAS,SAAS,IAAI;OAChD,WAAW,EAAE,YACXA,IACE,SACA,QAAQ,QAAQ,UACd,QAAQ,IAAI,OAAO,MAAM,SAAS,CACpC,CACF;OACF,YAAY,EAAE,YACZC,UACED,IACE,SACA,QAAQ,QAAQ,UACd,QAAQ,IAAI,OAAO,MAAM,SAAS,CACpC,CACF,CACF;MACJ,CAAC,CACH;MACA,OAAO,MAAM,MAAM,QAAQ,KAAA,CAAS;MACpC,OAAO,IAAI,OAAO,cAAc,aAC9BE,SAAoB,UAAU;OAAE;OAAY;MAAQ,CAAC,CACvD;MACA,OAAO,SAAS,QAAQ,SAAS,KAAA,CAAS;KAC5C,CAAC,CACH,CACF;IACF;GACF;EACF;CACF;AACF;AAEA,MAAM,qBAAqB,OAAO,WAAW,WAC3C,QACA,YACA,cACA,SACA;CACA,MAAM,cAAc,OAAO,OAAO;CAClC,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,cAAc,OAAO,YAAY;CACvC,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,qBAAqB,KAAK,KAC9B,OAAO,GAAG,SAAS,gBAAgB,GACnC,YACF;CACA,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,mBAAmB,SAAS,KAAW,CAAC,CAAC,KAC7C,OAAO,KAAK,YAAY,SAAS,QAAQ,SAAS,KAAA,CAAS,CAAC,CAC9D;CAEA,MAAM,OAAO,OAAO,IAAI,aAAa;EACnC,MAAM,WAAW,OAAO,QAAQ,SAAS;EACzC,MAAM,UAAU,OAAO,IAAI,IAAI,QAAQ,SAAS;EAChD,MAAM,UAAU,OAAOC;EACvB,OAAO,IAAI,IAAI,QAAQ,UAAU,QAAQ,KAAA,KAAqB,IAAI,KAAK,CAAC,CAAC;EACzE,MAAM,eAAe,QAAQ,aAC3B,QAAQ,KAAK,UAAU,MAAM,YAAY,CAC3C;EACA,MAAM,MAAM,KACV,MAAM,aAAa,YAAY,GAC/B,MAAM,KAAK,MAAM,MAAM,GACvB,MAAM,KAAK,IAAI,CACjB;EAMA,IALkB,iBAAiB,OAAO,SAAS;GACjD,qBAAqB;GACrB,aAAa,QAAQ,WAAW;GAChC,SAAS,EAAE,KAAK,kBAAkB,gBAAgB;EACpD,CACY,GAAG;GACb,MAAM,UAAU,OAAO,iBAAiB,OAAO,SAAS;IACtD,qBAAqB;IACrB,aAAa;IACb,SAAS,EAAE,SAAS,oBAAoB,OAAO,QAAQ,aAAa;GACtE,CAAC;GACD,OAAO,QAAQ,SAAS,UAAU,UAAU,OAAO;GACnD;EACF;EACA,OAAO,iBAAiB,OAAO,SAAS;GACtC,qBAAqB,OAAO;GAC5B,aAAa,IAAI,OAAO,YAAYX,IAAc;GAClD,SAAS,EAAE,SAAS,iBAAiB,YACnC,OAAO,IAAI,aAAa;IACtB,MAAM,kBAAkB,gBAAgB,QACrC,UACCY,WAAsB,KAAK,KAC3B,CAAC,QAAQ,IAAI,cAAc,MAAM,YAAY,CACjD;IACA,OAAO,IAAI,OAAO,aAAa,YAC7BJ,IAAcR,KAAe,OAAO,GAAG,eAAe,CACxD;IACA,OAAO,MAAM,MAAM,OAAO,KAAK,IAAI;GACrC,CAAC;EACL,CAAC;EACD,OAAO,IAAI,IAAI,QAAQ,WAAW,iBAAiB,cAAc,CAAC;EAClE,OAAO,IAAI,IAAI,QAAQ,aAAA,MAA8B,QAAQ,CAAC;EAC9D,IAAI,CAAC,MAAM,wBAAwB,OAAO,GAAG;GAC3C,OAAO,IAAI,IAAI,QAAQ,WAAW,iBAAiB,MAAM,CAAC;GAC1D,OAAO,IAAI,OAAO,YAAYA,IAAc;GAC5C,OAAO,MAAM,MAAM,QAAQ,KAAA,CAAS;GACpC,MAAM,UAAU,OAAO;GACvB,OAAO,QAAQ,SAAS,UAAU,UAAU,OAAO;GACnD;EACF;EAEA,MAAM,QAAQ,OAAO,MAAM,KAAK,aAAa,YAAY;EACzD,MAAM,aAAa,OAAO,IAAI,KAAKI,YAAuB;EAC1D,MAAM,WAAW,OAAO,IAAI,KAAKS,eAA0B;EAC3D,MAAM,UAAU,OAAO,SAAS,KAAW;EAC3C,MAAM,WAAW,aAAa,WAAW;EACzC,MAAM,cAAc,sBAClB,UAAU,KAAK,iBAAiB,SAAS,CAAC,CAC5C;EACA,MAAM,UAAU,OAAO,OAAO,eAC5B,OAAO,WAAW;GAChB,WACE,QAAQ,QACN,eACE,MACA,IACA,QACA,aACA,oBACA,SACA,aACA,QAAQ,aACR,YACA,UACA,QACA,YACA,OACF,CACF;GACF,QAAQ,UAAU,IAAI,eAAe;IAAE,WAAW;IAAU;GAAM,CAAC;EACrE,CAAC,IACA,QACC,OAAO,WAAW;GAChB,WAAW,IAAI,QAAQ;GACvB,QAAQ,UAAU,IAAI,eAAe;IAAE,WAAW;IAAW;GAAM,CAAC;EACtE,CAAC,CAAC,CAAC,KAAK,OAAO,KAAK,CACxB,CAAC,CAAC,KAAK,MAAM,QAAQ,KAAK,CAAC;EAC3B,MAAM,6BAAa,IAAI,QAGrB;EACF,OAAO,IAAI,IACT,QAAQ,WACR,iBAAiB,OAAO;GACtB;GACA;GACA;GACA;GACA;GACA,kBAAkB,YAAY;IAC5B,MAAM,WAAW,WAAW,IAAI,OAAO;IACvC,IAAI,aAAa,KAAA,GAAW,OAAO;IACnC,MAAM,KAAKC,cAAyB;IACpC,WAAW,IAAI,SAAS,EAAE;IAC1B,OAAO;GACT;GACA;GACA,eAAe,aACbC,aAAwB,UAAU,IAAI,MAAM;EAChD,CAAC,CACH;EACA,OAAO,OAAO,WAAW;GACvB,WAAW,QAAQ,MAAM;GACzB,QAAQ,UAAU,IAAI,eAAe;IAAE,WAAW;IAAS;GAAM,CAAC;EACpE,CAAC;EACD,OAAO,QAAQ,SAAS,UAAU,UAAU,OAAO;CACrD,CAAC,CAAC,CAAC,KAAK,UAAU,WAAW,QAAQ,IAAI,CAAC;CAE1C,OAAO;CACP,OAAO,OAAO,OAAO,QACnB,MAAM,KAAK,YAAY,CAAC,CAAC,KAAK,OAAO,QAAQ,IAAI,CAAC,CACpD;AACF,GAAG,OAAO,QAAQ,WAAW,KAAK,CAAC;AAEnC,MAAM,UAAU,OAAO,WAAW,WAAW,SAAgB;CAC3D,OAAO,OAAO,QAAQ,KAAK,WACzB,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,IAAI,QAAQ,SAAS;EAChD,OAAO,OAAO,iBAAiB,OAAO,SAAS;GAC7C,aAAa,OAAO,QAAQ,eAAe,MAAM,CAAC;GAClD,qBACE,OAAO,KACL,IAAI,eAAe;IACjB,WAAW;IACX,OAAO;GACT,CAAC,CACH;GACF,SAAS,EAAE,SAAS,iBAAiB,UAAU,mBAC7C,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,YAAY,OAAO,OAAO,QAAQ,CAAC;IAC1D,MAAM,UAAUC,eAA0B,OAAO,IAAI,IAAI,QAAQ,CAAC;IA+DlE,MAAM,YAAY,OA9DF,OAAO,IAAI,aAAa;KACtC,MAAM,QAAQ,OAAO,OAAO,WAAW;MACrC,WAAW;OACT,MAAM,UAAU,QAAQ,QAAQ;OAChC,QACE,IAAI,OAAO,WAAW,oBACpBC,QACE,iBACA,gBAAgB,OAAO,CACzB,CACF,CACF;OACA,OAAO;MACT;MACA,QAAQ,UACN,IAAI,eAAe;OAAE,WAAW;OAAW;MAAM,CAAC;KACtD,CAAC,CAAC,CAAC,KAAK,OAAO,MAAM;KACrB,MAAM,kBAAkB,OAAO,IAAI,IAAI,QAAQ;KAC/C,MAAM,YAAYC,cAAyB,eAAe;KAC1D,IAAI,OAAO,UAAU,KAAK,GAAG;MAC3B,MAAM,WAAW,aAAa,MAAM,QAAQ,KAAK;MACjD,IAAI,OAAO,OAAO,QAAQ,GAAG,OAAO,OAAO,MAAM;MACjD,IAAIC,wBAAmC,eAAe,GACpD,OAAO,OAAO,KAA4B;MAC5C,IACE,OAAO,OAAO,SAAS,KACvB,CAAA,QAAoB,IAAI,QAAQ,CAAC,CAAC,UAAU,MAAM,OAAO,GAEzD,OAAO,OAAO,IAAI,eAAe;OAC/B,WAAW;OACX,OAAO,SAAS,MAAM;MACxB,CAAC;KACL;KACA,IAAIA,wBAAmC,eAAe,GACpD,OAAO,OAAO,KAA4B;KAC5C,MAAM,WAAW,OAAO,OAAO,WAAW,SAAS,CAAC,CAAC,KACnD,OAAO,eAEH,IAAI,eAAe;MACjB,WAAW;MACX,OAAO;KACT,CAAC,CACL,CACF;KACA,IACEC,QAAmB,SAAS,QAAQ,KAAA,QACjB,IAAI,WAAW,CAAC,CAAC,SAAS,OAAO,GAUhD;UAAA,EAAC,OARoB,aACvB,SAAS,QAAQ,QACnB,CAAC,CAAC,KACA,OAAO,UACJ,EAAE,YACD,IAAI,eAAe;OAAE,WAAW;OAAW;MAAM,CAAC,CACtD,CACF,IACgB,OAAO,OAAO,KAA4B;KAAA;KAE5D,OAAO,OAAO,KAAK,QAAQ;IAC7B,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,IAAI,OAAO,UAAUC,UAAqB,CAAC,CAE9B,CAAC,CAAC,KAC/B,OAAO,OAAO,EACZ,QAAQ,aACN,OAAO,OAAO,QAAQ,KACtB,CAACD,QAAmB,SAAS,SAAS,KAAK,EAC/C,CAAC,GACD,OAAO,SAAS,aACd,OAAO,WAAW,QAAQ,CAAC,CAAC,KAAK,OAAO,KAAK,CAC/C,CACF;IACA,OAAO,eAAe,SAAS,EAAE,SAAS,UAAU,QAAQ,CAAC;GAC/D,CAAC;EACL,CAAC;CACH,CAAC,CACH;AACF,CAAC;AAED,MAAM,0BAA0B,OAAO,WAAW,WAChD,QACA,YACA,SACA,cACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,mBAAmB,OAAO,OAAO;EACrC,MAAM,OAAO,QAAQ,QAAQ;EAC7B,SAAS,OAAO,QAAQ,SAAS;EACjC,MAAM,OAAO;CACf,CAAC;CAED,OAAO,KACL,GAAG,MAAM,kBAAkB,EAAE,WAAW,KAAK,CAAC,GAC9C,OAAO,YAAY,UACjB,oBAAoB;EAClB,cAAc,KAAK,SACjB,kBACA,KAAK,QAAQ,kBAAkB,MAAM,IAAI,CAC3C;EACA,UAAU,MAAM;EAChB;EACA;EACA;EACA;CACF,CAAC,CACH,GACA,OAAO,MAAM;EACX,UAAU,SAAS,OAAO,WAAW;EACrC,QAAQ,UACN,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,oBAAoB,gBAAgB,CAAC,CAC1D,MAAM,OAAO,KACf;GACA,IAAI,OAAO,OAAO,OAAO,GAAG,OAAO;GACnC,MAAM,WAAW,KAAK,SAAS,kBAAkB,QAAQ,MAAM,IAAI;GACnE,IACE,aAAa,MACb,aAAa,QACb,SAAS,WAAW,KAAK,KAAK,KAAK,KACnC,KAAK,WAAW,QAAQ,GAExB,OAAO;GACT,IACE,EAAE,OAAO,GACN,OAAO,gBAAgB,CAAC,CACxB,KAAK,OAAO,oBAAoB,KAAK,CAAC,IAEzC,OAAO;GAET,OAAO;GACP,OAAO;EACT,CAAC;CACL,CAAC,CACH;AACF,CAAC;;;;;;;;;AAUD,MAAM,+BAA+B,OAAO,WAAW,WACrD,SACA,UACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAE/C,OAAO,KACL,GAAG,MAAM,eAAe,GACxB,OAAO,YAAY,UACjB,OAAO,IAAI,aAAa;EAGtB,IACE,KAAK,SAAS,iBAAiB,MAAM,IAAI,MAAA,uBAExC,OAAO,IAAI,IAAI,QAAQ,IAExB;EACF,OAAO,MAAM,MAAM,KAAK,CAAC,CAAC,KACxB,MAAM,IAAI,gBAAgB,OAAO,IAAI,GACrC,MAAM,IAAI,UAAU,gBAAgB,OAAO,GAC3C,MAAM,UACR;CACF,CAAC,CACH,CACF;AACF,CAAC;AAED,MAAM,sBACJ,YACA,QACA,SACA,sBAEA,oBACI,UACA,IAAI,OAAO,YAAYpB,IAAc,CAAC,CAAC,KACrC,OAAO,QAAQ,MAAM,MAAM,QAAQ,KAAA,CAAS,CAAC,CAC/C;AAEN,MAAM,uBAAuB,EAC3B,cACA,UACA,QACA,YACA,SACA,mBAQI;CACJ,IAAI,aAAa,MAAM,OAAO,CAAC,CAAC,SAAS,YAAY,GACnD,OAAO,OAAO;CAGhB,IAAI,CAAC,aAAa,SAAS,KAAK,GAC9B,OAAO,OAAO;CAGhB,MAAM,oBAAoB,MAAM,MAAM,QAAQ,CAAC,CAAC,KAC9C,MAAM,KAAK,gBAAgB,KAAK,GAChC,MAAM,OAAO,UAAU,gBAAgB,IAAI,GAC3C,MAAM,UACR;CAEA,MAAM,QAAQsB,MAAiB,YAAY;CAC3C,IAAI,OAAO,OAAO,KAAK,GACrB,OAAO,oBAAoB,aAAa,MAAM,KAAK,IAAI,OAAO;CAOhE,IAAI,iBAAiB,aACnB,OAAO,mBAAmB,YAAY,QAAQ,SAAS,iBAAiB;CAS1E,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC,KAC3B,MAAM,KAAK,gBACT,mBAAmB,YAAY,QAAQ,SAAS,KAAK,CACvD,GACA,MAAM,OAAO,UAAU,gBAAgB,OAAO,IAAI,GAClD,MAAM,UACR;AACF"}
|
|
1
|
+
{"version":3,"file":"Watcher.mjs","names":["SyncState.empty","WatchTopology.make","RestoreWatcher.make","SyncState.full","WatchBuild.diagnostics","Bundler.bundleWorkspacePlugin","WatchBuild.start","WatchBuild.emptyCapture","WatchBuild.parse","WatchBuild.observe","WatchBuild.diff","SyncState.add","SyncState.reconcile","WatchBuild.complete","EntryPoint.discover","EntryPoint.isOptional","WatchBuild.initialProgress","WatchBuild.makeRequestId","WatchBuild.verifyInputs","WatchBuild.captureBarrier","WatchBuild.request","WatchBuild.getCompletion","WatchBuild.joinedWithoutCompletion","WatchBuild.isFresh","WatchBuild.endRequest","EntryPoint.parse"],"sources":["../src/Watcher.ts"],"sourcesContent":["import * as Clock from \"effect/Clock\";\nimport * as Duration from \"effect/Duration\";\nimport * as NodeCrypto from \"@effect/platform-node/NodeCrypto\";\nimport * as Crypto from \"effect/Crypto\";\nimport * as Hex from \"effect/encoding/Hex\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Data from \"effect/Data\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as Match from \"effect/Match\";\nimport * as Option from \"effect/Option\";\nimport * as Order from \"effect/Order\";\nimport * as Queue from \"effect/Queue\";\nimport * as Ref from \"effect/Ref\";\nimport * as Result from \"effect/Result\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Scope from \"effect/Scope\";\nimport * as Schema from \"effect/Schema\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as Stream from \"effect/Stream\";\nimport {\n externalPlugin,\n loadTsConfig,\n tsconfigPathsToRegExp,\n} from \"bundle-require\";\nimport * as esbuild from \"esbuild\";\nimport * as Bundler from \"./Bundler.js\";\nimport * as WatchBuild from \"./WatchBuild.js\";\nimport * as WatchTopology from \"./WatchTopology.js\";\nimport * as SyncState from \"./SyncState.js\";\nimport type { Pending } from \"./SyncState.js\";\nimport * as EntryPoint from \"./EntryPoint.js\";\nimport type { EntryPoint as Entry } from \"./EntryPoint.js\";\nimport { ConfectDirectory } from \"./ConfectDirectory.js\";\nimport { CONVEX_CONFIG_FILENAME } from \"./ConvexConfig.js\";\nimport { ConvexDirectory } from \"./ConvexDirectory.js\";\nimport { ProjectRoot } from \"./ProjectRoot.js\";\nimport * as RestoreWatcher from \"./RestoreWatcher.js\";\n\nconst COALESCE_QUIESCENCE = Duration.millis(300);\n\n// Upper bound on `drainUntilQuiescent` so a pathological infinite\n// signal stream can't pin the sync loop forever.\nconst COALESCE_MAX_WAIT = Duration.seconds(5);\n\ntype WatcherLifecycle = Data.TaggedEnum<{\n Uninitialized: {};\n Empty: {};\n Active: {\n readonly initial: Deferred.Deferred<void>;\n readonly key: string;\n readonly scope: Scope.Closeable;\n readonly entries: Array.NonEmptyReadonlyArray<Entry>;\n readonly context: esbuild.BuildContext;\n readonly identifyRequest: (\n promise: Promise<esbuild.BuildResult>,\n ) => WatchBuild.RequestId;\n readonly progress: Ref.Ref<WatchBuild.Progress>;\n readonly verifyInputs: (\n snapshot: WatchBuild.Snapshot,\n ) => ReturnType<typeof WatchBuild.verifyInputs>;\n };\n}>;\nconst WatcherLifecycle = Data.taggedEnum<WatcherLifecycle>();\n\ninterface State {\n readonly lifecycle: Ref.Ref<WatcherLifecycle>;\n readonly observation: Ref.Ref<WatchBuild.State>;\n readonly lock: Semaphore.Semaphore;\n readonly topology: WatchTopology.WatchTopology;\n // Whether the last discovery found `confect/app.ts`.\n readonly declared: Ref.Ref<boolean>;\n}\n\ntype RefreshOutcome = Data.TaggedEnum<{\n Empty: {};\n Observed: { readonly outcome: WatchBuild.Outcome };\n}>;\nconst RefreshOutcome = Data.taggedEnum<RefreshOutcome>();\n\nexport const isRefreshSuccessful = (outcome: RefreshOutcome): boolean =>\n RefreshOutcome.$match(outcome, {\n Empty: () => true,\n Observed: ({ outcome: observed }) =>\n WatchBuild.Outcome.$match(observed, {\n Succeeded: () => true,\n Failed: () => false,\n }),\n });\n\nexport class OperationError extends Schema.TaggedError<OperationError>()(\n \"WatcherOperationError\",\n {\n operation: Schema.Literals([\n \"create\",\n \"watch\",\n \"rebuild\",\n \"dispose\",\n \"refresh\",\n ]),\n cause: Schema.Unknown,\n },\n) {}\n\nconst ReportedBuildFailure = Schema.Struct({\n errors: Schema.NonEmptyArray(Schema.Struct({ text: Schema.String })),\n});\nexport const parseFailure = Schema.decodeUnknownOption(ReportedBuildFailure);\n\nexport const make = Effect.fnUntraced(function* () {\n const signal = yield* Queue.sliding<void>(1);\n const pendingRef = yield* Ref.make<Pending>(SyncState.empty);\n const restartQueue = yield* Queue.sliding<void>(1);\n const state: State = {\n lifecycle: yield* Ref.make<WatcherLifecycle>(\n WatcherLifecycle.Uninitialized(),\n ),\n observation: yield* Ref.make<WatchBuild.State>(WatchBuild.State.Initial()),\n lock: yield* Semaphore.make(1),\n topology: yield* WatchTopology.make(pendingRef),\n declared: yield* Ref.make(false),\n };\n const restartWith = (invalidation: Effect.Effect<void>) =>\n invalidation.pipe(\n Effect.andThen(Queue.offer(restartQueue, undefined)),\n Effect.andThen(Queue.offer(signal, undefined)),\n );\n const restart = restartWith(state.topology.invalidate);\n const restartEntry = (entry: EntryPoint.Kind) =>\n restartWith(state.topology.invalidateEntry(entry));\n const duringRestoreRef = yield* Ref.make<\n Effect.Effect<void, never, Scope.Scope>\n >(Effect.void);\n return {\n run: Effect.gen(function* () {\n const structure = yield* RestoreWatcher.make(\n confectStructureWatcher(signal, pendingRef, restart, restartEntry),\n );\n yield* Ref.set(duringRestoreRef, structure.duringRestore);\n yield* Effect.all(\n [\n Effect.scoped(\n entryPointsWatcher(signal, pendingRef, restartQueue, state),\n ),\n structure.failure,\n convexConfigStructureWatcher(restart, state.declared),\n ],\n { concurrency: \"unbounded\" },\n );\n }).pipe(Effect.scoped),\n duringRestore: Effect.flatten(Ref.get(duringRestoreRef)),\n // Restored output makes esbuild rebuild; observe that, then drop the work\n // it queued so the restore does not trigger another generation.\n discardRestoreEchoes: refresh(state).pipe(\n Effect.ignore,\n Effect.andThen(Ref.set(pendingRef, SyncState.empty)),\n ),\n refresh: refresh(state),\n next: Queue.take(signal).pipe(\n Effect.andThen(\n drainUntilQuiescent(signal, COALESCE_QUIESCENCE, COALESCE_MAX_WAIT),\n ),\n Effect.andThen(state.topology.takePending),\n Effect.onInterrupt(() => Queue.offer(signal, undefined)),\n ),\n pending: Ref.get(pendingRef),\n requestRetry: Ref.update(pendingRef, SyncState.full).pipe(\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.asVoid,\n ),\n diagnostics: Ref.get(state.observation).pipe(\n Effect.map(WatchBuild.diagnostics),\n ),\n } as const;\n});\n\nexport type Watcher = Effect.Success<ReturnType<typeof make>>;\n\nconst drainUntilQuiescent = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n quiescence: Duration.Duration,\n maxWait: Duration.Duration,\n) {\n const start = yield* Clock.currentTimeMillis;\n const maxMillis = Duration.toMillis(maxWait);\n const loop: Effect.Effect<void> = Effect.gen(function* () {\n yield* Effect.sleep(quiescence);\n const drained = yield* Queue.clear(signal);\n if (drained.length === 0) return;\n const now = yield* Clock.currentTimeMillis;\n if (now - start < maxMillis) {\n yield* loop;\n }\n });\n yield* loop;\n});\n\nconst esbuildOptions = (\n path: Path.Path,\n fs: FileSystem.FileSystem,\n crypto: Crypto.Crypto,\n projectRoot: string,\n generatedDirectory: string,\n entries: ReadonlyArray<Entry>,\n notExternal: ReadonlyArray<RegExp>,\n stateRef: Ref.Ref<WatchBuild.State>,\n captureRef: Ref.Ref<WatchBuild.Capture>,\n progressRef: Ref.Ref<WatchBuild.Progress>,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initial: Deferred.Deferred<void>,\n): esbuild.BuildOptions => {\n const encodeString = Schema.encodeSync(Schema.fromJsonString(Schema.String));\n return {\n absWorkingDir: projectRoot,\n stdin: {\n contents: entries\n .map((entry) => `import ${encodeString(entry.absolutePath)};`)\n .join(\"\\n\"),\n resolveDir: projectRoot,\n sourcefile: \"confect-watch.ts\",\n loader: \"ts\",\n },\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\",\n format: \"esm\",\n logLevel: \"silent\",\n plugins: [\n Bundler.bundleWorkspacePlugin(path, fs, notExternal),\n externalPlugin({ notExternal: [...notExternal] }),\n {\n name: \"notify-rebuild\",\n setup(build) {\n build.onStart(() =>\n Effect.runPromise(\n Effect.gen(function* () {\n yield* Ref.update(progressRef, WatchBuild.start);\n yield* Ref.set(captureRef, WatchBuild.emptyCapture);\n }).pipe(Effect.as(undefined)),\n ),\n );\n build.onLoad({ filter: /.*/ }, (args) =>\n Effect.runPromise(\n Effect.gen(function* () {\n const fingerprint = yield* fs.readFile(args.path).pipe(\n Effect.flatMap((contents) =>\n crypto.digest(\"SHA-256\", contents),\n ),\n Effect.map(Hex.encode),\n Effect.result,\n );\n yield* Ref.update(captureRef, (capture) =>\n Result.isSuccess(fingerprint)\n ? {\n ...capture,\n fingerprints: HashMap.set(\n capture.fingerprints,\n args.path,\n fingerprint.success,\n ),\n }\n : {\n ...capture,\n failures: HashMap.set(\n capture.failures,\n args.path,\n WatchBuild.Failure.InputCaptureFailed({\n file: args.path,\n cause: fingerprint.failure,\n }),\n ),\n },\n );\n }).pipe(Effect.as(undefined)),\n ),\n );\n build.onEnd((result) =>\n Effect.runPromise(\n Effect.gen(function* () {\n const { generation } = yield* Ref.get(progressRef);\n const previous = yield* Ref.get(stateRef);\n const outcome = yield* WatchBuild.parse(\n result,\n yield* Ref.get(captureRef),\n (file) => path.resolve(projectRoot, file),\n crypto,\n );\n const current = WatchBuild.observe(previous, outcome);\n yield* Ref.set(stateRef, current);\n const change = WatchBuild.diff(\n previous,\n current,\n generatedDirectory + path.sep,\n );\n yield* Ref.update(pendingRef, (pending) =>\n WatchBuild.Change.$match(change, {\n Full: () => SyncState.add(pending, entries, true),\n Affected: ({ paths }) =>\n SyncState.add(\n pending,\n entries.filter((entry) =>\n HashSet.has(paths, entry.inputPath),\n ),\n ),\n Recovered: ({ paths }) =>\n SyncState.reconcile(\n SyncState.add(\n pending,\n entries.filter((entry) =>\n HashSet.has(paths, entry.inputPath),\n ),\n ),\n ),\n }),\n );\n yield* Queue.offer(signal, undefined);\n yield* Ref.update(progressRef, (progress) =>\n WatchBuild.complete(progress, { generation, outcome }),\n );\n yield* Deferred.succeed(initial, undefined);\n }),\n ),\n );\n },\n },\n ],\n };\n};\n\nconst entryPointsWatcher = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n watcher: State,\n) {\n const parentScope = yield* Effect.scope;\n const path = yield* Path.Path;\n const fs = yield* FileSystem.FileSystem;\n const projectRoot = yield* ProjectRoot.get;\n const confectDirectory = yield* ConfectDirectory.get;\n const generatedDirectory = path.join(\n yield* fs.realPath(confectDirectory),\n \"_generated\",\n );\n const crypto = yield* Crypto.Crypto;\n const completedInitial = Deferred.make<void>().pipe(\n Effect.tap((initial) => Deferred.succeed(initial, undefined)),\n );\n\n const sync = Effect.gen(function* () {\n const revision = yield* watcher.topology.revision;\n const current = yield* Ref.get(watcher.lifecycle);\n const entries = yield* EntryPoint.discover;\n yield* Ref.set(watcher.declared, entries.some(EntryPoint.Kind.$is(\"App\")));\n const desiredPaths = HashSet.fromIterable(\n entries.map((entry) => entry.absolutePath),\n );\n const key = pipe(\n Array.fromIterable(desiredPaths),\n Array.sort(Order.String),\n Array.join(\"\\n\"),\n );\n const unchanged = WatcherLifecycle.$match(current, {\n Uninitialized: () => false,\n Empty: () => entries.length === 0,\n Active: ({ key: previousKey }) => previousKey === key,\n });\n if (unchanged) {\n const initial = yield* WatcherLifecycle.$match(current, {\n Uninitialized: () => completedInitial,\n Empty: () => completedInitial,\n Active: ({ initial: activeInitial }) => Effect.succeed(activeInitial),\n });\n yield* refreshCurrent(current).pipe(\n Effect.catchTag(\"WatcherOperationError\", () =>\n Ref.update(pendingRef, SyncState.full),\n ),\n );\n yield* watcher.topology.reconcile(revision, initial);\n return;\n }\n yield* WatcherLifecycle.$match(current, {\n Uninitialized: () => Effect.void,\n Empty: () => Ref.update(pendingRef, SyncState.full),\n Active: ({ entries: previousEntries, scope }) =>\n Effect.gen(function* () {\n const removedOptional = previousEntries.filter(\n (entry) =>\n EntryPoint.isOptional(entry) &&\n !HashSet.has(desiredPaths, entry.absolutePath),\n );\n yield* Ref.update(pendingRef, (pending) =>\n SyncState.add(SyncState.full(pending), removedOptional),\n );\n yield* Scope.close(scope, Exit.void);\n }),\n });\n yield* Ref.set(watcher.lifecycle, WatcherLifecycle.Uninitialized());\n yield* Ref.set(watcher.observation, WatchBuild.State.Initial());\n if (!Array.isReadonlyArrayNonEmpty(entries)) {\n yield* Ref.set(watcher.lifecycle, WatcherLifecycle.Empty());\n yield* Ref.update(pendingRef, SyncState.full);\n yield* Queue.offer(signal, undefined);\n const initial = yield* completedInitial;\n yield* watcher.topology.reconcile(revision, initial);\n return;\n }\n\n const scope = yield* Scope.fork(parentScope, \"sequential\");\n const captureRef = yield* Ref.make(WatchBuild.emptyCapture);\n const progress = yield* Ref.make(WatchBuild.initialProgress);\n const initial = yield* Deferred.make<void>();\n const tsconfig = loadTsConfig(projectRoot);\n const notExternal = tsconfigPathsToRegExp(\n tsconfig?.data.compilerOptions?.paths ?? {},\n );\n const context = yield* Effect.acquireRelease(\n Effect.tryPromise({\n try: () =>\n esbuild.context(\n esbuildOptions(\n path,\n fs,\n crypto,\n projectRoot,\n generatedDirectory,\n entries,\n notExternal,\n watcher.observation,\n captureRef,\n progress,\n signal,\n pendingRef,\n initial,\n ),\n ),\n catch: (cause) => new OperationError({ operation: \"create\", cause }),\n }),\n (ctx) =>\n Effect.tryPromise({\n try: () => ctx.dispose(),\n catch: (cause) => new OperationError({ operation: \"dispose\", cause }),\n }).pipe(Effect.orDie),\n ).pipe(Scope.provide(scope));\n const requestIds = new WeakMap<\n Promise<esbuild.BuildResult>,\n WatchBuild.RequestId\n >();\n yield* Ref.set(\n watcher.lifecycle,\n WatcherLifecycle.Active({\n initial,\n key,\n scope,\n entries,\n context,\n identifyRequest: (promise) => {\n const existing = requestIds.get(promise);\n if (existing !== undefined) return existing;\n const id = WatchBuild.makeRequestId();\n requestIds.set(promise, id);\n return id;\n },\n progress,\n verifyInputs: (snapshot) =>\n WatchBuild.verifyInputs(snapshot, fs, crypto),\n }),\n );\n yield* Effect.tryPromise({\n try: () => context.watch(),\n catch: (cause) => new OperationError({ operation: \"watch\", cause }),\n });\n yield* watcher.topology.reconcile(revision, initial);\n }).pipe(Semaphore.withPermit(watcher.lock));\n\n yield* sync;\n return yield* Effect.forever(\n Queue.take(restartQueue).pipe(Effect.andThen(sync)),\n );\n}, Effect.provide(NodeCrypto.layer));\n\nconst refreshCurrent = Effect.fnUntraced(function* (current: WatcherLifecycle) {\n return yield* WatcherLifecycle.$match(current, {\n Empty: () => Effect.succeed(RefreshOutcome.Empty()),\n Uninitialized: () =>\n Effect.fail(\n new OperationError({\n operation: \"refresh\",\n cause: \"The dependency watcher is not initialized\",\n }),\n ),\n Active: ({ context, identifyRequest, progress, verifyInputs }) =>\n Effect.gen(function* () {\n const runSync = Effect.runSyncWith(yield* Effect.context());\n const barrier = WatchBuild.captureBarrier(yield* Ref.get(progress));\n const rebuild = Effect.gen(function* () {\n const built = yield* Effect.tryPromise({\n try: () => {\n const promise = context.rebuild();\n runSync(\n Ref.update(progress, (currentProgress) =>\n WatchBuild.request(currentProgress, identifyRequest(promise)),\n ),\n );\n return promise;\n },\n catch: (cause) =>\n new OperationError({ operation: \"rebuild\", cause }),\n }).pipe(Effect.result);\n const currentProgress = yield* Ref.get(progress);\n const completed = WatchBuild.getCompletion(currentProgress);\n if (Result.isFailure(built)) {\n const rejected = parseFailure(built.failure.cause);\n if (Option.isNone(rejected)) return yield* built.failure;\n if (WatchBuild.joinedWithoutCompletion(currentProgress))\n return Option.none<WatchBuild.Completion>();\n if (\n Option.isNone(completed) ||\n !WatchBuild.Outcome.$is(\"Failed\")(completed.value.outcome)\n )\n return yield* new OperationError({\n operation: \"rebuild\",\n cause: rejected.value.errors,\n });\n }\n if (WatchBuild.joinedWithoutCompletion(currentProgress))\n return Option.none<WatchBuild.Completion>();\n const observed = yield* Effect.fromOption(completed).pipe(\n Effect.mapError(\n () =>\n new OperationError({\n operation: \"rebuild\",\n cause: \"esbuild completed without a build observation\",\n }),\n ),\n );\n if (\n WatchBuild.isFresh(barrier, observed) &&\n WatchBuild.Outcome.$is(\"Succeeded\")(observed.outcome)\n ) {\n const unchanged = yield* verifyInputs(\n observed.outcome.snapshot,\n ).pipe(\n Effect.mapError(\n ({ cause }) =>\n new OperationError({ operation: \"refresh\", cause }),\n ),\n );\n if (!unchanged) return Option.none<WatchBuild.Completion>();\n }\n return Option.some(observed);\n }).pipe(Effect.ensuring(Ref.update(progress, WatchBuild.endRequest)));\n const completed = yield* rebuild.pipe(\n Effect.repeat({\n while: (observed) =>\n Option.isNone(observed) ||\n !WatchBuild.isFresh(barrier, observed.value),\n }),\n Effect.flatMap((observed) =>\n Effect.fromOption(observed).pipe(Effect.orDie),\n ),\n );\n return RefreshOutcome.Observed({ outcome: completed.outcome });\n }),\n });\n});\n\nconst refresh = (watcher: State) =>\n watcher.lock.withPermit(\n Ref.get(watcher.lifecycle).pipe(Effect.flatMap(refreshCurrent)),\n );\n\nconst confectStructureWatcher = Effect.fnUntraced(function* (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restart: Effect.Effect<unknown>,\n restartEntry: (entry: EntryPoint.Kind) => Effect.Effect<unknown>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const removedDirectory = Schema.Struct({\n code: Schema.Literal(\"ENOENT\"),\n syscall: Schema.Literal(\"scandir\"),\n path: Schema.String,\n });\n\n yield* pipe(\n fs.watch(confectDirectory, { recursive: true }),\n Stream.runForEach((event) =>\n handleConfectChange({\n relativePath: path.relative(\n confectDirectory,\n path.resolve(confectDirectory, event.path),\n ),\n eventTag: event._tag,\n signal,\n pendingRef,\n restart,\n restartEntry,\n }),\n ),\n Effect.retry({\n schedule: Schedule.spaced(\"10 millis\"),\n while: (error) =>\n Effect.gen(function* () {\n const removed = Schema.decodeUnknownOption(removedDirectory)(\n error.reason.cause,\n );\n if (Option.isNone(removed)) return false;\n const relative = path.relative(confectDirectory, removed.value.path);\n if (\n relative === \"\" ||\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n )\n return false;\n if (\n !(yield* fs\n .exists(confectDirectory)\n .pipe(Effect.orElseSucceed(() => false)))\n ) {\n return false;\n }\n yield* restart;\n return true;\n }),\n }),\n );\n});\n\n/**\n * Non-recursive `fs.watch` on the convex directory, reacting only to\n * `convex.config.ts`. Recursion is deliberately avoided: codegen (Confect's and\n * Convex's) writes into `convex/` and `convex/_generated/` constantly, and\n * reacting to those writes would loop. Create/Remove offers to `restartQueue`\n * so the entry-point watcher set picks up (or drops) the config's esbuild\n * watcher.\n */\nconst convexConfigStructureWatcher = Effect.fnUntraced(function* (\n restart: Effect.Effect<unknown>,\n declared: Ref.Ref<boolean>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* pipe(\n fs.watch(convexDirectory),\n Stream.runForEach((event) =>\n Effect.gen(function* () {\n // A declared backend's config is codegen output; reacting to its\n // writes would rerun codegen for nothing.\n if (\n path.relative(convexDirectory, event.path) !==\n CONVEX_CONFIG_FILENAME ||\n (yield* Ref.get(declared))\n )\n return;\n yield* Match.value(event).pipe(\n Match.tag(\"Update\", () => Effect.void),\n Match.tag(\"Create\", \"Remove\", () => restart),\n Match.exhaustive,\n );\n }),\n ),\n );\n});\n\nconst flipDirtyAndSignal = (\n pendingRef: Ref.Ref<Pending>,\n signal: Queue.Queue<void>,\n restart: Effect.Effect<unknown>,\n isLifecycleChange: boolean,\n) =>\n isLifecycleChange\n ? restart\n : Ref.update(pendingRef, SyncState.full).pipe(\n Effect.andThen(Queue.offer(signal, undefined)),\n );\n\nconst handleConfectChange = ({\n relativePath,\n eventTag,\n signal,\n pendingRef,\n restart,\n restartEntry,\n}: {\n relativePath: string;\n eventTag: \"Create\" | \"Update\" | \"Remove\";\n signal: Queue.Queue<void>;\n pendingRef: Ref.Ref<Pending>;\n restart: Effect.Effect<unknown>;\n restartEntry: (entry: EntryPoint.Kind) => Effect.Effect<unknown>;\n}) => {\n if (relativePath.split(/[/\\\\]/).includes(\"_generated\")) {\n return Effect.void;\n }\n\n if (!relativePath.endsWith(\".ts\")) {\n return Effect.void;\n }\n\n const isLifecycleChange = Match.value(eventTag).pipe(\n Match.when(\"Update\", () => false),\n Match.whenOr(\"Create\", \"Remove\", () => true),\n Match.exhaustive,\n );\n\n const entry = EntryPoint.parse(relativePath);\n if (Option.isSome(entry)) {\n return isLifecycleChange ? restartEntry(entry.value) : Effect.void;\n }\n\n // A stray `confect/schema.ts` (now codegen-owned at\n // `_generated/schema.ts`) shouldn't exist; flagging it here ensures the\n // next codegen pass surfaces the migration error\n // (`LegacySchemaFileError`) instead of silently ignoring the file.\n if (relativePath === \"schema.ts\") {\n return flipDirtyAndSignal(pendingRef, signal, restart, isLifecycleChange);\n }\n\n // Any other `.ts` under `confect/` (helpers like `tables/notes.ts`).\n // Updates to such files are handled by the esbuild watcher for whichever\n // entry point imports them—its onEnd flips the right dirty flag.\n // Creates are our safety net: when a previously-missing import is added,\n // esbuild may not have its parent directory on a poll path, so we\n // re-run codegen on Create here.\n return Match.value(eventTag).pipe(\n Match.when(\"Create\", () =>\n flipDirtyAndSignal(pendingRef, signal, restart, false),\n ),\n Match.whenOr(\"Update\", \"Remove\", () => Effect.void),\n Match.exhaustive,\n );\n};\n"],"mappings":"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"}
|
package/dist/cliApp.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliApp.mjs","names":["packageJson.version"],"sources":["../src/cliApp.ts"],"sourcesContent":["import * as Command from \"effect/cli/Command\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { confect } from \"./confect\";\n\nexport const cliApp = Command.run(confect, {\n version: packageJson.version,\n});\n"],"mappings":";;;;AAIA,MAAa,SAAS,QAAQ,IAAI,SAAS,EAChCA,QACX,CAAC"}
|
|
1
|
+
{"version":3,"file":"cliApp.mjs","names":["packageJson.version"],"sources":["../src/cliApp.ts"],"sourcesContent":["import * as Command from \"effect/cli/Command\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { confect } from \"./confect.js\";\n\nexport const cliApp = Command.run(confect, {\n version: packageJson.version,\n});\n"],"mappings":";;;;AAIA,MAAa,SAAS,QAAQ,IAAI,SAAS,EAChCA,QACX,CAAC"}
|
package/dist/confect/codegen.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fromBundlerError } from "../BuildError.mjs";
|
|
2
2
|
import { ProjectRoot } from "../ProjectRoot.mjs";
|
|
3
3
|
import { logFileAdded, logFileModified, logFileRemoved, logPending, logSuccess, logWarn } from "../log.mjs";
|
|
4
|
-
import { bundle } from "../Bundler.mjs";
|
|
4
|
+
import { bundle, resolveModule } from "../Bundler.mjs";
|
|
5
5
|
import { CallbackPrefixError, ComponentNodeGroupError, ConflictingDocNameError, InvalidConvexConfigError, LegacySchemaFileError, tapAndLog } from "../CodegenError.mjs";
|
|
6
6
|
import { ConvexDirectory } from "../ConvexDirectory.mjs";
|
|
7
7
|
import { ConfectDirectory } from "../ConfectDirectory.mjs";
|
|
@@ -501,13 +501,15 @@ const generateTesting = Effect.fnUntraced(function* (app) {
|
|
|
501
501
|
const value = path.relative(generated, file).split(path.sep).join("/");
|
|
502
502
|
return value.startsWith(".") ? value : `./${value}`;
|
|
503
503
|
};
|
|
504
|
-
const imports = ["import schema from \"./schema\";", "import convexSchema from \"./convexSchema\";"];
|
|
504
|
+
const imports = ["import schema from \"./schema.js\";", "import convexSchema from \"./convexSchema.js\";"];
|
|
505
505
|
const moduleMap = Effect.fnUntraced(function* (directory) {
|
|
506
506
|
return `{ "_generated/testing.ts": () => Promise.resolve({}),\n${(yield* fs.readDirectory(directory, { recursive: true })).filter((file) => /\.[cm]?[jt]s$/.test(file) && !file.endsWith(".d.ts") && !file.split(path.sep).includes("_generated") && !file.includes(".config.")).sort().map((file) => {
|
|
507
507
|
const specifier = relative(path.join(directory, file));
|
|
508
|
-
return `${quote(file.split(path.sep).join("/"))}: () => import(${quote(specifier.replace(/\.[cm]?[jt]s$/, ""))}),`;
|
|
508
|
+
return `${quote(file.split(path.sep).join("/"))}: () => import(${quote(specifier.replace(/\.([cm]?)[jt]s$/, ".$1js"))}),`;
|
|
509
509
|
}).join("\n")}\n}`;
|
|
510
510
|
});
|
|
511
|
+
const declarations = [];
|
|
512
|
+
const identifiers = /* @__PURE__ */ new Set(["schema", "convexSchema"]);
|
|
511
513
|
const components = [];
|
|
512
514
|
const httpMounts = [];
|
|
513
515
|
const kits = /* @__PURE__ */ new Map();
|
|
@@ -517,28 +519,48 @@ const generateTesting = Effect.fnUntraced(function* (app) {
|
|
|
517
519
|
const installed = yield* discoverInstalledComponents(config, relative(config), kind);
|
|
518
520
|
for (const component of installed) {
|
|
519
521
|
const name = prefix + component.name;
|
|
520
|
-
const
|
|
522
|
+
const fromPackage = !path.isAbsolute(component.componentDefinitionPath);
|
|
523
|
+
const resolved = fromPackage ? path.dirname(yield* resolveDefinition(`${component.componentDefinitionPath}/convex.config`, config, path).pipe(Effect.fromOption, Effect.mapError(() => new InvalidConvexConfigError({
|
|
521
524
|
configPath: relative(config),
|
|
522
525
|
reason: `Cannot resolve component ${component.componentDefinitionPath} for testing.`
|
|
523
|
-
}))));
|
|
526
|
+
})))) : component.componentDefinitionPath;
|
|
524
527
|
const localPrefix = prefix === "" ? App.mounts(app).find((mount) => mount.name === component.name)?.httpPrefix : component.httpPrefix;
|
|
525
528
|
const httpPrefix = parentHttpPrefix === void 0 || localPrefix === void 0 ? void 0 : `${parentHttpPrefix.replace(/\/+$/, "")}${localPrefix}`.replace(/\/+$/, "") || "/";
|
|
526
529
|
if (httpPrefix !== void 0 && ((yield* fs.exists(path.join(resolved, "http.ts"))) || (yield* fs.exists(path.join(resolved, "http.js"))))) httpMounts.push(`{ componentPath: ${quote(name)}, httpPrefix: ${quote(httpPrefix)} }`);
|
|
527
530
|
let identifier = kits.get(resolved);
|
|
528
531
|
if (identifier === void 0) {
|
|
529
|
-
|
|
532
|
+
const cleaned = name.replaceAll(/[^A-Za-z0-9_$]/g, "_");
|
|
533
|
+
const base = /^[A-Za-z_$]/.test(cleaned) ? cleaned : `_${cleaned}`;
|
|
534
|
+
identifier = `${base}Kit`;
|
|
535
|
+
let schemaIdentifier = `${base}Schema`;
|
|
536
|
+
for (let n = 2; identifiers.has(identifier) || identifiers.has(schemaIdentifier); n++) {
|
|
537
|
+
identifier = `${base}Kit${n}`;
|
|
538
|
+
schemaIdentifier = `${base}Schema${n}`;
|
|
539
|
+
}
|
|
540
|
+
identifiers.add(identifier);
|
|
541
|
+
identifiers.add(schemaIdentifier);
|
|
530
542
|
kits.set(resolved, identifier);
|
|
531
|
-
|
|
532
|
-
|
|
543
|
+
if (fromPackage) {
|
|
544
|
+
const testEntry = `${component.componentDefinitionPath}/test`;
|
|
545
|
+
if (Option.isNone(resolveModule(testEntry, config))) return yield* new InvalidConvexConfigError({
|
|
546
|
+
configPath: relative(config),
|
|
547
|
+
reason: `the component package ${quote(component.componentDefinitionPath)} has no ${quote(testEntry)} export. Tests register a component from another package through that entry's \`register(t, name)\`.`
|
|
548
|
+
});
|
|
549
|
+
imports.push(`import * as ${identifier} from ${quote(testEntry)};`);
|
|
550
|
+
} else {
|
|
551
|
+
imports.push(`import ${schemaIdentifier} from ${quote(relative(path.join(resolved, "schema.js")))};`);
|
|
552
|
+
declarations.push(`const ${identifier} = { schema: ${schemaIdentifier}, modules: ${yield* moduleMap(resolved)} };`);
|
|
553
|
+
}
|
|
533
554
|
}
|
|
534
555
|
components.push(`${quote(name)}: ${identifier},`);
|
|
535
|
-
yield* visit(resolved, `${name}/`, "component", httpPrefix);
|
|
556
|
+
if (!fromPackage) yield* visit(resolved, `${name}/`, "component", httpPrefix);
|
|
536
557
|
}
|
|
537
558
|
});
|
|
538
559
|
yield* visit(convexDirectory, "", (yield* ConvexDirectory.target).kind, "");
|
|
539
560
|
yield* writeFileStringAndLog(path.join(generated, "testing.ts"), [
|
|
540
561
|
"// Generated by `confect codegen`. Test-only; do not import in backend functions.",
|
|
541
562
|
...imports,
|
|
563
|
+
...declarations,
|
|
542
564
|
`export default { httpMounts: [${httpMounts.join(",")}], schema, convexSchema, modules: ${yield* moduleMap(convexDirectory)}, components: {${components.join("\n")}} };`
|
|
543
565
|
].join("\n"));
|
|
544
566
|
});
|