@gaganref/confect-alchemy 0.0.0-stage → 10.3.1-fork.1
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/LICENSE +7 -0
- package/README.md +22 -2
- package/dist/Backend.d.ts +45 -0
- package/dist/Backend.d.ts.map +1 -0
- package/dist/Backend.js +38 -0
- package/dist/Backend.js.map +1 -0
- package/dist/Code.d.ts +80 -0
- package/dist/Code.d.ts.map +1 -0
- package/dist/Code.js +29 -0
- package/dist/Code.js.map +1 -0
- package/dist/Confect.d.ts +2 -0
- package/dist/Confect.d.ts.map +1 -0
- package/dist/Confect.js +8 -0
- package/dist/Confect.js.map +1 -0
- package/dist/Convex.d.ts +7 -0
- package/dist/Convex.d.ts.map +1 -0
- package/dist/Convex.js +21 -0
- package/dist/Convex.js.map +1 -0
- package/dist/ConvexClient.d.ts +215 -0
- package/dist/ConvexClient.d.ts.map +1 -0
- package/dist/ConvexClient.js +224 -0
- package/dist/ConvexClient.js.map +1 -0
- package/dist/DeployKey.d.ts +120 -0
- package/dist/DeployKey.d.ts.map +1 -0
- package/dist/DeployKey.js +151 -0
- package/dist/DeployKey.js.map +1 -0
- package/dist/Deployment.d.ts +113 -0
- package/dist/Deployment.d.ts.map +1 -0
- package/dist/Deployment.js +160 -0
- package/dist/Deployment.js.map +1 -0
- package/dist/EnvironmentVariables.d.ts +155 -0
- package/dist/EnvironmentVariables.d.ts.map +1 -0
- package/dist/EnvironmentVariables.js +135 -0
- package/dist/EnvironmentVariables.js.map +1 -0
- package/dist/Project.d.ts +100 -0
- package/dist/Project.d.ts.map +1 -0
- package/dist/Project.js +123 -0
- package/dist/Project.js.map +1 -0
- package/dist/Providers.d.ts +17 -0
- package/dist/Providers.d.ts.map +1 -0
- package/dist/Providers.js +46 -0
- package/dist/Providers.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/internal/CodeDeployment.d.ts +17 -0
- package/dist/internal/CodeDeployment.d.ts.map +1 -0
- package/dist/internal/CodeDeployment.js +53 -0
- package/dist/internal/CodeDeployment.js.map +1 -0
- package/dist/internal/CommandRunner.d.ts +55 -0
- package/dist/internal/CommandRunner.d.ts.map +1 -0
- package/dist/internal/CommandRunner.js +126 -0
- package/dist/internal/CommandRunner.js.map +1 -0
- package/dist/internal/FileLock.d.ts +30 -0
- package/dist/internal/FileLock.d.ts.map +1 -0
- package/dist/internal/FileLock.js +83 -0
- package/dist/internal/FileLock.js.map +1 -0
- package/dist/internal/Lifecycle.d.ts +11 -0
- package/dist/internal/Lifecycle.d.ts.map +1 -0
- package/dist/internal/Lifecycle.js +45 -0
- package/dist/internal/Lifecycle.js.map +1 -0
- package/dist/internal/LocalBackend.d.ts +63 -0
- package/dist/internal/LocalBackend.d.ts.map +1 -0
- package/dist/internal/LocalBackend.js +333 -0
- package/dist/internal/LocalBackend.js.map +1 -0
- package/dist/internal/LocalSidecar.d.ts +3 -0
- package/dist/internal/LocalSidecar.d.ts.map +1 -0
- package/dist/internal/LocalSidecar.js +7 -0
- package/dist/internal/LocalSidecar.js.map +1 -0
- package/dist/internal/Loopback.d.ts +18 -0
- package/dist/internal/Loopback.d.ts.map +1 -0
- package/dist/internal/Loopback.js +31 -0
- package/dist/internal/Loopback.js.map +1 -0
- package/dist/internal/PushLock.d.ts +36 -0
- package/dist/internal/PushLock.d.ts.map +1 -0
- package/dist/internal/PushLock.js +61 -0
- package/dist/internal/PushLock.js.map +1 -0
- package/dist/internal/ResourceIdentity.d.ts +3 -0
- package/dist/internal/ResourceIdentity.d.ts.map +1 -0
- package/dist/internal/ResourceIdentity.js +10 -0
- package/dist/internal/ResourceIdentity.js.map +1 -0
- package/dist/internal/UserCache.d.ts +12 -0
- package/dist/internal/UserCache.d.ts.map +1 -0
- package/dist/internal/UserCache.js +25 -0
- package/dist/internal/UserCache.js.map +1 -0
- package/dist/internal/Zip.d.ts +15 -0
- package/dist/internal/Zip.d.ts.map +1 -0
- package/dist/internal/Zip.js +65 -0
- package/dist/internal/Zip.js.map +1 -0
- package/package.json +65 -3
- package/src/Backend.ts +101 -0
- package/src/Code.ts +55 -0
- package/src/Confect.ts +1 -0
- package/src/Convex.ts +18 -0
- package/src/ConvexClient.ts +544 -0
- package/src/DeployKey.ts +256 -0
- package/src/Deployment.ts +277 -0
- package/src/EnvironmentVariables.ts +254 -0
- package/src/Project.ts +205 -0
- package/src/Providers.ts +67 -0
- package/src/index.ts +2 -0
- package/src/internal/CodeDeployment.ts +81 -0
- package/src/internal/CommandRunner.ts +251 -0
- package/src/internal/FileLock.ts +136 -0
- package/src/internal/Lifecycle.ts +49 -0
- package/src/internal/LocalBackend.ts +492 -0
- package/src/internal/LocalSidecar.ts +9 -0
- package/src/internal/Loopback.ts +34 -0
- package/src/internal/PushLock.ts +99 -0
- package/src/internal/ResourceIdentity.ts +20 -0
- package/src/internal/UserCache.ts +25 -0
- package/src/internal/Zip.ts +108 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalBackend.js","names":["Loopback.parsePortRange","FileLock.acquire","Loopback.origin"],"sources":["../../src/internal/LocalBackend.ts"],"sourcesContent":["import { createServer, type Server } from \"node:net\";\nimport { dotAlchemyDirectory } from \"alchemy/AlchemyContext\";\nimport { Stack } from \"alchemy/Stack\";\nimport * as Config from \"effect/Config\";\nimport * as Crypto from \"effect/Crypto\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Option from \"effect/Option\";\nimport * as Path from \"effect/Path\";\nimport * as Redacted from \"effect/Redacted\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as Schema from \"effect/Schema\";\nimport * as Scope from \"effect/Scope\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Stream from \"effect/Stream\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\";\nimport { processEnvironment } from \"./CommandRunner\";\nimport * as FileLock from \"./FileLock\";\nimport * as Loopback from \"./Loopback\";\nimport { userCache } from \"./UserCache\";\nimport { extract } from \"./Zip\";\n\n/**\n * The backend release a new local deployment runs. An existing data directory\n * keeps running the release that created it, so upgrading this package never\n * opens a database with a different release.\n */\nexport const backendVersion = \"precompiled-2026-10-06-a3538c6\";\n\nexport class LocalBackendError extends Schema.TaggedError<LocalBackendError>()(\n \"LocalBackendError\",\n {\n deploymentName: Schema.String,\n reason: Schema.Literals([\n \"configuration\",\n \"filesystem\",\n \"install\",\n \"identity\",\n \"ports\",\n \"running\",\n \"spawn\",\n \"exit\",\n \"timeout\",\n ]),\n exitCode: Schema.optionalKey(Schema.Finite),\n },\n) {\n override get message() {\n const subject = `Local backend for ${this.deploymentName}`;\n switch (this.reason) {\n case \"configuration\":\n return `${subject} needs a home directory and, when set, CONFECT_LOCAL_BACKEND_PORTS as a range such as 3300-3399`;\n case \"filesystem\":\n return `${subject} could not read or write its data directory`;\n case \"install\":\n return `${subject} could not download or install the backend binary`;\n case \"identity\":\n return `${subject} could not read, create, or save its identity`;\n case \"ports\":\n return `${subject} could not get its ports; another process is using them`;\n case \"running\":\n return `${subject} is already running: stop the other alchemy dev session, or the backend process left over from one, that serves its data directory`;\n case \"spawn\":\n return `${subject} could not be started`;\n case \"exit\":\n return `${subject} exited${this.exitCode === undefined ? \"\" : ` with code ${this.exitCode}`} before it was ready`;\n case \"timeout\":\n return `${subject} did not become ready in time`;\n }\n }\n}\n\nexport interface LocalBackend {\n readonly url: string;\n readonly siteUrl: string;\n readonly adminKey: Redacted.Redacted<string>;\n /**\n * Completes when the backend process has exited and released its data\n * directory, so the deployment can be booted again.\n */\n readonly exited: Effect.Effect<void>;\n}\n\n// Ports are saved once a backend has answered on them, so a first start that\n// fails picks fresh ports next time instead of keeping ones that never worked.\nconst Identity = Schema.fromJsonString(\n Schema.Struct({\n name: Schema.String,\n version: Schema.String,\n instanceSecret: Schema.String,\n adminKey: Schema.String,\n ports: Schema.optionalKey(\n Schema.Struct({ port: Schema.Int, sitePort: Schema.Int }),\n ),\n }),\n);\ntype Identity = typeof Identity.Type;\n\n// The release archive per platform, as the Convex CLI maps it\n// (convex-js src/cli/lib/localDeployment/download.ts getDownloadPath).\nconst artifacts: Readonly<Record<string, string>> = {\n \"darwin-arm64\": \"convex-local-backend-aarch64-apple-darwin.zip\",\n \"darwin-x64\": \"convex-local-backend-x86_64-apple-darwin.zip\",\n \"linux-arm64\": \"convex-local-backend-aarch64-unknown-linux-gnu.zip\",\n \"linux-x64\": \"convex-local-backend-x86_64-unknown-linux-gnu.zip\",\n \"win32-arm64\": \"convex-local-backend-x86_64-pc-windows-msvc.zip\",\n \"win32-x64\": \"convex-local-backend-x86_64-pc-windows-msvc.zip\",\n};\nconst executable =\n process.platform === \"win32\"\n ? \"convex-local-backend.exe\"\n : \"convex-local-backend\";\n\nconst close = (server: Server) =>\n Effect.callback<void>((resume) => {\n server.close(() => resume(Effect.void));\n });\n\n// A server listening on `port`, or undefined when the port is taken, closed\n// with the scope. Listening is uninterruptible, so an interrupted probe never\n// leaves a server bound to the port.\nconst listen = (port: number) =>\n Effect.acquireRelease(\n Effect.callback<Server | undefined>((resume) => {\n const server = createServer();\n server.once(\"error\", () => resume(Effect.undefined));\n server.listen(port, \"127.0.0.1\", () => resume(Effect.succeed(server)));\n }),\n (server) => (server ? close(server) : Effect.void),\n );\n\nconst isFree = (port: number) =>\n listen(port).pipe(\n Effect.map((server) => server !== undefined),\n Effect.scoped,\n );\n\nconst portOf = (server: Server | undefined) => {\n const address = server?.address();\n return typeof address === \"object\" && address !== null\n ? address.port\n : undefined;\n};\n\n// Backends in one process start one at a time, so two deployments starting\n// together never probe and claim the same ports.\nconst startup = Semaphore.makeUnsafe(1);\n\n/**\n * Resolves the directory that holds one local deployment's identity, database,\n * and file storage: `.alchemy/convex/<stack>/<stage>/<deployment>`. Keyed by\n * the deployment name because a deploy key resource only knows that name.\n */\nexport const dataDirectories = Effect.gen(function* () {\n const path = yield* Path.Path;\n const stack = yield* Stack;\n const root = path.resolve(\n yield* dotAlchemyDirectory,\n \"convex\",\n stack.name,\n stack.stage,\n );\n return (deploymentName: string) => path.join(root, deploymentName);\n});\n\nconst readIdentity = Effect.fn(\"LocalBackend.readIdentity\")(function* (\n deploymentName: string,\n directory: string,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const file = path.join(directory, \"identity.json\");\n const invalid = () =>\n new LocalBackendError({ deploymentName, reason: \"identity\" });\n if (!(yield* fs.exists(file).pipe(Effect.mapError(invalid))))\n return Option.none<Identity>();\n const identity = yield* fs\n .readFileString(file)\n .pipe(\n Effect.flatMap(Schema.decodeEffect(Identity)),\n Effect.mapError(invalid),\n );\n if (identity.name !== deploymentName) return yield* invalid();\n return Option.some(identity);\n});\n\n/**\n * The admin key of a local deployment, for its deploy key resource.\n */\nexport const adminKey = Effect.fn(\"LocalBackend.adminKey\")(function* (\n deploymentName: string,\n directory: string,\n) {\n const identity = yield* readIdentity(deploymentName, directory);\n if (Option.isNone(identity))\n return yield* new LocalBackendError({ deploymentName, reason: \"identity\" });\n return Redacted.make(identity.value.adminKey);\n});\n\nexport const remove = Effect.fn(\"LocalBackend.remove\")(function* (\n deploymentName: string,\n directory: string,\n) {\n const fs = yield* FileSystem.FileSystem;\n yield* fs\n .remove(directory, { recursive: true, force: true })\n .pipe(\n Effect.mapError(\n () => new LocalBackendError({ deploymentName, reason: \"filesystem\" }),\n ),\n );\n});\n\n/**\n * Install a backend release into the Convex CLI's binary cache,\n * `<cache>/convex/binaries/<version>/`, unless it is already there.\n */\nconst install = Effect.fn(\"LocalBackend.install\")(function* (\n deploymentName: string,\n version: string,\n cache: string,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const http = yield* HttpClient.HttpClient;\n const failed = () =>\n new LocalBackendError({ deploymentName, reason: \"install\" });\n const binaries = path.join(cache, \"convex\", \"binaries\");\n const binary = path.join(binaries, version, executable);\n const artifact = artifacts[`${process.platform}-${process.arch}`];\n return yield* Effect.gen(function* () {\n if (yield* fs.exists(binary)) return binary;\n if (artifact === undefined) return yield* failed();\n yield* fs.makeDirectory(binaries, { recursive: true });\n const temp = yield* fs.makeTempDirectoryScoped({\n directory: binaries,\n prefix: \".download-\",\n });\n const archive = path.join(temp, artifact);\n const response = yield* http.get(\n `https://github.com/get-convex/convex-backend/releases/download/${version}/${artifact}`,\n );\n if (response.status !== 200) return yield* failed();\n yield* response.stream.pipe(Stream.run(fs.sink(archive)));\n const extracted = path.join(temp, executable);\n yield* extract(archive, executable, extracted);\n yield* fs.chmod(extracted, 0o755);\n yield* fs.makeDirectory(path.dirname(binary), { recursive: true });\n yield* fs.rename(extracted, binary);\n return binary;\n }).pipe(Effect.scoped, Effect.mapError(failed));\n});\n\n/**\n * Boot the local backend for one deployment and keep it running for the ambient\n * scope. The process and the data directory's `owner.lock` share a child scope\n * that closes when the boot fails or is interrupted and when the process exits,\n * so a deployment can be booted again in the same session. The first boot\n * installs the pinned release, generates an instance secret and admin key, and\n * picks two free loopback ports; later boots reuse all of them, so stored\n * credentials and URLs stay valid. A saved port that something else now holds\n * is an error rather than a move, because resources keep the URL.\n */\nexport const boot = Effect.fn(\"LocalBackend.boot\")(function* (\n deploymentName: string,\n directory: string,\n) {\n const path = yield* Path.Path;\n const fs = yield* FileSystem.FileSystem;\n const http = yield* HttpClient.HttpClient;\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n const crypto = yield* Crypto.Crypto;\n const fail = (reason: LocalBackendError[\"reason\"], exitCode?: number) =>\n new LocalBackendError({\n deploymentName,\n reason,\n ...(exitCode === undefined ? {} : { exitCode }),\n });\n const settings = yield* Config.all({\n environment: processEnvironment,\n ports: Config.String(\"CONFECT_LOCAL_BACKEND_PORTS\").pipe(Config.option),\n }).pipe(Effect.mapError(() => fail(\"configuration\")));\n const cache = yield* userCache.pipe(\n Effect.mapError(() => fail(\"configuration\")),\n );\n if (Option.isNone(cache)) return yield* fail(\"configuration\");\n const range = Option.map(settings.ports, Loopback.parsePortRange);\n if (Option.isSome(range) && Option.isNone(range.value))\n return yield* fail(\"configuration\");\n const env = {\n ...settings.environment,\n DISABLE_BEACON: \"1\",\n DO_NOT_TRACK: \"1\",\n };\n\n const answer = (url: string) =>\n http.get(`${url}/instance_name`).pipe(\n Effect.flatMap((response) =>\n response.status === 200 ? response.text : Effect.undefined,\n ),\n Effect.orElseSucceed(() => undefined),\n );\n\n const allocate = Effect.gen(function* () {\n if (Option.isNone(range) || Option.isNone(range.value)) {\n // A closed server no longer has an address.\n const [port, sitePort] = yield* Effect.all([listen(0), listen(0)]).pipe(\n Effect.map((servers) => servers.map(portOf)),\n Effect.scoped,\n );\n if (port === undefined || sitePort === undefined)\n return yield* fail(\"ports\");\n return { port, sitePort };\n }\n const free: number[] = [];\n const { from, to } = range.value.value;\n for (let port = from; port <= to && free.length < 2; port++) {\n if (yield* isFree(port)) free.push(port);\n }\n const [port, sitePort] = free;\n if (port === undefined || sitePort === undefined)\n return yield* fail(\"ports\");\n return { port, sitePort };\n });\n\n const generate = (binary: string) =>\n Effect.gen(function* () {\n const instanceSecret = Buffer.from(\n yield* crypto.randomBytes(32),\n ).toString(\"hex\");\n const output = yield* spawner.string(\n ChildProcess.make(\n binary,\n [\n \"keygen\",\n \"admin-key\",\n \"--instance-name\",\n deploymentName,\n \"--instance-secret\",\n instanceSecret,\n ],\n { env, extendEnv: false, stdin: \"ignore\", stderr: \"ignore\" },\n ),\n );\n const key = output.trim();\n if (!key.startsWith(`${deploymentName}|`)) return yield* fail(\"identity\");\n return { instanceSecret, adminKey: key };\n }).pipe(\n Effect.catchTag(\"PlatformError\", () => Effect.fail(fail(\"identity\"))),\n );\n\n const save = (identity: Identity) =>\n Effect.gen(function* () {\n const file = path.join(directory, \"identity.json\");\n const temp = `${file}.${process.pid}.tmp`;\n yield* fs.writeFileString(\n temp,\n yield* Schema.encodeEffect(Identity)(identity),\n { mode: 0o600 },\n );\n yield* fs.rename(temp, file);\n }).pipe(\n // Node cannot cancel a write or rename it has started; finishing it\n // keeps a pending rename from landing after owner.lock is released.\n Effect.uninterruptible,\n Effect.mapError(() => fail(\"identity\")),\n );\n\n const session = yield* Scope.Scope;\n const owned = yield* Scope.fork(session);\n const stopped = yield* Deferred.make<void>();\n // Registered first, so it runs after the process and owner.lock finalizers\n // however the scope closes.\n yield* Scope.addFinalizer(owned, Deferred.succeed(stopped, undefined));\n const start = Effect.gen(function* () {\n yield* FileLock.acquire(path.join(directory, \"owner.lock\")).pipe(\n Effect.mapError((error) =>\n fail(error.reason === \"held\" ? \"running\" : \"filesystem\"),\n ),\n );\n const saved = yield* readIdentity(deploymentName, directory);\n const version = Option.isSome(saved) ? saved.value.version : backendVersion;\n const binary = yield* install(deploymentName, version, cache.value);\n let identity: Identity;\n if (Option.isSome(saved)) {\n identity = saved.value;\n } else {\n identity = {\n name: deploymentName,\n version,\n ...(yield* generate(binary)),\n };\n yield* save(identity);\n }\n const ports = identity.ports ?? (yield* allocate);\n if (identity.ports !== undefined) {\n if (!(yield* isFree(ports.port)) || !(yield* isFree(ports.sitePort)))\n return yield* fail(\n (yield* answer(Loopback.origin(ports.port))) === deploymentName\n ? \"running\"\n : \"ports\",\n );\n }\n\n const url = Loopback.origin(ports.port);\n const siteUrl = Loopback.origin(ports.sitePort);\n const handle = yield* spawner\n .spawn(\n ChildProcess.make(\n binary,\n [\n \"--interface\",\n \"127.0.0.1\",\n \"--port\",\n String(ports.port),\n \"--site-proxy-port\",\n String(ports.sitePort),\n \"--instance-name\",\n deploymentName,\n \"--instance-secret\",\n identity.instanceSecret,\n \"--convex-origin\",\n url,\n \"--convex-site\",\n siteUrl,\n \"--local-storage\",\n path.join(directory, \"convex_local_storage\"),\n \"--disable-beacon\",\n path.join(directory, \"convex_local_backend.sqlite3\"),\n ],\n {\n cwd: directory,\n env,\n extendEnv: false,\n stdin: \"ignore\",\n stdout: \"ignore\",\n stderr: \"ignore\",\n forceKillAfter: \"3 seconds\",\n },\n ),\n )\n .pipe(Effect.mapError(() => fail(\"spawn\")));\n\n const ready = answer(url).pipe(\n Effect.flatMap((name) =>\n name === undefined || name === deploymentName\n ? Effect.succeed(name === deploymentName)\n : Effect.fail(fail(\"ports\")),\n ),\n Effect.repeat({\n until: (isReady) => isReady,\n schedule: Schedule.spaced(\"100 millis\"),\n }),\n );\n const exit = handle.exitCode.pipe(\n Effect.mapError(() => fail(\"exit\")),\n Effect.flatMap((code) => Effect.fail(fail(\"exit\", Number(code)))),\n );\n yield* Effect.raceFirst(ready, exit).pipe(\n Effect.timeoutOrElse({\n duration: \"60 seconds\",\n orElse: () => Effect.fail(fail(\"timeout\")),\n }),\n );\n if (identity.ports === undefined) yield* save({ ...identity, ports });\n // An exited backend releases the data directory before `exited`\n // completes, so the deployment can be booted again in this session.\n yield* handle.exitCode.pipe(\n Effect.ignore,\n Effect.andThen(Scope.close(owned, Exit.void)),\n Effect.forkIn(session),\n );\n return {\n url,\n siteUrl,\n adminKey: Redacted.make(identity.adminKey),\n exited: Deferred.await(stopped),\n } satisfies LocalBackend;\n });\n\n return yield* startup\n .withPermits(1)(start)\n .pipe(\n Scope.provide(owned),\n Effect.onExit((exit) =>\n Exit.isSuccess(exit) ? Effect.void : Scope.close(owned, exit),\n ),\n );\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const _default: import("effect/Layer").Layer<import("alchemy/Provider").Provider<import("../Deployment").Deployment>, never, import("alchemy/AlchemyContext").AlchemyContext | import("alchemy/Artifacts").ArtifactStore | import("effect/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/http/HttpClient").HttpClient | import("effect/Path").Path | import("alchemy/Stack").Stack>;
|
|
2
|
+
export default _default;
|
|
3
|
+
//# sourceMappingURL=LocalSidecar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalSidecar.d.ts","sourceRoot":"","sources":["../../src/internal/LocalSidecar.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalSidecar.js","names":[],"sources":["../../src/internal/LocalSidecar.ts"],"sourcesContent":["/**\n * The local Deployment provider served by Alchemy's dev sidecar, so a local\n * backend outlives reloads of the stack. Alchemy imports this module by URL and\n * builds its default export once per dev session.\n */\nimport type * as RpcServer from \"alchemy/Local/RpcServer\";\nimport { localLayer } from \"../Deployment\";\n\nexport default localLayer satisfies RpcServer.ProviderLayer;\n"],"mappings":";;AAQA,IAAA,uBAAe"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
/**
|
|
3
|
+
* The origin of a local backend port. Local backends bind only loopback.
|
|
4
|
+
*/
|
|
5
|
+
export declare const origin: (port: number) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Whether `url` is exactly a loopback origin such as `http://127.0.0.1:3210`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isOrigin: (url: string) => boolean;
|
|
10
|
+
export interface PortRange {
|
|
11
|
+
readonly from: number;
|
|
12
|
+
readonly to: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse an inclusive `from-to` range of usable ports, such as `3300-3399`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const parsePortRange: (value: string) => Option.Option<PortRange>;
|
|
18
|
+
//# sourceMappingURL=Loopback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loopback.d.ts","sourceRoot":"","sources":["../../src/internal/Loopback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;GAEG;AACH,eAAO,MAAM,MAAM,SAAU,MAAM,WAA+B,CAAC;AAInE;;GAEG;AACH,eAAO,MAAM,QAAQ,QAAS,MAAM,YAGnC,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAQrE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
//#region src/internal/Loopback.ts
|
|
3
|
+
/**
|
|
4
|
+
* The origin of a local backend port. Local backends bind only loopback.
|
|
5
|
+
*/
|
|
6
|
+
const origin = (port) => `http://127.0.0.1:${port}`;
|
|
7
|
+
const isPort = (port) => port >= 1 && port <= 65535;
|
|
8
|
+
/**
|
|
9
|
+
* Whether `url` is exactly a loopback origin such as `http://127.0.0.1:3210`.
|
|
10
|
+
*/
|
|
11
|
+
const isOrigin = (url) => {
|
|
12
|
+
const match = /^http:\/\/127\.0\.0\.1:([1-9][0-9]{0,4})$/.exec(url);
|
|
13
|
+
return match !== null && isPort(Number(match[1]));
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Parse an inclusive `from-to` range of usable ports, such as `3300-3399`.
|
|
17
|
+
*/
|
|
18
|
+
const parsePortRange = (value) => {
|
|
19
|
+
const match = /^([0-9]{1,5})-([0-9]{1,5})$/.exec(value);
|
|
20
|
+
if (match === null) return Option.none();
|
|
21
|
+
const from = Number(match[1]);
|
|
22
|
+
const to = Number(match[2]);
|
|
23
|
+
return isPort(from) && isPort(to) && from <= to ? Option.some({
|
|
24
|
+
from,
|
|
25
|
+
to
|
|
26
|
+
}) : Option.none();
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { isOrigin, origin, parsePortRange };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=Loopback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Loopback.js","names":[],"sources":["../../src/internal/Loopback.ts"],"sourcesContent":["import * as Option from \"effect/Option\";\n\n/**\n * The origin of a local backend port. Local backends bind only loopback.\n */\nexport const origin = (port: number) => `http://127.0.0.1:${port}`;\n\nconst isPort = (port: number) => port >= 1 && port <= 65535;\n\n/**\n * Whether `url` is exactly a loopback origin such as `http://127.0.0.1:3210`.\n */\nexport const isOrigin = (url: string) => {\n const match = /^http:\\/\\/127\\.0\\.0\\.1:([1-9][0-9]{0,4})$/.exec(url);\n return match !== null && isPort(Number(match[1]));\n};\n\nexport interface PortRange {\n readonly from: number;\n readonly to: number;\n}\n\n/**\n * Parse an inclusive `from-to` range of usable ports, such as `3300-3399`.\n */\nexport const parsePortRange = (value: string): Option.Option<PortRange> => {\n const match = /^([0-9]{1,5})-([0-9]{1,5})$/.exec(value);\n if (match === null) return Option.none();\n const from = Number(match[1]);\n const to = Number(match[2]);\n return isPort(from) && isPort(to) && from <= to\n ? Option.some({ from, to })\n : Option.none();\n};\n"],"mappings":";;;;;AAKA,MAAa,UAAU,SAAiB,oBAAoB;AAE5D,MAAM,UAAU,SAAiB,QAAQ,KAAK,QAAQ;;;;AAKtD,MAAa,YAAY,QAAgB;CACvC,MAAM,QAAQ,4CAA4C,KAAK,GAAG;CAClE,OAAO,UAAU,QAAQ,OAAO,OAAO,MAAM,EAAE,CAAC;AAClD;;;;AAUA,MAAa,kBAAkB,UAA4C;CACzE,MAAM,QAAQ,8BAA8B,KAAK,KAAK;CACtD,IAAI,UAAU,MAAM,OAAO,OAAO,KAAK;CACvC,MAAM,OAAO,OAAO,MAAM,EAAE;CAC5B,MAAM,KAAK,OAAO,MAAM,EAAE;CAC1B,OAAO,OAAO,IAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KACzC,OAAO,KAAK;EAAE;EAAM;CAAG,CAAC,IACxB,OAAO,KAAK;AAClB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Crypto from "effect/Crypto";
|
|
3
|
+
import * as Duration from "effect/Duration";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as FileSystem from "effect/FileSystem";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Path from "effect/Path";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
declare const PushLockError_base: Schema.Class<PushLockError, Schema.TaggedStruct<"PushLockError", {
|
|
10
|
+
readonly deploymentName: Schema.String;
|
|
11
|
+
readonly reason: Schema.Literals<readonly ["configuration", "timeout", "filesystem"]>;
|
|
12
|
+
}>, import("effect/Cause").YieldableError>;
|
|
13
|
+
export declare class PushLockError extends PushLockError_base {
|
|
14
|
+
get message(): string;
|
|
15
|
+
}
|
|
16
|
+
declare const PushLock_base: Context.ServiceClass<PushLock, "@confect/alchemy/internal/PushLock", {
|
|
17
|
+
readonly withLock: <A, E, R>(deploymentName: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | PushLockError, R>;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Serialises pushes per deployment. Overlapping `convex deploy` runs against
|
|
21
|
+
* one deployment race on `wait_for_schema`, so every push holds the lock for
|
|
22
|
+
* its whole duration, whichever stack, checkout, or process it comes from.
|
|
23
|
+
*/
|
|
24
|
+
export declare class PushLock extends PushLock_base {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One lock file per deployment under the user's cache,
|
|
28
|
+
* `confect/alchemy/locks/<deployment>.lock`, so every checkout on the machine
|
|
29
|
+
* shares it. A lock left by a process that exited is taken over; a holder that
|
|
30
|
+
* never releases ends in a typed timeout.
|
|
31
|
+
*/
|
|
32
|
+
export declare const layer: (options?: {
|
|
33
|
+
readonly timeout?: Duration.Input;
|
|
34
|
+
}) => Layer.Layer<PushLock, never, Crypto.Crypto | FileSystem.FileSystem | Path.Path>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=PushLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PushLock.d.ts","sourceRoot":"","sources":["../../src/internal/PushLock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;;;;;AAKxC,qBAAa,aAAc,SAAQ,kBAMlC;IACC,IAAa,OAAO,WASnB;CACF;;uBAUsB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACzB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,CAAC;;AAX/C;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,aAQW;CAAG;AAE5C;;;;;GAKG;AACH,eAAO,MAAM,KAAK,aAAc;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAA;CAAE,oFA6ClE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { acquire } from "./FileLock.js";
|
|
2
|
+
import { userCache } from "./UserCache.js";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import * as Context from "effect/Context";
|
|
6
|
+
import * as FileSystem from "effect/FileSystem";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Path from "effect/Path";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
import * as Crypto from "effect/Crypto";
|
|
11
|
+
import * as Duration from "effect/Duration";
|
|
12
|
+
import * as Scope from "effect/Scope";
|
|
13
|
+
//#region src/internal/PushLock.ts
|
|
14
|
+
var PushLockError = class extends Schema.TaggedError()("PushLockError", {
|
|
15
|
+
deploymentName: Schema.String,
|
|
16
|
+
reason: Schema.Literals([
|
|
17
|
+
"configuration",
|
|
18
|
+
"timeout",
|
|
19
|
+
"filesystem"
|
|
20
|
+
])
|
|
21
|
+
}) {
|
|
22
|
+
get message() {
|
|
23
|
+
switch (this.reason) {
|
|
24
|
+
case "configuration": return `Push lock for ${this.deploymentName} needs a home directory to keep its lock file in`;
|
|
25
|
+
case "timeout": return `Timed out waiting for another push to ${this.deploymentName} to release its push lock`;
|
|
26
|
+
case "filesystem": return `Push lock for ${this.deploymentName} could not read or write its lock file`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Serialises pushes per deployment. Overlapping `convex deploy` runs against
|
|
32
|
+
* one deployment race on `wait_for_schema`, so every push holds the lock for
|
|
33
|
+
* its whole duration, whichever stack, checkout, or process it comes from.
|
|
34
|
+
*/
|
|
35
|
+
var PushLock = class extends Context.Service()("@confect/alchemy/internal/PushLock") {};
|
|
36
|
+
/**
|
|
37
|
+
* One lock file per deployment under the user's cache,
|
|
38
|
+
* `confect/alchemy/locks/<deployment>.lock`, so every checkout on the machine
|
|
39
|
+
* shares it. A lock left by a process that exited is taken over; a holder that
|
|
40
|
+
* never releases ends in a typed timeout.
|
|
41
|
+
*/
|
|
42
|
+
const layer = (options) => Layer.effect(PushLock, Effect.gen(function* () {
|
|
43
|
+
const fs = yield* FileSystem.FileSystem;
|
|
44
|
+
const path = yield* Path.Path;
|
|
45
|
+
const crypto = yield* Crypto.Crypto;
|
|
46
|
+
const timeout = options?.timeout ?? Duration.minutes(10);
|
|
47
|
+
const lock = Effect.fn("PushLock.lock")(function* (deploymentName, scope) {
|
|
48
|
+
const failed = (reason) => new PushLockError({
|
|
49
|
+
deploymentName,
|
|
50
|
+
reason
|
|
51
|
+
});
|
|
52
|
+
const cache = yield* userCache.pipe(Effect.mapError(() => failed("configuration")));
|
|
53
|
+
if (Option.isNone(cache)) return yield* failed("configuration");
|
|
54
|
+
yield* acquire(path.join(cache.value, "confect", "alchemy", "locks", `${deploymentName}.lock`), timeout).pipe(Scope.provide(scope), Effect.mapError((error) => failed(error.reason === "timeout" ? "timeout" : "filesystem")));
|
|
55
|
+
}, Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path), Effect.provideService(Crypto.Crypto, crypto));
|
|
56
|
+
return PushLock.of({ withLock: (deploymentName, effect) => Effect.acquireUseRelease(Scope.make(), (scope) => lock(deploymentName, scope).pipe(Effect.andThen(effect)), (scope, exit) => Scope.close(scope, exit)) });
|
|
57
|
+
}));
|
|
58
|
+
//#endregion
|
|
59
|
+
export { PushLock, PushLockError, layer };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=PushLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PushLock.js","names":["FileLock.acquire"],"sources":["../../src/internal/PushLock.ts"],"sourcesContent":["import * as Context from \"effect/Context\";\nimport * as Crypto from \"effect/Crypto\";\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Path from \"effect/Path\";\nimport * as Schema from \"effect/Schema\";\nimport * as Scope from \"effect/Scope\";\nimport * as FileLock from \"./FileLock\";\nimport { userCache } from \"./UserCache\";\n\nexport class PushLockError extends Schema.TaggedError<PushLockError>()(\n \"PushLockError\",\n {\n deploymentName: Schema.String,\n reason: Schema.Literals([\"configuration\", \"timeout\", \"filesystem\"]),\n },\n) {\n override get message() {\n switch (this.reason) {\n case \"configuration\":\n return `Push lock for ${this.deploymentName} needs a home directory to keep its lock file in`;\n case \"timeout\":\n return `Timed out waiting for another push to ${this.deploymentName} to release its push lock`;\n case \"filesystem\":\n return `Push lock for ${this.deploymentName} could not read or write its lock file`;\n }\n }\n}\n\n/**\n * Serialises pushes per deployment. Overlapping `convex deploy` runs against\n * one deployment race on `wait_for_schema`, so every push holds the lock for\n * its whole duration, whichever stack, checkout, or process it comes from.\n */\nexport class PushLock extends Context.Service<\n PushLock,\n {\n readonly withLock: <A, E, R>(\n deploymentName: string,\n effect: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | PushLockError, R>;\n }\n>()(\"@confect/alchemy/internal/PushLock\") {}\n\n/**\n * One lock file per deployment under the user's cache,\n * `confect/alchemy/locks/<deployment>.lock`, so every checkout on the machine\n * shares it. A lock left by a process that exited is taken over; a holder that\n * never releases ends in a typed timeout.\n */\nexport const layer = (options?: { readonly timeout?: Duration.Input }) =>\n Layer.effect(\n PushLock,\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const crypto = yield* Crypto.Crypto;\n const timeout = options?.timeout ?? Duration.minutes(10);\n const lock = Effect.fn(\"PushLock.lock\")(\n function* (deploymentName: string, scope: Scope.Scope) {\n const failed = (reason: PushLockError[\"reason\"]) =>\n new PushLockError({ deploymentName, reason });\n const cache = yield* userCache.pipe(\n Effect.mapError(() => failed(\"configuration\")),\n );\n if (Option.isNone(cache)) return yield* failed(\"configuration\");\n yield* FileLock.acquire(\n path.join(\n cache.value,\n \"confect\",\n \"alchemy\",\n \"locks\",\n `${deploymentName}.lock`,\n ),\n timeout,\n ).pipe(\n Scope.provide(scope),\n Effect.mapError((error) =>\n failed(error.reason === \"timeout\" ? \"timeout\" : \"filesystem\"),\n ),\n );\n },\n Effect.provideService(FileSystem.FileSystem, fs),\n Effect.provideService(Path.Path, path),\n Effect.provideService(Crypto.Crypto, crypto),\n );\n return PushLock.of({\n withLock: (deploymentName, effect) =>\n Effect.acquireUseRelease(\n Scope.make(),\n (scope) => lock(deploymentName, scope).pipe(Effect.andThen(effect)),\n (scope, exit) => Scope.close(scope, exit),\n ),\n });\n }),\n );\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,gBAAb,cAAmC,OAAO,YAA2B,CAAC,CACpE,iBACA;CACE,gBAAgB,OAAO;CACvB,QAAQ,OAAO,SAAS;EAAC;EAAiB;EAAW;CAAY,CAAC;AACpE,CACF,CAAC,CAAC;CACA,IAAa,UAAU;EACrB,QAAQ,KAAK,QAAb;GACE,KAAK,iBACH,OAAO,iBAAiB,KAAK,eAAe;GAC9C,KAAK,WACH,OAAO,yCAAyC,KAAK,eAAe;GACtE,KAAK,cACH,OAAO,iBAAiB,KAAK,eAAe;EAChD;CACF;AACF;;;;;;AAOA,IAAa,WAAb,cAA8B,QAAQ,QAQpC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC;;;;;;;AAQ3C,MAAa,SAAS,YACpB,MAAM,OACJ,UACA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,UAAU,SAAS,WAAW,SAAS,QAAQ,EAAE;CACvD,MAAM,OAAO,OAAO,GAAG,eAAe,CAAC,CACrC,WAAW,gBAAwB,OAAoB;EACrD,MAAM,UAAU,WACd,IAAI,cAAc;GAAE;GAAgB;EAAO,CAAC;EAC9C,MAAM,QAAQ,OAAO,UAAU,KAC7B,OAAO,eAAe,OAAO,eAAe,CAAC,CAC/C;EACA,IAAI,OAAO,OAAO,KAAK,GAAG,OAAO,OAAO,OAAO,eAAe;EAC9D,OAAOA,QACL,KAAK,KACH,MAAM,OACN,WACA,WACA,SACA,GAAG,eAAe,MACpB,GACA,OACF,CAAC,CAAC,KACA,MAAM,QAAQ,KAAK,GACnB,OAAO,UAAU,UACf,OAAO,MAAM,WAAW,YAAY,YAAY,YAAY,CAC9D,CACF;CACF,GACA,OAAO,eAAe,WAAW,YAAY,EAAE,GAC/C,OAAO,eAAe,KAAK,MAAM,IAAI,GACrC,OAAO,eAAe,OAAO,QAAQ,MAAM,CAC7C;CACA,OAAO,SAAS,GAAG,EACjB,WAAW,gBAAgB,WACzB,OAAO,kBACL,MAAM,KAAK,IACV,UAAU,KAAK,gBAAgB,KAAK,CAAC,CAAC,KAAK,OAAO,QAAQ,MAAM,CAAC,IACjE,OAAO,SAAS,MAAM,MAAM,OAAO,IAAI,CAC1C,EACJ,CAAC;AACH,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceIdentity.d.ts","sourceRoot":"","sources":["../../src/internal/ResourceIdentity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,eAAO,MAAM,YAAY,wFAiBvB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
//#region src/internal/ResourceIdentity.ts
|
|
3
|
+
const resourceName = Effect.fn("ResourceIdentity.name")(function* (kind, fqn, instanceId) {
|
|
4
|
+
const hash = yield* Effect.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${fqn}:${instanceId}`)));
|
|
5
|
+
return `confect-${kind}-${Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 24)}`;
|
|
6
|
+
});
|
|
7
|
+
//#endregion
|
|
8
|
+
export { resourceName };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=ResourceIdentity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceIdentity.js","names":[],"sources":["../../src/internal/ResourceIdentity.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\n\nexport const resourceName = Effect.fn(\"ResourceIdentity.name\")(function* (\n kind: string,\n fqn: string,\n instanceId: string,\n) {\n const hash = yield* Effect.promise(() =>\n crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(`${fqn}:${instanceId}`),\n ),\n );\n const suffix = Array.from(new Uint8Array(hash), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n )\n .join(\"\")\n .slice(0, 24);\n return `confect-${kind}-${suffix}`;\n});\n"],"mappings":";;AAEA,MAAa,eAAe,OAAO,GAAG,uBAAuB,CAAC,CAAC,WAC7D,MACA,KACA,YACA;CACA,MAAM,OAAO,OAAO,OAAO,cACzB,OAAO,OAAO,OACZ,WACA,IAAI,YAAY,CAAC,CAAC,OAAO,GAAG,IAAI,GAAG,YAAY,CACjD,CACF;CAMA,OAAO,WAAW,KAAK,GALR,MAAM,KAAK,IAAI,WAAW,IAAI,IAAI,SAC/C,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CACnC,CAAC,CACE,KAAK,EAAE,CAAC,CACR,MAAM,GAAG,EACmB;AACjC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Config from "effect/Config";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Path from "effect/Path";
|
|
5
|
+
/**
|
|
6
|
+
* The user's cache directory, as the Convex CLI resolves it: `~/.cache` on
|
|
7
|
+
* POSIX and `%LOCALAPPDATA%` on Windows. Shared by every checkout on the
|
|
8
|
+
* machine, so binaries are downloaded once and push locks are seen by every
|
|
9
|
+
* stack. None when no absolute directory can be derived.
|
|
10
|
+
*/
|
|
11
|
+
export declare const userCache: Effect.Effect<Option.None<string> | Option.Some<string>, Config.ConfigError, Path.Path>;
|
|
12
|
+
//# sourceMappingURL=UserCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserCache.d.ts","sourceRoot":"","sources":["../../src/internal/UserCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,SAAS,yFAapB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Config from "effect/Config";
|
|
3
|
+
import * as Path from "effect/Path";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
//#region src/internal/UserCache.ts
|
|
6
|
+
/**
|
|
7
|
+
* The user's cache directory, as the Convex CLI resolves it: `~/.cache` on
|
|
8
|
+
* POSIX and `%LOCALAPPDATA%` on Windows. Shared by every checkout on the
|
|
9
|
+
* machine, so binaries are downloaded once and push locks are seen by every
|
|
10
|
+
* stack. None when no absolute directory can be derived.
|
|
11
|
+
*/
|
|
12
|
+
const userCache = Effect.gen(function* () {
|
|
13
|
+
const path = yield* Path.Path;
|
|
14
|
+
const { HOME, LOCALAPPDATA, USERPROFILE } = yield* Config.all({
|
|
15
|
+
HOME: Config.String("HOME").pipe(Config.withDefault("")),
|
|
16
|
+
LOCALAPPDATA: Config.String("LOCALAPPDATA").pipe(Config.withDefault("")),
|
|
17
|
+
USERPROFILE: Config.String("USERPROFILE").pipe(Config.withDefault(""))
|
|
18
|
+
});
|
|
19
|
+
const root = process.platform === "win32" ? LOCALAPPDATA || USERPROFILE && path.join(USERPROFILE, "AppData", "Local") : HOME && path.join(HOME, ".cache");
|
|
20
|
+
return root && path.isAbsolute(root) ? Option.some(root) : Option.none();
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
export { userCache };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=UserCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserCache.js","names":[],"sources":["../../src/internal/UserCache.ts"],"sourcesContent":["import * as Config from \"effect/Config\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Path from \"effect/Path\";\n\n/**\n * The user's cache directory, as the Convex CLI resolves it: `~/.cache` on\n * POSIX and `%LOCALAPPDATA%` on Windows. Shared by every checkout on the\n * machine, so binaries are downloaded once and push locks are seen by every\n * stack. None when no absolute directory can be derived.\n */\nexport const userCache = Effect.gen(function* () {\n const path = yield* Path.Path;\n const { HOME, LOCALAPPDATA, USERPROFILE } = yield* Config.all({\n HOME: Config.String(\"HOME\").pipe(Config.withDefault(\"\")),\n LOCALAPPDATA: Config.String(\"LOCALAPPDATA\").pipe(Config.withDefault(\"\")),\n USERPROFILE: Config.String(\"USERPROFILE\").pipe(Config.withDefault(\"\")),\n });\n const root =\n process.platform === \"win32\"\n ? LOCALAPPDATA ||\n (USERPROFILE && path.join(USERPROFILE, \"AppData\", \"Local\"))\n : HOME && path.join(HOME, \".cache\");\n return root && path.isAbsolute(root) ? Option.some(root) : Option.none();\n});\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,YAAY,OAAO,IAAI,aAAa;CAC/C,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,EAAE,MAAM,cAAc,gBAAgB,OAAO,OAAO,IAAI;EAC5D,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,YAAY,EAAE,CAAC;EACvD,cAAc,OAAO,OAAO,cAAc,CAAC,CAAC,KAAK,OAAO,YAAY,EAAE,CAAC;EACvE,aAAa,OAAO,OAAO,aAAa,CAAC,CAAC,KAAK,OAAO,YAAY,EAAE,CAAC;CACvE,CAAC;CACD,MAAM,OACJ,QAAQ,aAAa,UACjB,gBACC,eAAe,KAAK,KAAK,aAAa,WAAW,OAAO,IACzD,QAAQ,KAAK,KAAK,MAAM,QAAQ;CACtC,OAAO,QAAQ,KAAK,WAAW,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK;AACzE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as FileSystem from "effect/FileSystem";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
declare const ZipError_base: Schema.Class<ZipError, Schema.TaggedStruct<"ZipError", {
|
|
5
|
+
readonly reason: Schema.Literals<readonly ["format", "entry", "io"]>;
|
|
6
|
+
}>, import("effect/Cause").YieldableError>;
|
|
7
|
+
export declare class ZipError extends ZipError_base {
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract one entry of a zip archive. Convex publishes the local backend as a
|
|
11
|
+
* single-file zip, stored or deflated.
|
|
12
|
+
*/
|
|
13
|
+
export declare const extract: (archive: string, name: string, destination: string) => Effect.Effect<void, ZipError, FileSystem.FileSystem>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=Zip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Zip.d.ts","sourceRoot":"","sources":["../../src/internal/Zip.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;;;;AAExC,qBAAa,QAAS,SAAQ,aAE5B;CAAG;AA2EL;;;GAGG;AACH,eAAO,MAAM,OAAO,8GAoBlB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Result from "effect/Result";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import * as FileSystem from "effect/FileSystem";
|
|
5
|
+
import { inflateRaw } from "node:zlib";
|
|
6
|
+
//#region src/internal/Zip.ts
|
|
7
|
+
var ZipError = class extends Schema.TaggedError()("ZipError", { reason: Schema.Literals([
|
|
8
|
+
"format",
|
|
9
|
+
"entry",
|
|
10
|
+
"io"
|
|
11
|
+
]) }) {};
|
|
12
|
+
const format = () => Result.fail(new ZipError({ reason: "format" }));
|
|
13
|
+
/**
|
|
14
|
+
* Locate one entry's compressed bytes through the archive's central directory.
|
|
15
|
+
* No Zip64: a single-file release far under 4 GiB never needs it.
|
|
16
|
+
*/
|
|
17
|
+
const locate = (archive, name) => {
|
|
18
|
+
const bytes = Buffer.from(archive.buffer, archive.byteOffset, archive.length);
|
|
19
|
+
const end = bytes.lastIndexOf(Buffer.from([
|
|
20
|
+
80,
|
|
21
|
+
75,
|
|
22
|
+
5,
|
|
23
|
+
6
|
|
24
|
+
]));
|
|
25
|
+
if (end < 0 || end + 22 > bytes.length) return format();
|
|
26
|
+
const directoryEnd = bytes.readUInt32LE(end + 16) + bytes.readUInt32LE(end + 12);
|
|
27
|
+
if (directoryEnd > bytes.length) return format();
|
|
28
|
+
for (let offset = bytes.readUInt32LE(end + 16); offset + 46 <= directoryEnd;) {
|
|
29
|
+
if (bytes.readUInt32LE(offset) !== 33639248) return format();
|
|
30
|
+
const nameLength = bytes.readUInt16LE(offset + 28);
|
|
31
|
+
if (bytes.toString("utf8", offset + 46, offset + 46 + nameLength) === name) {
|
|
32
|
+
const size = bytes.readUInt32LE(offset + 20);
|
|
33
|
+
const header = bytes.readUInt32LE(offset + 42);
|
|
34
|
+
if (header + 30 > bytes.length || bytes.readUInt32LE(header) !== 67324752) return format();
|
|
35
|
+
const start = header + 30 + bytes.readUInt16LE(header + 26) + bytes.readUInt16LE(header + 28);
|
|
36
|
+
if (start + size > bytes.length) return format();
|
|
37
|
+
return Result.succeed({
|
|
38
|
+
method: bytes.readUInt16LE(offset + 10),
|
|
39
|
+
start,
|
|
40
|
+
end: start + size
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
offset += 46 + nameLength + bytes.readUInt16LE(offset + 30) + bytes.readUInt16LE(offset + 32);
|
|
44
|
+
}
|
|
45
|
+
return Result.fail(new ZipError({ reason: "entry" }));
|
|
46
|
+
};
|
|
47
|
+
const inflate = (data) => Effect.callback((resume) => {
|
|
48
|
+
inflateRaw(data, (error, output) => resume(error ? Effect.fail(new ZipError({ reason: "format" })) : Effect.succeed(output)));
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Extract one entry of a zip archive. Convex publishes the local backend as a
|
|
52
|
+
* single-file zip, stored or deflated.
|
|
53
|
+
*/
|
|
54
|
+
const extract = Effect.fn("Zip.extract")(function* (archive, name, destination) {
|
|
55
|
+
const fs = yield* FileSystem.FileSystem;
|
|
56
|
+
const bytes = yield* fs.readFile(archive).pipe(Effect.mapError(() => new ZipError({ reason: "io" })));
|
|
57
|
+
const entry = yield* Effect.fromResult(locate(bytes, name));
|
|
58
|
+
const data = bytes.subarray(entry.start, entry.end);
|
|
59
|
+
const content = entry.method === 8 ? yield* inflate(data) : entry.method === 0 ? data : yield* new ZipError({ reason: "format" });
|
|
60
|
+
yield* fs.writeFile(destination, content, { mode: 493 }).pipe(Effect.mapError(() => new ZipError({ reason: "io" })));
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
63
|
+
export { ZipError, extract };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=Zip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Zip.js","names":[],"sources":["../../src/internal/Zip.ts"],"sourcesContent":["import { inflateRaw } from \"node:zlib\";\nimport * as Effect from \"effect/Effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Result from \"effect/Result\";\nimport * as Schema from \"effect/Schema\";\n\nexport class ZipError extends Schema.TaggedError<ZipError>()(\"ZipError\", {\n reason: Schema.Literals([\"format\", \"entry\", \"io\"]),\n}) {}\n\ninterface Entry {\n readonly method: number;\n readonly start: number;\n readonly end: number;\n}\n\nconst format = () => Result.fail(new ZipError({ reason: \"format\" }));\n\n/**\n * Locate one entry's compressed bytes through the archive's central directory.\n * No Zip64: a single-file release far under 4 GiB never needs it.\n */\nconst locate = (\n archive: Uint8Array,\n name: string,\n): Result.Result<Entry, ZipError> => {\n const bytes = Buffer.from(archive.buffer, archive.byteOffset, archive.length);\n const end = bytes.lastIndexOf(Buffer.from([0x50, 0x4b, 0x05, 0x06]));\n if (end < 0 || end + 22 > bytes.length) return format();\n const directoryEnd =\n bytes.readUInt32LE(end + 16) + bytes.readUInt32LE(end + 12);\n if (directoryEnd > bytes.length) return format();\n for (\n let offset = bytes.readUInt32LE(end + 16);\n offset + 46 <= directoryEnd;\n ) {\n if (bytes.readUInt32LE(offset) !== 0x02014b50) return format();\n const nameLength = bytes.readUInt16LE(offset + 28);\n const entryName = bytes.toString(\n \"utf8\",\n offset + 46,\n offset + 46 + nameLength,\n );\n if (entryName === name) {\n const size = bytes.readUInt32LE(offset + 20);\n const header = bytes.readUInt32LE(offset + 42);\n if (\n header + 30 > bytes.length ||\n bytes.readUInt32LE(header) !== 0x04034b50\n )\n return format();\n const start =\n header +\n 30 +\n bytes.readUInt16LE(header + 26) +\n bytes.readUInt16LE(header + 28);\n if (start + size > bytes.length) return format();\n return Result.succeed({\n method: bytes.readUInt16LE(offset + 10),\n start,\n end: start + size,\n });\n }\n offset +=\n 46 +\n nameLength +\n bytes.readUInt16LE(offset + 30) +\n bytes.readUInt16LE(offset + 32);\n }\n return Result.fail(new ZipError({ reason: \"entry\" }));\n};\n\nconst inflate = (data: Uint8Array) =>\n Effect.callback<Uint8Array, ZipError>((resume) => {\n inflateRaw(data, (error, output) =>\n resume(\n error\n ? Effect.fail(new ZipError({ reason: \"format\" }))\n : Effect.succeed(output),\n ),\n );\n });\n\n/**\n * Extract one entry of a zip archive. Convex publishes the local backend as a\n * single-file zip, stored or deflated.\n */\nexport const extract = Effect.fn(\"Zip.extract\")(function* (\n archive: string,\n name: string,\n destination: string,\n) {\n const fs = yield* FileSystem.FileSystem;\n const bytes = yield* fs\n .readFile(archive)\n .pipe(Effect.mapError(() => new ZipError({ reason: \"io\" })));\n const entry = yield* Effect.fromResult(locate(bytes, name));\n const data = bytes.subarray(entry.start, entry.end);\n const content =\n entry.method === 8\n ? yield* inflate(data)\n : entry.method === 0\n ? data\n : yield* new ZipError({ reason: \"format\" });\n yield* fs\n .writeFile(destination, content, { mode: 0o755 })\n .pipe(Effect.mapError(() => new ZipError({ reason: \"io\" })));\n});\n"],"mappings":";;;;;;AAMA,IAAa,WAAb,cAA8B,OAAO,YAAsB,CAAC,CAAC,YAAY,EACvE,QAAQ,OAAO,SAAS;CAAC;CAAU;CAAS;AAAI,CAAC,EACnD,CAAC,CAAC,CAAC,CAAC;AAQJ,MAAM,eAAe,OAAO,KAAK,IAAI,SAAS,EAAE,QAAQ,SAAS,CAAC,CAAC;;;;;AAMnE,MAAM,UACJ,SACA,SACmC;CACnC,MAAM,QAAQ,OAAO,KAAK,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,MAAM;CAC5E,MAAM,MAAM,MAAM,YAAY,OAAO,KAAK;EAAC;EAAM;EAAM;EAAM;CAAI,CAAC,CAAC;CACnE,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,QAAQ,OAAO,OAAO;CACtD,MAAM,eACJ,MAAM,aAAa,MAAM,EAAE,IAAI,MAAM,aAAa,MAAM,EAAE;CAC5D,IAAI,eAAe,MAAM,QAAQ,OAAO,OAAO;CAC/C,KACE,IAAI,SAAS,MAAM,aAAa,MAAM,EAAE,GACxC,SAAS,MAAM,eACf;EACA,IAAI,MAAM,aAAa,MAAM,MAAM,UAAY,OAAO,OAAO;EAC7D,MAAM,aAAa,MAAM,aAAa,SAAS,EAAE;EAMjD,IALkB,MAAM,SACtB,QACA,SAAS,IACT,SAAS,KAAK,UAEJ,MAAM,MAAM;GACtB,MAAM,OAAO,MAAM,aAAa,SAAS,EAAE;GAC3C,MAAM,SAAS,MAAM,aAAa,SAAS,EAAE;GAC7C,IACE,SAAS,KAAK,MAAM,UACpB,MAAM,aAAa,MAAM,MAAM,UAE/B,OAAO,OAAO;GAChB,MAAM,QACJ,SACA,KACA,MAAM,aAAa,SAAS,EAAE,IAC9B,MAAM,aAAa,SAAS,EAAE;GAChC,IAAI,QAAQ,OAAO,MAAM,QAAQ,OAAO,OAAO;GAC/C,OAAO,OAAO,QAAQ;IACpB,QAAQ,MAAM,aAAa,SAAS,EAAE;IACtC;IACA,KAAK,QAAQ;GACf,CAAC;EACH;EACA,UACE,KACA,aACA,MAAM,aAAa,SAAS,EAAE,IAC9B,MAAM,aAAa,SAAS,EAAE;CAClC;CACA,OAAO,OAAO,KAAK,IAAI,SAAS,EAAE,QAAQ,QAAQ,CAAC,CAAC;AACtD;AAEA,MAAM,WAAW,SACf,OAAO,UAAgC,WAAW;CAChD,WAAW,OAAO,OAAO,WACvB,OACE,QACI,OAAO,KAAK,IAAI,SAAS,EAAE,QAAQ,SAAS,CAAC,CAAC,IAC9C,OAAO,QAAQ,MAAM,CAC3B,CACF;AACF,CAAC;;;;;AAMH,MAAa,UAAU,OAAO,GAAG,aAAa,CAAC,CAAC,WAC9C,SACA,MACA,aACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,QAAQ,OAAO,GAClB,SAAS,OAAO,CAAC,CACjB,KAAK,OAAO,eAAe,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC;CAC7D,MAAM,QAAQ,OAAO,OAAO,WAAW,OAAO,OAAO,IAAI,CAAC;CAC1D,MAAM,OAAO,MAAM,SAAS,MAAM,OAAO,MAAM,GAAG;CAClD,MAAM,UACJ,MAAM,WAAW,IACb,OAAO,QAAQ,IAAI,IACnB,MAAM,WAAW,IACf,OACA,OAAO,IAAI,SAAS,EAAE,QAAQ,SAAS,CAAC;CAChD,OAAO,GACJ,UAAU,aAAa,SAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CAChD,KAAK,OAAO,eAAe,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC;AAC/D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaganref/confect-alchemy",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "10.3.1-fork.1",
|
|
4
|
+
"dependencies": {
|
|
5
|
+
"@convex-dev/platform": "^0.1.10"
|
|
6
|
+
},
|
|
7
|
+
"peerDependencies": {
|
|
8
|
+
"@confect/cli": "npm:@gaganref/confect-cli@10.3.1-fork.1",
|
|
9
|
+
"@confect/core": "npm:@gaganref/confect-core@10.3.1-fork.1",
|
|
10
|
+
"alchemy": "2.0.0-beta.82",
|
|
11
|
+
"convex": "^1.32.0",
|
|
12
|
+
"effect": "^4.0.0"
|
|
13
|
+
},
|
|
14
|
+
"description": "Alchemy resources for deploying Convex and Confect applications",
|
|
15
|
+
"author": "RJ Dellecese",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/gaganref/confect"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=24"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./*": {
|
|
28
|
+
"types": "./dist/*.d.ts",
|
|
29
|
+
"default": "./dist/*.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
"./internal/*": null
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"CHANGELOG.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"README.md",
|
|
38
|
+
"dist",
|
|
39
|
+
"!dist/*.tsbuildinfo",
|
|
40
|
+
"package.json",
|
|
41
|
+
"src"
|
|
42
|
+
],
|
|
43
|
+
"homepage": "https://confect.dev",
|
|
44
|
+
"keywords": [
|
|
45
|
+
"alchemy",
|
|
46
|
+
"convex",
|
|
47
|
+
"effect"
|
|
48
|
+
],
|
|
49
|
+
"license": "ISC",
|
|
50
|
+
"main": "./dist/index.js",
|
|
51
|
+
"module": "./dist/index.js",
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@confect/cli": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/gaganref/confect.git"
|
|
60
|
+
},
|
|
61
|
+
"sideEffects": false,
|
|
62
|
+
"type": "module",
|
|
63
|
+
"types": "./dist/index.d.ts",
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"registry": "https://registry.npmjs.org",
|
|
66
|
+
"access": "public"
|
|
67
|
+
}
|
|
6
68
|
}
|
package/src/Backend.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type * as Manifest from "@confect/core/Manifest";
|
|
2
|
+
import type { Input } from "alchemy/Input";
|
|
3
|
+
import * as Namespace from "alchemy/Namespace";
|
|
4
|
+
import * as Output from "alchemy/Output";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import type * as Redacted from "effect/Redacted";
|
|
7
|
+
import { Code } from "./Code";
|
|
8
|
+
import { EnvironmentVariables } from "./EnvironmentVariables";
|
|
9
|
+
|
|
10
|
+
interface CommonProps {
|
|
11
|
+
readonly deployment: {
|
|
12
|
+
readonly name: Input<string>;
|
|
13
|
+
readonly url: Input<string>;
|
|
14
|
+
};
|
|
15
|
+
readonly deployKey: Input<Redacted.Redacted<string>>;
|
|
16
|
+
readonly cwd?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Environment<M extends Manifest.Manifest> = {
|
|
20
|
+
readonly [K in keyof Manifest.Values<M>]: Input<
|
|
21
|
+
Exclude<Manifest.Values<M>[K], undefined>
|
|
22
|
+
>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type BackendProps<M extends Manifest.Manifest | undefined = undefined> =
|
|
26
|
+
CommonProps &
|
|
27
|
+
(M extends Manifest.Manifest
|
|
28
|
+
? { readonly manifest: M } & ({} extends Manifest.Values<M>
|
|
29
|
+
? { readonly env?: Environment<NoInfer<M>> }
|
|
30
|
+
: { readonly env: Environment<NoInfer<M>> })
|
|
31
|
+
: {
|
|
32
|
+
readonly manifest?: never;
|
|
33
|
+
readonly env?: Readonly<
|
|
34
|
+
Record<string, Input<string | Redacted.Redacted<string>>>
|
|
35
|
+
>;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Deploy a declared backend with env typed by its generated manifest.
|
|
40
|
+
*/
|
|
41
|
+
export function Backend<
|
|
42
|
+
const M extends Manifest.Manifest,
|
|
43
|
+
const E extends Environment<NoInfer<M>> = Environment<NoInfer<M>>,
|
|
44
|
+
>(
|
|
45
|
+
id: string,
|
|
46
|
+
props: BackendProps<M> & {
|
|
47
|
+
readonly env?: E &
|
|
48
|
+
Readonly<Record<Exclude<keyof E, keyof M["env"]>, never>>;
|
|
49
|
+
},
|
|
50
|
+
): ReturnType<typeof backend>;
|
|
51
|
+
export function Backend(
|
|
52
|
+
id: string,
|
|
53
|
+
props: BackendProps,
|
|
54
|
+
): ReturnType<typeof backend>;
|
|
55
|
+
export function Backend(
|
|
56
|
+
id: string,
|
|
57
|
+
props: CommonProps & {
|
|
58
|
+
readonly env?: Readonly<
|
|
59
|
+
Record<string, Input<string | Redacted.Redacted<string>>>
|
|
60
|
+
>;
|
|
61
|
+
},
|
|
62
|
+
): ReturnType<typeof backend> {
|
|
63
|
+
return backend(id, props);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const backend = (
|
|
67
|
+
id: string,
|
|
68
|
+
props: CommonProps & {
|
|
69
|
+
readonly env?: Readonly<
|
|
70
|
+
Record<string, Input<string | Redacted.Redacted<string>>>
|
|
71
|
+
>;
|
|
72
|
+
},
|
|
73
|
+
) =>
|
|
74
|
+
Namespace.push(
|
|
75
|
+
id,
|
|
76
|
+
Effect.gen(function* () {
|
|
77
|
+
const environment =
|
|
78
|
+
props.env === undefined
|
|
79
|
+
? undefined
|
|
80
|
+
: yield* EnvironmentVariables("Environment", {
|
|
81
|
+
url: props.deployment.url,
|
|
82
|
+
deployKey: props.deployKey,
|
|
83
|
+
variables: props.env,
|
|
84
|
+
});
|
|
85
|
+
const code = yield* Code("Code", {
|
|
86
|
+
deployment: {
|
|
87
|
+
name: props.deployment.name,
|
|
88
|
+
url: environment
|
|
89
|
+
? Output.map(
|
|
90
|
+
Output.all(environment.variables, environment.url),
|
|
91
|
+
([, url]) => url,
|
|
92
|
+
)
|
|
93
|
+
: props.deployment.url,
|
|
94
|
+
},
|
|
95
|
+
deployKey: props.deployKey,
|
|
96
|
+
...(props.cwd === undefined ? {} : { cwd: props.cwd }),
|
|
97
|
+
prepare: { command: "confect", args: ["codegen"] },
|
|
98
|
+
});
|
|
99
|
+
return { code, environment, name: code.name, url: code.url };
|
|
100
|
+
}),
|
|
101
|
+
);
|