theokit 0.9.2 → 0.9.4
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/{actions-virtual-module-52C6RKDK.js → actions-virtual-module-WNA77RHT.js} +3 -4
- package/dist/{actions-virtual-module-52C6RKDK.js.map → actions-virtual-module-WNA77RHT.js.map} +1 -1
- package/dist/{add-2ZFFGGE4.js → add-W6FNTAFS.js} +1 -2
- package/dist/{add-2ZFFGGE4.js.map → add-W6FNTAFS.js.map} +1 -1
- package/dist/{app-typed-client-A4CP55NM.js → app-typed-client-XGCP563T.js} +3 -4
- package/dist/{app-typed-client-A4CP55NM.js.map → app-typed-client-XGCP563T.js.map} +1 -1
- package/dist/{aws-lambda-WT4HRBNL.js → aws-lambda-EC74AYN4.js} +1 -2
- package/dist/{aws-lambda-WT4HRBNL.js.map → aws-lambda-EC74AYN4.js.map} +1 -1
- package/dist/{broadcast-QOQGUNNK.js → broadcast-LUMOJIJT.js} +1 -2
- package/dist/{build-S73OUGYP.js → build-B7B5Q7TV.js} +6 -7
- package/dist/{build-S73OUGYP.js.map → build-B7B5Q7TV.js.map} +1 -1
- package/dist/{build-request-body-preview-II2235E6.js → build-request-body-preview-QMWD2IXK.js} +1 -2
- package/dist/{build-request-body-preview-II2235E6.js.map → build-request-body-preview-QMWD2IXK.js.map} +1 -1
- package/dist/{bun-UTDVN6R4.js → bun-FUYLUP62.js} +1 -2
- package/dist/{bun-UTDVN6R4.js.map → bun-FUYLUP62.js.map} +1 -1
- package/dist/{check-F3UDYSZ4.js → check-PD2FTKMM.js} +2 -3
- package/dist/{check-F3UDYSZ4.js.map → check-PD2FTKMM.js.map} +1 -1
- package/dist/{chunk-2DXLN7FF.js → chunk-AUNPGO2V.js} +8 -8
- package/dist/{chunk-Z374WCMZ.js → chunk-BLT7PPII.js} +5 -5
- package/dist/{chunk-256WFVQQ.js → chunk-HTNVOG34.js} +2 -2
- package/dist/cli/index.js +14 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/{cloudflare-U5GYYI47.js → cloudflare-KHF7JJZ7.js} +1 -2
- package/dist/{cloudflare-U5GYYI47.js.map → cloudflare-KHF7JJZ7.js.map} +1 -1
- package/dist/{configure-agent-registry-6YGTJYBC.js → configure-agent-registry-ZOBVU3MV.js} +1 -2
- package/dist/{configure-agent-registry-6YGTJYBC.js.map → configure-agent-registry-ZOBVU3MV.js.map} +1 -1
- package/dist/{db-ZPDW3UCU.js → db-3UNAMSFK.js} +1 -2
- package/dist/{db-ZPDW3UCU.js.map → db-3UNAMSFK.js.map} +1 -1
- package/dist/{deno-deploy-3QHJ3AJQ.js → deno-deploy-O4JP56BM.js} +1 -2
- package/dist/{deno-deploy-3QHJ3AJQ.js.map → deno-deploy-O4JP56BM.js.map} +1 -1
- package/dist/{dev-WLUBGVDM.js → dev-KHBWZUY3.js} +7 -8
- package/dist/{dev-WLUBGVDM.js.map → dev-KHBWZUY3.js.map} +1 -1
- package/dist/{dev-emit-OUPI7NAQ.js → dev-emit-FEFEDLZF.js} +1 -2
- package/dist/{dev-emit-OUPI7NAQ.js.map → dev-emit-FEFEDLZF.js.map} +1 -1
- package/dist/{dispatcher-EJME6C6M.js → dispatcher-EJHL6JMJ.js} +1 -2
- package/dist/{dispatcher-EJME6C6M.js.map → dispatcher-EJHL6JMJ.js.map} +1 -1
- package/dist/{docker-AZSJFLP2.js → docker-LZZB4D5E.js} +1 -2
- package/dist/{docker-AZSJFLP2.js.map → docker-LZZB4D5E.js.map} +1 -1
- package/dist/{generate-ODDAYXNR.js → generate-OMKHQ7OM.js} +1 -2
- package/dist/{generate-ODDAYXNR.js.map → generate-OMKHQ7OM.js.map} +1 -1
- package/dist/{info-CSGWIAXA.js → info-DWUBVHHF.js} +1 -2
- package/dist/{info-CSGWIAXA.js.map → info-DWUBVHHF.js.map} +1 -1
- package/dist/{netlify-DUB7BZ4E.js → netlify-E63264H6.js} +1 -2
- package/dist/{netlify-DUB7BZ4E.js.map → netlify-E63264H6.js.map} +1 -1
- package/dist/{node-ELE236WK.js → node-XVZAGVVO.js} +1 -2
- package/dist/{openapi-NXGRXABL.js → openapi-NPFP4WZY.js} +1 -2
- package/dist/{openapi-NXGRXABL.js.map → openapi-NPFP4WZY.js.map} +1 -1
- package/dist/registry-WHMRQG5F.js +24 -0
- package/dist/{registry-B7FA6KMI.js.map → registry-WHMRQG5F.js.map} +1 -1
- package/dist/{router-RGZFX75G.js → router-TLEAOFID.js} +1 -2
- package/dist/{router-RGZFX75G.js.map → router-TLEAOFID.js.map} +1 -1
- package/dist/{routes-6A5XFGF7.js → routes-GHMCCV7V.js} +1 -2
- package/dist/{routes-6A5XFGF7.js.map → routes-GHMCCV7V.js.map} +1 -1
- package/dist/{scan-I5PT6TUX.js → scan-7MJC6PYU.js} +1 -2
- package/dist/{schema-NPI4NJEF.js → schema-7K4SFSA3.js} +1 -2
- package/dist/{server-error-to-envelope-W7KJ6AXW.js → server-error-to-envelope-OHQCV66V.js} +1 -2
- package/dist/{server-routes-hmr-GZJGPWMS.js → server-routes-hmr-VZXOP5YT.js} +1 -2
- package/dist/{server-routes-hmr-GZJGPWMS.js.map → server-routes-hmr-VZXOP5YT.js.map} +1 -1
- package/dist/{services-json-6SHWMYJH.js → services-json-Y3XODNB5.js} +1 -2
- package/dist/{services-json-6SHWMYJH.js.map → services-json-Y3XODNB5.js.map} +1 -1
- package/dist/{services-typed-client-24VBMDOF.js → services-typed-client-6JPFJJ56.js} +1 -2
- package/dist/{services-typed-client-24VBMDOF.js.map → services-typed-client-6JPFJJ56.js.map} +1 -1
- package/dist/{start-ALXGQIVT.js → start-ETABX6ZK.js} +8 -9
- package/dist/{start-ALXGQIVT.js.map → start-ETABX6ZK.js.map} +1 -1
- package/dist/{static-YWF2M6YT.js → static-3JXPPDIH.js} +1 -2
- package/dist/{static-YWF2M6YT.js.map → static-3JXPPDIH.js.map} +1 -1
- package/dist/{storage-manager-D5KBXXKB.js → storage-manager-ZSQFLEYT.js} +1 -2
- package/dist/{storage-manager-D5KBXXKB.js.map → storage-manager-ZSQFLEYT.js.map} +1 -1
- package/dist/{theo-cloud-EUK56I7O.js → theo-cloud-I3KQE3JN.js} +1 -2
- package/dist/{theo-cloud-EUK56I7O.js.map → theo-cloud-I3KQE3JN.js.map} +1 -1
- package/dist/{upgrade-readiness-GUJBCJIS.js → upgrade-readiness-ACGW44JC.js} +1 -2
- package/dist/{upgrade-readiness-GUJBCJIS.js.map → upgrade-readiness-ACGW44JC.js.map} +1 -1
- package/dist/{vercel-LGDQWYZD.js → vercel-6VWQTIUL.js} +1 -2
- package/dist/{vercel-LGDQWYZD.js.map → vercel-6VWQTIUL.js.map} +1 -1
- package/dist/{vite-plugin-TEHQFA6D.js → vite-plugin-AYWW2YTE.js} +4 -5
- package/package.json +7 -3
- package/dist/chunk-KT3MWNZG.js +0 -18
- package/dist/dist-IW7G5Y23.js +0 -19454
- package/dist/dist-IW7G5Y23.js.map +0 -1
- package/dist/lib-JBI3XXH3.js +0 -781
- package/dist/lib-JBI3XXH3.js.map +0 -1
- package/dist/proper-lockfile-MVKMQGFK.js +0 -1592
- package/dist/proper-lockfile-MVKMQGFK.js.map +0 -1
- package/dist/registry-B7FA6KMI.js +0 -25
- package/dist/sqlite-vec-54KB4RD3.js +0 -41
- package/dist/sqlite-vec-54KB4RD3.js.map +0 -1
- package/dist/vite-plugin-TEHQFA6D.js.map +0 -1
- /package/dist/{broadcast-QOQGUNNK.js.map → broadcast-LUMOJIJT.js.map} +0 -0
- /package/dist/{chunk-2DXLN7FF.js.map → chunk-AUNPGO2V.js.map} +0 -0
- /package/dist/{chunk-Z374WCMZ.js.map → chunk-BLT7PPII.js.map} +0 -0
- /package/dist/{chunk-256WFVQQ.js.map → chunk-HTNVOG34.js.map} +0 -0
- /package/dist/{chunk-KT3MWNZG.js.map → node-XVZAGVVO.js.map} +0 -0
- /package/dist/{node-ELE236WK.js.map → scan-7MJC6PYU.js.map} +0 -0
- /package/dist/{scan-I5PT6TUX.js.map → schema-7K4SFSA3.js.map} +0 -0
- /package/dist/{schema-NPI4NJEF.js.map → server-error-to-envelope-OHQCV66V.js.map} +0 -0
- /package/dist/{server-error-to-envelope-W7KJ6AXW.js.map → vite-plugin-AYWW2YTE.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/storage/storage-manager.ts"],"sourcesContent":["/**\n * StorageManager — per-process singleton coordinating pluggable storage\n * adapters (Postgres pools, Redis clients, in-memory adapters).\n *\n * Architectural decisions: see ADR-0007 (docs/adr/0007-storage-manager-singleton.md).\n *\n * Lifecycle:\n * 1. `getStorageManager()` returns the singleton (D1).\n * 2. `configure(storageConfig)` honored once per process (D3). Second call\n * warns and is ignored.\n * 3. `usePostgres(dbName, factory)` / `useRedis(serverName, factory)`\n * lazily create + cache (D2). Factory invoked exactly once per name.\n * 4. `register(adapter)` opts the adapter into the SIGTERM drain (D6).\n * 5. `dispose()` drains in parallel — adapters + pools + Redis (D5).\n * Idempotent. Adapter errors logged + swallowed (do not block shutdown).\n *\n * Test seam: `__resetForTests()` resets state. Test files MUST call it from\n * `beforeEach` to avoid pollution across `it` blocks (EC-3).\n */\n\nimport type {\n PoolLike,\n PostgresDatabaseConfig,\n RedisLike,\n RedisServerConfig,\n ServerConfig,\n StorageAdapter,\n StorageConfig,\n} from './storage-types.js'\n\nexport type PostgresFactory = (server: ServerConfig, db: PostgresDatabaseConfig) => PoolLike\nexport type RedisFactory = (config: RedisServerConfig) => RedisLike\n\nexport class StorageManager {\n #config: StorageConfig | undefined\n readonly #dbPools = new Map<string, PoolLike>()\n readonly #redisClients = new Map<string, RedisLike>()\n readonly #genericClients = new Map<string, unknown>()\n readonly #adapters = new Set<StorageAdapter>()\n #disposed = false\n\n /**\n * Apply the storage config block from `theo.config.ts`. Second call warns\n * and is ignored (D3). Reset only via `__resetForTests()`.\n */\n configure(config: StorageConfig): void {\n if (this.#config !== undefined) {\n console.warn('[theokit] StorageManager already configured; ignoring second configure() call')\n return\n }\n this.#config = config\n }\n\n /**\n * Generic cache + factory for ANY client (MySQL, Mongo, Turso, DynamoDB, …).\n * Returns the cached client OR invokes the factory and caches its return.\n *\n * EC-1 FIX: cache-hit check uses `Map.has(name)`, NOT `cached !== undefined`.\n * Factories returning `null` or `undefined` are valid (lazy connect, stubs);\n * the `!== undefined` check would re-invoke the factory infinitely for\n * undefined returns AND silently cache `null` cast as `T`.\n *\n * EC-2 (documented type hole): `useStorage<A>('x', fA)` followed by\n * `useStorage<B>('x', fB)` returns the cached A cast as B. Same trade-off\n * as `Map<string, unknown>` — caller is responsible for using unique names\n * per type.\n *\n * Lifecycle: generic clients are NOT auto-drained by `dispose()`. Call\n * `manager.register({ name, dispose })` separately to participate.\n *\n * Reserved name prefixes (do NOT use): `__pg:`, `__redis:`, `__unstorage:`,\n * `__db0:` — these are used internally by the typed helpers.\n */\n useStorage<T>(name: string, factory: () => T): T {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n if (this.#genericClients.has(name)) return this.#genericClients.get(name) as T\n const client = factory()\n this.#genericClients.set(name, client)\n return client\n }\n\n /**\n * Return a Postgres pool for the named database. Lazy + cached. Factory\n * invoked exactly once per `dbName` (D2).\n *\n * Throws if:\n * - manager is disposed\n * - `dbName` is not in `config.databases`\n * - the referenced server is not in `config.servers` (EC-2: deferred\n * cross-field validation surfaces here, not at configure time)\n */\n usePostgres(dbName: string, factory: PostgresFactory): PoolLike {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n const cached = this.#dbPools.get(dbName)\n if (cached !== undefined) return cached\n const dbConfig = this.#config?.databases?.[dbName]\n if (dbConfig === undefined) {\n throw new Error(\n `Database \"${dbName}\" not configured. Add it to theo.config.ts > storage.databases.`,\n )\n }\n const server = this.#config?.servers?.[dbConfig.server]\n if (server === undefined) {\n throw new Error(\n `Server \"${dbConfig.server}\" referenced by database \"${dbName}\" not found in theo.config.ts > storage.servers.`,\n )\n }\n const pool = factory(server, dbConfig)\n this.#dbPools.set(dbName, pool)\n return pool\n }\n\n /**\n * Return a Redis client for the named server. Lazy + cached. Factory\n * invoked exactly once per `serverName`.\n */\n useRedis(serverName: string, factory: RedisFactory): RedisLike {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n const cached = this.#redisClients.get(serverName)\n if (cached !== undefined) return cached\n const serverConfig = this.#config?.redis?.[serverName]\n if (serverConfig === undefined) {\n throw new Error(\n `Redis server \"${serverName}\" not configured. Add it to theo.config.ts > storage.redis.`,\n )\n }\n const client = factory(serverConfig)\n this.#redisClients.set(serverName, client)\n return client\n }\n\n /**\n * Register an adapter for graceful shutdown coordination (D6).\n *\n * EC-4: throws if the manager is already disposed — prevents silent\n * leaks in test seams that reset+reuse the manager.\n */\n register(adapter: StorageAdapter): void {\n if (this.#disposed) {\n throw new Error(\n 'StorageManager is disposed; cannot register new adapter. Call __resetForTests() in test setup.',\n )\n }\n this.#adapters.add(adapter)\n }\n\n /**\n * Drain in parallel — registered adapters, then PG pools, then Redis\n * clients. Idempotent (D5). Errors per resource are logged and swallowed\n * — they do NOT prevent shutdown of the remaining resources.\n *\n * Caller must wrap in a timeout if a bound is needed (EC-7: documented).\n * In production, `start.ts` wraps the whole shutdown sequence in a 25 s\n * force-exit timer.\n */\n async dispose(): Promise<void> {\n if (this.#disposed) return\n this.#disposed = true\n\n const adapterDrains = Array.from(this.#adapters).map((a) =>\n a.dispose().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] adapter \"${a.name}\" dispose failed: ${msg}`)\n }),\n )\n await Promise.all(adapterDrains)\n\n const poolCloses = Array.from(this.#dbPools.values()).map((pool) => {\n // EC-5: pool without `.end()` is silently skipped (TS already enforces\n // shape at factory signature; this is the graceful runtime fallback if\n // user bypasses via `as` cast).\n if (pool.end === undefined) return Promise.resolve()\n return pool.end().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] PG pool close failed: ${msg}`)\n })\n })\n await Promise.all(poolCloses)\n\n const redisCloses = Array.from(this.#redisClients.values()).map((c) =>\n c.quit().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] Redis quit failed, falling back to disconnect: ${msg}`)\n c.disconnect()\n }),\n )\n await Promise.all(redisCloses)\n\n this.#dbPools.clear()\n this.#redisClients.clear()\n this.#genericClients.clear()\n this.#adapters.clear()\n }\n\n /** @internal Testing seam — resets state so a single test file can re-use the singleton across `it` blocks (EC-3). */\n __resetForTests(): void {\n this.#config = undefined\n this.#dbPools.clear()\n this.#redisClients.clear()\n this.#genericClients.clear()\n this.#adapters.clear()\n this.#disposed = false\n }\n\n /** @internal Introspection helper for tests. */\n __isConfiguredForTests(): boolean {\n return this.#config !== undefined\n }\n\n /** @internal Introspection helper for tests. */\n __isDisposedForTests(): boolean {\n return this.#disposed\n }\n}\n\n// Singleton — one StorageManager per process (D1).\nlet __singleton: StorageManager | undefined\n\nexport function getStorageManager(): StorageManager {\n __singleton ??= new StorageManager()\n return __singleton\n}\n\n/** @internal Drops the module-level singleton — used by tests that need a fresh manager. */\nexport function __resetSingletonForTests(): void {\n __singleton = undefined\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server/storage/storage-manager.ts"],"sourcesContent":["/**\n * StorageManager — per-process singleton coordinating pluggable storage\n * adapters (Postgres pools, Redis clients, in-memory adapters).\n *\n * Architectural decisions: see ADR-0007 (docs/adr/0007-storage-manager-singleton.md).\n *\n * Lifecycle:\n * 1. `getStorageManager()` returns the singleton (D1).\n * 2. `configure(storageConfig)` honored once per process (D3). Second call\n * warns and is ignored.\n * 3. `usePostgres(dbName, factory)` / `useRedis(serverName, factory)`\n * lazily create + cache (D2). Factory invoked exactly once per name.\n * 4. `register(adapter)` opts the adapter into the SIGTERM drain (D6).\n * 5. `dispose()` drains in parallel — adapters + pools + Redis (D5).\n * Idempotent. Adapter errors logged + swallowed (do not block shutdown).\n *\n * Test seam: `__resetForTests()` resets state. Test files MUST call it from\n * `beforeEach` to avoid pollution across `it` blocks (EC-3).\n */\n\nimport type {\n PoolLike,\n PostgresDatabaseConfig,\n RedisLike,\n RedisServerConfig,\n ServerConfig,\n StorageAdapter,\n StorageConfig,\n} from './storage-types.js'\n\nexport type PostgresFactory = (server: ServerConfig, db: PostgresDatabaseConfig) => PoolLike\nexport type RedisFactory = (config: RedisServerConfig) => RedisLike\n\nexport class StorageManager {\n #config: StorageConfig | undefined\n readonly #dbPools = new Map<string, PoolLike>()\n readonly #redisClients = new Map<string, RedisLike>()\n readonly #genericClients = new Map<string, unknown>()\n readonly #adapters = new Set<StorageAdapter>()\n #disposed = false\n\n /**\n * Apply the storage config block from `theo.config.ts`. Second call warns\n * and is ignored (D3). Reset only via `__resetForTests()`.\n */\n configure(config: StorageConfig): void {\n if (this.#config !== undefined) {\n console.warn('[theokit] StorageManager already configured; ignoring second configure() call')\n return\n }\n this.#config = config\n }\n\n /**\n * Generic cache + factory for ANY client (MySQL, Mongo, Turso, DynamoDB, …).\n * Returns the cached client OR invokes the factory and caches its return.\n *\n * EC-1 FIX: cache-hit check uses `Map.has(name)`, NOT `cached !== undefined`.\n * Factories returning `null` or `undefined` are valid (lazy connect, stubs);\n * the `!== undefined` check would re-invoke the factory infinitely for\n * undefined returns AND silently cache `null` cast as `T`.\n *\n * EC-2 (documented type hole): `useStorage<A>('x', fA)` followed by\n * `useStorage<B>('x', fB)` returns the cached A cast as B. Same trade-off\n * as `Map<string, unknown>` — caller is responsible for using unique names\n * per type.\n *\n * Lifecycle: generic clients are NOT auto-drained by `dispose()`. Call\n * `manager.register({ name, dispose })` separately to participate.\n *\n * Reserved name prefixes (do NOT use): `__pg:`, `__redis:`, `__unstorage:`,\n * `__db0:` — these are used internally by the typed helpers.\n */\n useStorage<T>(name: string, factory: () => T): T {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n if (this.#genericClients.has(name)) return this.#genericClients.get(name) as T\n const client = factory()\n this.#genericClients.set(name, client)\n return client\n }\n\n /**\n * Return a Postgres pool for the named database. Lazy + cached. Factory\n * invoked exactly once per `dbName` (D2).\n *\n * Throws if:\n * - manager is disposed\n * - `dbName` is not in `config.databases`\n * - the referenced server is not in `config.servers` (EC-2: deferred\n * cross-field validation surfaces here, not at configure time)\n */\n usePostgres(dbName: string, factory: PostgresFactory): PoolLike {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n const cached = this.#dbPools.get(dbName)\n if (cached !== undefined) return cached\n const dbConfig = this.#config?.databases?.[dbName]\n if (dbConfig === undefined) {\n throw new Error(\n `Database \"${dbName}\" not configured. Add it to theo.config.ts > storage.databases.`,\n )\n }\n const server = this.#config?.servers?.[dbConfig.server]\n if (server === undefined) {\n throw new Error(\n `Server \"${dbConfig.server}\" referenced by database \"${dbName}\" not found in theo.config.ts > storage.servers.`,\n )\n }\n const pool = factory(server, dbConfig)\n this.#dbPools.set(dbName, pool)\n return pool\n }\n\n /**\n * Return a Redis client for the named server. Lazy + cached. Factory\n * invoked exactly once per `serverName`.\n */\n useRedis(serverName: string, factory: RedisFactory): RedisLike {\n if (this.#disposed) throw new Error('StorageManager is disposed')\n const cached = this.#redisClients.get(serverName)\n if (cached !== undefined) return cached\n const serverConfig = this.#config?.redis?.[serverName]\n if (serverConfig === undefined) {\n throw new Error(\n `Redis server \"${serverName}\" not configured. Add it to theo.config.ts > storage.redis.`,\n )\n }\n const client = factory(serverConfig)\n this.#redisClients.set(serverName, client)\n return client\n }\n\n /**\n * Register an adapter for graceful shutdown coordination (D6).\n *\n * EC-4: throws if the manager is already disposed — prevents silent\n * leaks in test seams that reset+reuse the manager.\n */\n register(adapter: StorageAdapter): void {\n if (this.#disposed) {\n throw new Error(\n 'StorageManager is disposed; cannot register new adapter. Call __resetForTests() in test setup.',\n )\n }\n this.#adapters.add(adapter)\n }\n\n /**\n * Drain in parallel — registered adapters, then PG pools, then Redis\n * clients. Idempotent (D5). Errors per resource are logged and swallowed\n * — they do NOT prevent shutdown of the remaining resources.\n *\n * Caller must wrap in a timeout if a bound is needed (EC-7: documented).\n * In production, `start.ts` wraps the whole shutdown sequence in a 25 s\n * force-exit timer.\n */\n async dispose(): Promise<void> {\n if (this.#disposed) return\n this.#disposed = true\n\n const adapterDrains = Array.from(this.#adapters).map((a) =>\n a.dispose().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] adapter \"${a.name}\" dispose failed: ${msg}`)\n }),\n )\n await Promise.all(adapterDrains)\n\n const poolCloses = Array.from(this.#dbPools.values()).map((pool) => {\n // EC-5: pool without `.end()` is silently skipped (TS already enforces\n // shape at factory signature; this is the graceful runtime fallback if\n // user bypasses via `as` cast).\n if (pool.end === undefined) return Promise.resolve()\n return pool.end().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] PG pool close failed: ${msg}`)\n })\n })\n await Promise.all(poolCloses)\n\n const redisCloses = Array.from(this.#redisClients.values()).map((c) =>\n c.quit().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit] Redis quit failed, falling back to disconnect: ${msg}`)\n c.disconnect()\n }),\n )\n await Promise.all(redisCloses)\n\n this.#dbPools.clear()\n this.#redisClients.clear()\n this.#genericClients.clear()\n this.#adapters.clear()\n }\n\n /** @internal Testing seam — resets state so a single test file can re-use the singleton across `it` blocks (EC-3). */\n __resetForTests(): void {\n this.#config = undefined\n this.#dbPools.clear()\n this.#redisClients.clear()\n this.#genericClients.clear()\n this.#adapters.clear()\n this.#disposed = false\n }\n\n /** @internal Introspection helper for tests. */\n __isConfiguredForTests(): boolean {\n return this.#config !== undefined\n }\n\n /** @internal Introspection helper for tests. */\n __isDisposedForTests(): boolean {\n return this.#disposed\n }\n}\n\n// Singleton — one StorageManager per process (D1).\nlet __singleton: StorageManager | undefined\n\nexport function getStorageManager(): StorageManager {\n __singleton ??= new StorageManager()\n return __singleton\n}\n\n/** @internal Drops the module-level singleton — used by tests that need a fresh manager. */\nexport function __resetSingletonForTests(): void {\n __singleton = undefined\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
prepareTheoCloudArtifacts,
|
|
5
5
|
readManifest
|
|
6
6
|
} from "./chunk-CCVC6P6B.js";
|
|
7
|
-
import "./chunk-KT3MWNZG.js";
|
|
8
7
|
|
|
9
8
|
// src/adapters/theo-cloud.ts
|
|
10
9
|
var theoCloudAdapter = {
|
|
@@ -22,4 +21,4 @@ var theoCloudAdapter = {
|
|
|
22
21
|
export {
|
|
23
22
|
theoCloudAdapter
|
|
24
23
|
};
|
|
25
|
-
//# sourceMappingURL=theo-cloud-
|
|
24
|
+
//# sourceMappingURL=theo-cloud-I3KQE3JN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/theo-cloud.ts"],"sourcesContent":["/**\n * TheoCloud deploy adapter (Wave 3 v2.0 thin validator).\n *\n * Per ADR-0012 (mission expansion) + 2026-06-05 owner architectural decision,\n * TheoKit OSS does NOT emit K8s manifests. This adapter is intentionally thin:\n * 1. Validates the `.theokit/services.json` manifest shape (existing zod gate)\n * 2. Logs the services that will be deployed (visibility)\n * 3. Returns — bundle is ready for upload\n *\n * K8s manifest emission lives ENTIRELY inside TheoCloud (proprietary Go code)\n * upon receiving the upload. Rationale: OSS framework MUST emit formats\n * consumed by public/open systems OR neutral exchange formats; NÃO formats\n * consumed by proprietary closed systems. Exposing K8s shape here would leak\n * TheoCloud-internal infrastructure choices into the OSS surface.\n */\nimport type { TheoConfig } from '../config/schema.js'\nimport { prepareTheoCloudArtifacts, readManifest } from '../services/index.js'\n\nimport type { DeployAdapter } from './types.js'\n\nexport const theoCloudAdapter: DeployAdapter = {\n name: 'theo-cloud',\n\n build(_config: TheoConfig, cwd: string): Promise<void> {\n const manifest = readManifest(cwd)\n const artifacts = prepareTheoCloudArtifacts(manifest)\n const summary =\n artifacts.services.length === 0 ? 'TS-only app (no services)' : artifacts.services.join(', ')\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n `[theo-cloud] Wave 3 v2.0: manifest schemaVersion=${String(artifacts.manifestVersion)}, ` +\n `services=${summary}. Bundle ready for upload — TheoCloud emits K8s manifests internally upon deploy.`,\n )\n return Promise.resolve()\n },\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/theo-cloud.ts"],"sourcesContent":["/**\n * TheoCloud deploy adapter (Wave 3 v2.0 thin validator).\n *\n * Per ADR-0012 (mission expansion) + 2026-06-05 owner architectural decision,\n * TheoKit OSS does NOT emit K8s manifests. This adapter is intentionally thin:\n * 1. Validates the `.theokit/services.json` manifest shape (existing zod gate)\n * 2. Logs the services that will be deployed (visibility)\n * 3. Returns — bundle is ready for upload\n *\n * K8s manifest emission lives ENTIRELY inside TheoCloud (proprietary Go code)\n * upon receiving the upload. Rationale: OSS framework MUST emit formats\n * consumed by public/open systems OR neutral exchange formats; NÃO formats\n * consumed by proprietary closed systems. Exposing K8s shape here would leak\n * TheoCloud-internal infrastructure choices into the OSS surface.\n */\nimport type { TheoConfig } from '../config/schema.js'\nimport { prepareTheoCloudArtifacts, readManifest } from '../services/index.js'\n\nimport type { DeployAdapter } from './types.js'\n\nexport const theoCloudAdapter: DeployAdapter = {\n name: 'theo-cloud',\n\n build(_config: TheoConfig, cwd: string): Promise<void> {\n const manifest = readManifest(cwd)\n const artifacts = prepareTheoCloudArtifacts(manifest)\n const summary =\n artifacts.services.length === 0 ? 'TS-only app (no services)' : artifacts.services.join(', ')\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n `[theo-cloud] Wave 3 v2.0: manifest schemaVersion=${String(artifacts.manifestVersion)}, ` +\n `services=${summary}. Bundle ready for upload — TheoCloud emits K8s manifests internally upon deploy.`,\n )\n return Promise.resolve()\n },\n}\n"],"mappings":";;;;;;;;AAoBO,IAAM,mBAAkC;AAAA,EAC7C,MAAM;AAAA,EAEN,MAAM,SAAqB,KAA4B;AACrD,UAAM,WAAW,aAAa,GAAG;AACjC,UAAM,YAAY,0BAA0B,QAAQ;AACpD,UAAM,UACJ,UAAU,SAAS,WAAW,IAAI,8BAA8B,UAAU,SAAS,KAAK,IAAI;AAE9F,YAAQ;AAAA,MACN,oDAAoD,OAAO,UAAU,eAAe,CAAC,cACvE,OAAO;AAAA,IACvB;AACA,WAAO,QAAQ,QAAQ;AAAA,EACzB;AACF;","names":[]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "tsx/esm";
|
|
3
|
-
import "./chunk-KT3MWNZG.js";
|
|
4
3
|
|
|
5
4
|
// src/cli/commands/upgrade-readiness.ts
|
|
6
5
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
@@ -251,4 +250,4 @@ export {
|
|
|
251
250
|
scanUpgradeReadiness,
|
|
252
251
|
upgradeReadinessCommand
|
|
253
252
|
};
|
|
254
|
-
//# sourceMappingURL=upgrade-readiness-
|
|
253
|
+
//# sourceMappingURL=upgrade-readiness-ACGW44JC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/upgrade-readiness.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI lint scanner. Reads user source files under `projectRoot` (CLI\n * argument resolved to absolute). Read-only; never writes. No HTTP input.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { resolve, relative, sep } from 'node:path'\n\n/**\n * T2.3 — Upgrade-readiness scanner for the 0.2 → 0.3 cutover.\n *\n * LINT-only. Reads user source, reports anticipated 0.3.0 violations with\n * file:line + suggested fix. NEVER writes user files (ADR D1).\n *\n * Three rules:\n * 1. csrf-missing-header — raw fetch POST/PUT/PATCH/DELETE missing the\n * X-Theo-Action: '1' header → 403 under strict CSRF.\n * 2. inline-script — <script>...</script> in .html files without\n * `src=` attribute → blocked under enforce CSP without unsafe-inline.\n * 3. dangerously-set-inline-script — React `dangerouslySetInnerHTML`\n * payload that contains <script>.\n *\n * EC-7: occurrences inside line comments (// ...) or wrapped in quoted\n * strings are best-effort skipped. The scanner is not a parser — it\n * filters obvious false-positive lines before running the violation\n * regex.\n * EC-8: scanning a directory with no `app/` and no `server/` returns a\n * 'no-project-detected' report with exitCode 0 (no crash).\n */\n\nexport type ViolationRule =\n | 'csrf-missing-header'\n | 'inline-script'\n | 'dangerously-set-inline-script'\n | 'zero-config-tailwind-suggest'\n | 'handrolled-dotenv-suggest'\n\nexport interface Violation {\n file: string\n line: number\n rule: ViolationRule\n message: string\n fix: string\n}\n\nexport type ReadinessStatus =\n | 'ready'\n | 'has-violations'\n | 'no-project-detected'\n | 'not-a-theokit-project'\n\nexport interface UpgradeReadinessReport {\n status: ReadinessStatus\n exitCode: 0 | 1\n violations: Violation[]\n}\n\nexport interface ScanOptions {\n cwd: string\n /** When true, exitCode is forced to 0 even when violations exist. */\n allowWarnings?: boolean\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.theokit', '.cache'])\nconst SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])\nconst HTML_EXTS = new Set(['.html', '.htm'])\n\nfunction walkFiles(dir: string, root: string, out: string[]): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const name of entries) {\n if (SKIP_DIRS.has(name)) continue\n const full = resolve(dir, name)\n let s\n try {\n s = statSync(full)\n } catch {\n continue\n }\n if (s.isDirectory()) {\n walkFiles(full, root, out)\n } else if (s.isFile()) {\n const dot = name.lastIndexOf('.')\n if (dot < 0) continue\n const ext = name.slice(dot)\n if (SOURCE_EXTS.has(ext) || HTML_EXTS.has(ext)) out.push(full)\n }\n }\n}\n\nfunction isLikelyCommentOrString(rawLine: string, matchIndex: number): boolean {\n // Comment: line-comment marker (//) appears before the match position.\n const lineCommentIdx = rawLine.indexOf('//')\n if (lineCommentIdx !== -1 && lineCommentIdx < matchIndex) {\n // Make sure // is not inside a string (best-effort: count unescaped\n // quote chars before the //)\n const before = rawLine.slice(0, lineCommentIdx)\n const quoteCount = (before.match(/(?<!\\\\)[\"']/g) ?? []).length\n if (quoteCount % 2 === 0) return true\n }\n // String literal: scan quote characters around match. If the match is\n // surrounded by an odd number of unescaped quotes on either side, it\n // sits inside a string. Best-effort — does not parse template literals\n // or multiline strings.\n const before = rawLine.slice(0, matchIndex)\n const doubleQuotes = (before.match(/(?<!\\\\)\"/g) ?? []).length\n const singleQuotes = (before.match(/(?<!\\\\)'/g) ?? []).length\n return doubleQuotes % 2 === 1 || singleQuotes % 2 === 1\n}\n\n// `fetch(` followed (within ~200 chars, same call site) by a method:\n// 'POST' | 'PUT' | 'PATCH' | 'DELETE'. We scan per-line to keep regex\n// state simple; multi-line fetch calls match on the line that carries\n// the `method:` literal.\nconst METHOD_PATTERN = /method\\s*:\\s*['\"`](POST|PUT|PATCH|DELETE)['\"`]/gi\n\nfunction scanSourceFile(file: string, rel: string, content: string, out: Violation[]): void {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n METHOD_PATTERN.lastIndex = 0\n const match = METHOD_PATTERN.exec(line)\n if (!match) continue\n if (isLikelyCommentOrString(line, match.index)) continue\n\n // Look 8 lines back AND forward for the enclosing `fetch(` and for an\n // X-Theo-Action header in the same call-site block.\n const blockStart = Math.max(0, i - 8)\n const blockEnd = Math.min(lines.length - 1, i + 8)\n const block = lines.slice(blockStart, blockEnd + 1).join('\\n')\n if (!/\\bfetch\\s*\\(/.test(block)) continue\n if (/\\btheoFetch\\s*\\(/.test(block)) continue\n if (/['\"`]X-Theo-Action['\"`]\\s*:/i.test(block)) continue\n\n out.push({\n file: rel,\n line: i + 1,\n rule: 'csrf-missing-header',\n message:\n 'fetch(' +\n match[1] +\n ') without X-Theo-Action header — will return 403 under 0.3.0 strict CSRF',\n fix: \"Use theoFetch(path, { method, body }) OR add headers: { 'X-Theo-Action': '1' }\",\n })\n }\n\n // dangerouslySetInnerHTML with <script> in the payload.\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!/dangerouslySetInnerHTML/i.test(line)) continue\n // The script may live on the same line or in __html on a following\n // line; check a small window.\n const window = lines.slice(i, Math.min(lines.length, i + 6)).join('\\n')\n if (!/__html\\s*:\\s*['\"`][^'\"`]*<script\\b/i.test(window)) continue\n out.push({\n file: rel,\n line: i + 1,\n rule: 'dangerously-set-inline-script',\n message:\n 'dangerouslySetInnerHTML payload contains <script> — blocked under 0.3.0 enforce CSP',\n fix: 'Move the script into a real module loaded via <script src=\"...\">, or thread ctx.nonce',\n })\n }\n}\n\nconst INLINE_SCRIPT_PATTERN = /<script\\b(?![^>]*\\bsrc\\s*=)[^>]*>[\\s\\S]*?<\\/script>/gi\n\nfunction scanHtmlFile(file: string, rel: string, content: string, out: Violation[]): void {\n INLINE_SCRIPT_PATTERN.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = INLINE_SCRIPT_PATTERN.exec(content)) !== null) {\n // Compute 1-based line number from match index.\n const before = content.slice(0, match.index)\n const line = (before.match(/\\n/g) ?? []).length + 1\n out.push({\n file: rel,\n line,\n rule: 'inline-script',\n message:\n 'Inline <script> without src — blocked under 0.3.0 enforce CSP without unsafe-inline',\n fix: \"Move into a real script file (e.g. /main.js) and load with <script src='/main.js'>, or thread ctx.nonce\",\n })\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has @theokit/ui in deps AND a manual\n * tailwind.config.* that does NOT import the @theokit/ui preset. Suggest\n * extending via `import preset from '@theokit/ui/preset'`.\n */\nfunction scanZeroConfigTailwindHint(cwd: string, out: Violation[]): void {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) return\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n return\n }\n const hasUi =\n Boolean(pkg.dependencies?.['@theokit/ui']) || Boolean(pkg.devDependencies?.['@theokit/ui'])\n if (!hasUi) return\n\n for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {\n const cfgPath = resolve(cwd, `tailwind.config${ext}`)\n if (!existsSync(cfgPath)) continue\n let content: string\n try {\n content = readFileSync(cfgPath, 'utf8')\n } catch {\n continue\n }\n if (content.includes('@theokit/ui/preset')) return // already using preset\n out.push({\n file: `tailwind.config${ext}`,\n line: 1,\n rule: 'zero-config-tailwind-suggest',\n message:\n '@theokit/ui detected with a manual tailwind.config — extend with the UI preset to keep theme tokens in sync.',\n fix: \"import preset from '@theokit/ui/preset'; export default { presets: [preset], content: [...] }\",\n })\n return\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has hand-rolled dotenv loading in\n * server/ (import 'dotenv/config', dotenv.config(), readFile('.env'), etc.).\n * Suggest the framework's loadEnv (auto-invoked by CLI commands; importable\n * from theokit/server for standalone scripts).\n */\nfunction scanHandRolledDotenvHint(\n file: string,\n rel: string,\n content: string,\n out: Violation[],\n): void {\n // Only flag server/ files\n if (!rel.startsWith('server/')) return\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n // import 'dotenv/config' or import * as dotenv from 'dotenv'\n if (/from\\s+['\"]dotenv['\"]/.test(line) || /import\\s+['\"]dotenv\\/config['\"]/.test(line)) {\n out.push({\n file: rel,\n line: i + 1,\n rule: 'handrolled-dotenv-suggest',\n message:\n 'Manual dotenv import detected — TheoKit auto-loads .env in CLI commands. For standalone scripts, import { loadEnv } from \"theokit/server\".',\n fix: \"Remove this import. CLI commands (dev/build/start) auto-load .env. For standalone scripts: import { loadEnv } from 'theokit/server'; loadEnv()\",\n })\n return // one hint per file\n }\n }\n}\n\n/**\n * EC-3: a directory may have an `app/` directory (Next.js, Remix, etc.) but\n * not actually be a TheoKit project. Run our 3 rules against such a project\n * would generate many false positives. The strict guard checks for `theokit`\n * in dependencies OR devDependencies of the cwd's package.json. Returns\n * `null` when the project IS a TheoKit project, or a non-TheoKit report\n * otherwise.\n */\nfunction detectNonTheokitProject(cwd: string): UpgradeReadinessReport | null {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n // No package.json — let the existing app/server detection handle it.\n return null\n }\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n // Malformed package.json — treat as \"not a TheoKit project we can verify\".\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n const deps = pkg.dependencies ?? {}\n const devDeps = pkg.devDependencies ?? {}\n if (!Object.hasOwn(deps, 'theokit') && !Object.hasOwn(devDeps, 'theokit')) {\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n return null\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async surface for future fs.promises migration\nexport async function scanUpgradeReadiness(options: ScanOptions): Promise<UpgradeReadinessReport> {\n const cwd = options.cwd\n const allowWarnings = options.allowWarnings === true\n\n const appDir = resolve(cwd, 'app')\n const serverDir = resolve(cwd, 'server')\n const publicDir = resolve(cwd, 'public')\n\n const hasApp = existsSync(appDir)\n const hasServer = existsSync(serverDir)\n\n if (!hasApp && !hasServer) {\n return { status: 'no-project-detected', exitCode: 0, violations: [] }\n }\n\n // EC-3: refuse to scan non-TheoKit projects (avoid FP avalanche).\n const nonTheokit = detectNonTheokitProject(cwd)\n if (nonTheokit) return nonTheokit\n\n const files: string[] = []\n if (hasApp) walkFiles(appDir, cwd, files)\n if (hasServer) walkFiles(serverDir, cwd, files)\n if (existsSync(publicDir)) walkFiles(publicDir, cwd, files)\n\n const violations: Violation[] = []\n for (const file of files) {\n let content: string\n try {\n content = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const rel = relative(cwd, file).split(sep).join('/')\n const dot = file.lastIndexOf('.')\n const ext = dot >= 0 ? file.slice(dot) : ''\n if (HTML_EXTS.has(ext)) {\n scanHtmlFile(file, rel, content, violations)\n } else if (SOURCE_EXTS.has(ext)) {\n scanSourceFile(file, rel, content, violations)\n scanHandRolledDotenvHint(file, rel, content, violations)\n }\n }\n\n // T4.1 — Project-level hint (not per-file): does the consumer have a\n // manual tailwind.config without the UI preset?\n scanZeroConfigTailwindHint(cwd, violations)\n\n const hasHigh = violations.length > 0\n const status: ReadinessStatus = hasHigh ? 'has-violations' : 'ready'\n const exitCode: 0 | 1 = hasHigh && !allowWarnings ? 1 : 0\n return { status, exitCode, violations }\n}\n\n/**\n * CLI wrapper. Prints a human-readable or JSON report and exits with the\n * computed exit code. Imported lazily from `cli/index.ts`.\n */\nexport async function upgradeReadinessCommand(opts: {\n json?: boolean\n allowWarnings?: boolean\n}): Promise<void> {\n const report = await scanUpgradeReadiness({\n cwd: process.cwd(),\n allowWarnings: opts.allowWarnings,\n })\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2))\n process.exit(report.exitCode)\n }\n\n console.log('')\n if (report.status === 'no-project-detected') {\n console.log(\" · No theokit project detected here (no 'app/' or 'server/' dir).\")\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.status === 'not-a-theokit-project') {\n console.log(' ✗ Not a TheoKit project (theokit not in package.json).')\n console.log(' Run this command from a TheoKit project root.')\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.violations.length === 0) {\n console.log(' ✓ Upgrade-readiness 0.3: no violations detected.')\n console.log('')\n // T1.3 — friendly post-success URL (blueprint R4; Next.js next-codemod precedent).\n // Anchor canon: #rollback (existing in docs/migration/0.2-to-0.3.md per T1.1).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3')\n console.log(' Need to roll back temporarily? See #rollback')\n console.log('')\n process.exit(report.exitCode)\n }\n console.log(` ✗ Upgrade-readiness 0.3: ${report.violations.length} violation(s)`)\n console.log('')\n for (const v of report.violations) {\n console.log(` ${v.file}:${v.line} [${v.rule}]`)\n console.log(` ${v.message}`)\n console.log(` fix: ${v.fix}`)\n console.log('')\n }\n // T1.3 — print migration-guide URL on violations branch too (one canonical link\n // regardless of verdict, per blueprint Q5 Astro CHANGELOG URL pattern).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3#rollback')\n console.log('')\n process.exit(report.exitCode)\n}\n"],"mappings":"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","names":["before"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/upgrade-readiness.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI lint scanner. Reads user source files under `projectRoot` (CLI\n * argument resolved to absolute). Read-only; never writes. No HTTP input.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { resolve, relative, sep } from 'node:path'\n\n/**\n * T2.3 — Upgrade-readiness scanner for the 0.2 → 0.3 cutover.\n *\n * LINT-only. Reads user source, reports anticipated 0.3.0 violations with\n * file:line + suggested fix. NEVER writes user files (ADR D1).\n *\n * Three rules:\n * 1. csrf-missing-header — raw fetch POST/PUT/PATCH/DELETE missing the\n * X-Theo-Action: '1' header → 403 under strict CSRF.\n * 2. inline-script — <script>...</script> in .html files without\n * `src=` attribute → blocked under enforce CSP without unsafe-inline.\n * 3. dangerously-set-inline-script — React `dangerouslySetInnerHTML`\n * payload that contains <script>.\n *\n * EC-7: occurrences inside line comments (// ...) or wrapped in quoted\n * strings are best-effort skipped. The scanner is not a parser — it\n * filters obvious false-positive lines before running the violation\n * regex.\n * EC-8: scanning a directory with no `app/` and no `server/` returns a\n * 'no-project-detected' report with exitCode 0 (no crash).\n */\n\nexport type ViolationRule =\n | 'csrf-missing-header'\n | 'inline-script'\n | 'dangerously-set-inline-script'\n | 'zero-config-tailwind-suggest'\n | 'handrolled-dotenv-suggest'\n\nexport interface Violation {\n file: string\n line: number\n rule: ViolationRule\n message: string\n fix: string\n}\n\nexport type ReadinessStatus =\n | 'ready'\n | 'has-violations'\n | 'no-project-detected'\n | 'not-a-theokit-project'\n\nexport interface UpgradeReadinessReport {\n status: ReadinessStatus\n exitCode: 0 | 1\n violations: Violation[]\n}\n\nexport interface ScanOptions {\n cwd: string\n /** When true, exitCode is forced to 0 even when violations exist. */\n allowWarnings?: boolean\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.theokit', '.cache'])\nconst SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])\nconst HTML_EXTS = new Set(['.html', '.htm'])\n\nfunction walkFiles(dir: string, root: string, out: string[]): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const name of entries) {\n if (SKIP_DIRS.has(name)) continue\n const full = resolve(dir, name)\n let s\n try {\n s = statSync(full)\n } catch {\n continue\n }\n if (s.isDirectory()) {\n walkFiles(full, root, out)\n } else if (s.isFile()) {\n const dot = name.lastIndexOf('.')\n if (dot < 0) continue\n const ext = name.slice(dot)\n if (SOURCE_EXTS.has(ext) || HTML_EXTS.has(ext)) out.push(full)\n }\n }\n}\n\nfunction isLikelyCommentOrString(rawLine: string, matchIndex: number): boolean {\n // Comment: line-comment marker (//) appears before the match position.\n const lineCommentIdx = rawLine.indexOf('//')\n if (lineCommentIdx !== -1 && lineCommentIdx < matchIndex) {\n // Make sure // is not inside a string (best-effort: count unescaped\n // quote chars before the //)\n const before = rawLine.slice(0, lineCommentIdx)\n const quoteCount = (before.match(/(?<!\\\\)[\"']/g) ?? []).length\n if (quoteCount % 2 === 0) return true\n }\n // String literal: scan quote characters around match. If the match is\n // surrounded by an odd number of unescaped quotes on either side, it\n // sits inside a string. Best-effort — does not parse template literals\n // or multiline strings.\n const before = rawLine.slice(0, matchIndex)\n const doubleQuotes = (before.match(/(?<!\\\\)\"/g) ?? []).length\n const singleQuotes = (before.match(/(?<!\\\\)'/g) ?? []).length\n return doubleQuotes % 2 === 1 || singleQuotes % 2 === 1\n}\n\n// `fetch(` followed (within ~200 chars, same call site) by a method:\n// 'POST' | 'PUT' | 'PATCH' | 'DELETE'. We scan per-line to keep regex\n// state simple; multi-line fetch calls match on the line that carries\n// the `method:` literal.\nconst METHOD_PATTERN = /method\\s*:\\s*['\"`](POST|PUT|PATCH|DELETE)['\"`]/gi\n\nfunction scanSourceFile(file: string, rel: string, content: string, out: Violation[]): void {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n METHOD_PATTERN.lastIndex = 0\n const match = METHOD_PATTERN.exec(line)\n if (!match) continue\n if (isLikelyCommentOrString(line, match.index)) continue\n\n // Look 8 lines back AND forward for the enclosing `fetch(` and for an\n // X-Theo-Action header in the same call-site block.\n const blockStart = Math.max(0, i - 8)\n const blockEnd = Math.min(lines.length - 1, i + 8)\n const block = lines.slice(blockStart, blockEnd + 1).join('\\n')\n if (!/\\bfetch\\s*\\(/.test(block)) continue\n if (/\\btheoFetch\\s*\\(/.test(block)) continue\n if (/['\"`]X-Theo-Action['\"`]\\s*:/i.test(block)) continue\n\n out.push({\n file: rel,\n line: i + 1,\n rule: 'csrf-missing-header',\n message:\n 'fetch(' +\n match[1] +\n ') without X-Theo-Action header — will return 403 under 0.3.0 strict CSRF',\n fix: \"Use theoFetch(path, { method, body }) OR add headers: { 'X-Theo-Action': '1' }\",\n })\n }\n\n // dangerouslySetInnerHTML with <script> in the payload.\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!/dangerouslySetInnerHTML/i.test(line)) continue\n // The script may live on the same line or in __html on a following\n // line; check a small window.\n const window = lines.slice(i, Math.min(lines.length, i + 6)).join('\\n')\n if (!/__html\\s*:\\s*['\"`][^'\"`]*<script\\b/i.test(window)) continue\n out.push({\n file: rel,\n line: i + 1,\n rule: 'dangerously-set-inline-script',\n message:\n 'dangerouslySetInnerHTML payload contains <script> — blocked under 0.3.0 enforce CSP',\n fix: 'Move the script into a real module loaded via <script src=\"...\">, or thread ctx.nonce',\n })\n }\n}\n\nconst INLINE_SCRIPT_PATTERN = /<script\\b(?![^>]*\\bsrc\\s*=)[^>]*>[\\s\\S]*?<\\/script>/gi\n\nfunction scanHtmlFile(file: string, rel: string, content: string, out: Violation[]): void {\n INLINE_SCRIPT_PATTERN.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = INLINE_SCRIPT_PATTERN.exec(content)) !== null) {\n // Compute 1-based line number from match index.\n const before = content.slice(0, match.index)\n const line = (before.match(/\\n/g) ?? []).length + 1\n out.push({\n file: rel,\n line,\n rule: 'inline-script',\n message:\n 'Inline <script> without src — blocked under 0.3.0 enforce CSP without unsafe-inline',\n fix: \"Move into a real script file (e.g. /main.js) and load with <script src='/main.js'>, or thread ctx.nonce\",\n })\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has @theokit/ui in deps AND a manual\n * tailwind.config.* that does NOT import the @theokit/ui preset. Suggest\n * extending via `import preset from '@theokit/ui/preset'`.\n */\nfunction scanZeroConfigTailwindHint(cwd: string, out: Violation[]): void {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) return\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n return\n }\n const hasUi =\n Boolean(pkg.dependencies?.['@theokit/ui']) || Boolean(pkg.devDependencies?.['@theokit/ui'])\n if (!hasUi) return\n\n for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {\n const cfgPath = resolve(cwd, `tailwind.config${ext}`)\n if (!existsSync(cfgPath)) continue\n let content: string\n try {\n content = readFileSync(cfgPath, 'utf8')\n } catch {\n continue\n }\n if (content.includes('@theokit/ui/preset')) return // already using preset\n out.push({\n file: `tailwind.config${ext}`,\n line: 1,\n rule: 'zero-config-tailwind-suggest',\n message:\n '@theokit/ui detected with a manual tailwind.config — extend with the UI preset to keep theme tokens in sync.',\n fix: \"import preset from '@theokit/ui/preset'; export default { presets: [preset], content: [...] }\",\n })\n return\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has hand-rolled dotenv loading in\n * server/ (import 'dotenv/config', dotenv.config(), readFile('.env'), etc.).\n * Suggest the framework's loadEnv (auto-invoked by CLI commands; importable\n * from theokit/server for standalone scripts).\n */\nfunction scanHandRolledDotenvHint(\n file: string,\n rel: string,\n content: string,\n out: Violation[],\n): void {\n // Only flag server/ files\n if (!rel.startsWith('server/')) return\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n // import 'dotenv/config' or import * as dotenv from 'dotenv'\n if (/from\\s+['\"]dotenv['\"]/.test(line) || /import\\s+['\"]dotenv\\/config['\"]/.test(line)) {\n out.push({\n file: rel,\n line: i + 1,\n rule: 'handrolled-dotenv-suggest',\n message:\n 'Manual dotenv import detected — TheoKit auto-loads .env in CLI commands. For standalone scripts, import { loadEnv } from \"theokit/server\".',\n fix: \"Remove this import. CLI commands (dev/build/start) auto-load .env. For standalone scripts: import { loadEnv } from 'theokit/server'; loadEnv()\",\n })\n return // one hint per file\n }\n }\n}\n\n/**\n * EC-3: a directory may have an `app/` directory (Next.js, Remix, etc.) but\n * not actually be a TheoKit project. Run our 3 rules against such a project\n * would generate many false positives. The strict guard checks for `theokit`\n * in dependencies OR devDependencies of the cwd's package.json. Returns\n * `null` when the project IS a TheoKit project, or a non-TheoKit report\n * otherwise.\n */\nfunction detectNonTheokitProject(cwd: string): UpgradeReadinessReport | null {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n // No package.json — let the existing app/server detection handle it.\n return null\n }\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n // Malformed package.json — treat as \"not a TheoKit project we can verify\".\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n const deps = pkg.dependencies ?? {}\n const devDeps = pkg.devDependencies ?? {}\n if (!Object.hasOwn(deps, 'theokit') && !Object.hasOwn(devDeps, 'theokit')) {\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n return null\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async surface for future fs.promises migration\nexport async function scanUpgradeReadiness(options: ScanOptions): Promise<UpgradeReadinessReport> {\n const cwd = options.cwd\n const allowWarnings = options.allowWarnings === true\n\n const appDir = resolve(cwd, 'app')\n const serverDir = resolve(cwd, 'server')\n const publicDir = resolve(cwd, 'public')\n\n const hasApp = existsSync(appDir)\n const hasServer = existsSync(serverDir)\n\n if (!hasApp && !hasServer) {\n return { status: 'no-project-detected', exitCode: 0, violations: [] }\n }\n\n // EC-3: refuse to scan non-TheoKit projects (avoid FP avalanche).\n const nonTheokit = detectNonTheokitProject(cwd)\n if (nonTheokit) return nonTheokit\n\n const files: string[] = []\n if (hasApp) walkFiles(appDir, cwd, files)\n if (hasServer) walkFiles(serverDir, cwd, files)\n if (existsSync(publicDir)) walkFiles(publicDir, cwd, files)\n\n const violations: Violation[] = []\n for (const file of files) {\n let content: string\n try {\n content = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const rel = relative(cwd, file).split(sep).join('/')\n const dot = file.lastIndexOf('.')\n const ext = dot >= 0 ? file.slice(dot) : ''\n if (HTML_EXTS.has(ext)) {\n scanHtmlFile(file, rel, content, violations)\n } else if (SOURCE_EXTS.has(ext)) {\n scanSourceFile(file, rel, content, violations)\n scanHandRolledDotenvHint(file, rel, content, violations)\n }\n }\n\n // T4.1 — Project-level hint (not per-file): does the consumer have a\n // manual tailwind.config without the UI preset?\n scanZeroConfigTailwindHint(cwd, violations)\n\n const hasHigh = violations.length > 0\n const status: ReadinessStatus = hasHigh ? 'has-violations' : 'ready'\n const exitCode: 0 | 1 = hasHigh && !allowWarnings ? 1 : 0\n return { status, exitCode, violations }\n}\n\n/**\n * CLI wrapper. Prints a human-readable or JSON report and exits with the\n * computed exit code. Imported lazily from `cli/index.ts`.\n */\nexport async function upgradeReadinessCommand(opts: {\n json?: boolean\n allowWarnings?: boolean\n}): Promise<void> {\n const report = await scanUpgradeReadiness({\n cwd: process.cwd(),\n allowWarnings: opts.allowWarnings,\n })\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2))\n process.exit(report.exitCode)\n }\n\n console.log('')\n if (report.status === 'no-project-detected') {\n console.log(\" · No theokit project detected here (no 'app/' or 'server/' dir).\")\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.status === 'not-a-theokit-project') {\n console.log(' ✗ Not a TheoKit project (theokit not in package.json).')\n console.log(' Run this command from a TheoKit project root.')\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.violations.length === 0) {\n console.log(' ✓ Upgrade-readiness 0.3: no violations detected.')\n console.log('')\n // T1.3 — friendly post-success URL (blueprint R4; Next.js next-codemod precedent).\n // Anchor canon: #rollback (existing in docs/migration/0.2-to-0.3.md per T1.1).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3')\n console.log(' Need to roll back temporarily? See #rollback')\n console.log('')\n process.exit(report.exitCode)\n }\n console.log(` ✗ Upgrade-readiness 0.3: ${report.violations.length} violation(s)`)\n console.log('')\n for (const v of report.violations) {\n console.log(` ${v.file}:${v.line} [${v.rule}]`)\n console.log(` ${v.message}`)\n console.log(` fix: ${v.fix}`)\n console.log('')\n }\n // T1.3 — print migration-guide URL on violations branch too (one canonical link\n // regardless of verdict, per blueprint Q5 Astro CHANGELOG URL pattern).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3#rollback')\n console.log('')\n process.exit(report.exitCode)\n}\n"],"mappings":"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","names":["before"]}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
assertServicesUnsupported,
|
|
8
8
|
readManifest
|
|
9
9
|
} from "./chunk-CCVC6P6B.js";
|
|
10
|
-
import "./chunk-KT3MWNZG.js";
|
|
11
10
|
|
|
12
11
|
// src/adapters/vercel.ts
|
|
13
12
|
import { existsSync, mkdirSync, writeFileSync, cpSync } from "fs";
|
|
@@ -131,4 +130,4 @@ export {
|
|
|
131
130
|
renderVercelVcConfigJson,
|
|
132
131
|
vercelAdapter
|
|
133
132
|
};
|
|
134
|
-
//# sourceMappingURL=vercel-
|
|
133
|
+
//# sourceMappingURL=vercel-6VWQTIUL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/vercel.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Vercel deploy adapter. All paths derived from `cwd` and a fixed\n * `.theokit/vercel/` output layout. Build-time tool — no HTTP input.\n */\nimport { existsSync, mkdirSync, writeFileSync, cpSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport { assertServicesUnsupported, readManifest } from '../services/index.js'\n\nimport { nodeAdapter } from './node.js'\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\n/**\n * T2.2 — Vercel adapter rewritten to consume `theokit/adapters/web-shim`\n * instead of emitting an inline plain-object Request/Response shim and\n * lazy-importing internal `theo-server/` paths.\n */\n\nexport function renderVercelFunctionEntry(): string {\n return [\n `// Generated by Theo — Vercel Functions adapter`,\n `// Environment variables are resolved at RUNTIME, not build time.`,\n ``,\n `import { randomUUID } from 'node:crypto'`,\n `import { resolve } from 'node:path'`,\n `import { scanServerRoutes, matchRoute, executeRoute, createProductionLoader } from 'theokit/server'`,\n `import { createWebShim } from 'theokit/adapters/web-shim'`,\n ``,\n `// Cold-start cache`,\n `let routesCache = null`,\n `let loaderCache = null`,\n `const serverDir = resolve(process.cwd(), 'server')`,\n ``,\n `// Vercel Functions invoke this default export with a (req, res) pair`,\n `// (Node IncomingMessage-style). We convert it to a Web Request, run the`,\n `// pipeline through the shim, and pipe the Response back.`,\n `export default async function handler(nodeReq, nodeRes) {`,\n ` const url = new URL(nodeReq.url ?? '/', 'http://' + (nodeReq.headers?.host ?? 'localhost'))`,\n ``,\n ` if (!url.pathname.startsWith('/api/')) {`,\n ` nodeRes.statusCode = 404`,\n ` nodeRes.end('Not Found')`,\n ` return`,\n ` }`,\n ``,\n ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,\n ` if (!loaderCache) loaderCache = createProductionLoader()`,\n ``,\n ` const match = matchRoute(url.pathname, routesCache)`,\n ` if (!match) {`,\n ` nodeRes.statusCode = 404`,\n ` nodeRes.setHeader('Content-Type', 'application/json')`,\n ` nodeRes.end(JSON.stringify({ error: { code: 'NOT_FOUND' } }))`,\n ` return`,\n ` }`,\n ``,\n ` // Convert Node-style req to Web Request for the shim`,\n ` const headers = new Headers()`,\n ` for (const [k, v] of Object.entries(nodeReq.headers ?? {})) {`,\n ` if (typeof v === 'string') headers.set(k, v)`,\n ` }`,\n ` let body`,\n ` const method = (nodeReq.method ?? 'GET').toUpperCase()`,\n ` if (method !== 'GET' && method !== 'HEAD') {`,\n ` const chunks = []`,\n ` await new Promise((resolveProm, rejectProm) => {`,\n ` nodeReq.on('data', (c) => chunks.push(c))`,\n ` nodeReq.on('end', () => resolveProm())`,\n ` nodeReq.on('error', rejectProm)`,\n ` })`,\n ` body = Buffer.concat(chunks)`,\n ` }`,\n ` const webRequest = new Request(url.toString(), { method, headers, body })`,\n ``,\n ` const { req, res, toResponse } = createWebShim(webRequest)`,\n ` const requestId = randomUUID()`,\n ` await executeRoute({`,\n ` route: match.route, method, params: match.params,`,\n ` req, res, loadModule: loaderCache, serverDir, requestId,`,\n ` })`,\n ` const webResponse = await toResponse()`,\n ``,\n ` nodeRes.statusCode = webResponse.status`,\n ` webResponse.headers.forEach((v, k) => nodeRes.setHeader(k, v))`,\n ` const text = await webResponse.text()`,\n ` nodeRes.end(text)`,\n `}`,\n ].join('\\n')\n}\n\nexport interface VercelRoutingRule {\n src?: string\n dest?: string\n handle?: string\n}\n\nexport function renderVercelConfigJson(): {\n version: number\n routes: VercelRoutingRule[]\n} {\n return {\n version: 3,\n routes: [\n { src: '/api/(.*)', dest: '/api' },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index.html' },\n ],\n }\n}\n\nexport function renderVercelVcConfigJson(): {\n runtime: string\n handler: string\n launcherType: string\n shouldAddHelpers: boolean\n} {\n return {\n runtime: 'nodejs22.x',\n handler: 'index.mjs',\n launcherType: 'Nodejs',\n shouldAddHelpers: true,\n }\n}\n\nexport const vercelAdapter: DeployAdapter = {\n name: 'vercel',\n\n async build(config: TheoConfig, cwd: string, ctx?: AdapterBuildContext): Promise<void> {\n // Wave 2 (T2.2) — reject polyglot services on this adapter.\n // Per 2026-05-27 owner decision, polyglot is wired via `node` (local\n // docker-compose harness) + `theo-cloud` (Wave 3). Vercel adapter\n // wire-up is deferred to a fresh ADR with demand evidence.\n assertServicesUnsupported('vercel', readManifest(cwd))\n\n // 1. Run the standard Node build first (ctx forwarded so nodeAdapter has makeVitePlugins)\n await nodeAdapter.build(config, cwd, ctx)\n\n const clientDir = resolve(cwd, '.theokit/client')\n const outputDir = resolve(cwd, '.vercel/output')\n\n // 2. Create .vercel/output structure\n mkdirSync(resolve(outputDir, 'static'), { recursive: true })\n mkdirSync(resolve(outputDir, 'functions/api.func'), { recursive: true })\n\n // 3. Copy static assets\n if (existsSync(clientDir)) {\n cpSync(clientDir, resolve(outputDir, 'static'), { recursive: true })\n }\n\n // 4. Emit serverless function entry (now uses shared web-shim)\n writeFileSync(resolve(outputDir, 'functions/api.func/index.mjs'), renderVercelFunctionEntry())\n\n // 5. Emit .vc-config.json\n writeFileSync(\n resolve(outputDir, 'functions/api.func/.vc-config.json'),\n JSON.stringify(renderVercelVcConfigJson(), null, 2),\n )\n\n // 6. Emit config.json (routing)\n writeFileSync(\n resolve(outputDir, 'config.json'),\n JSON.stringify(renderVercelConfigJson(), null, 2),\n )\n\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n ✓ Vercel output → .vercel/output/\\n')\n },\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/adapters/vercel.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Vercel deploy adapter. All paths derived from `cwd` and a fixed\n * `.theokit/vercel/` output layout. Build-time tool — no HTTP input.\n */\nimport { existsSync, mkdirSync, writeFileSync, cpSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport { assertServicesUnsupported, readManifest } from '../services/index.js'\n\nimport { nodeAdapter } from './node.js'\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\n/**\n * T2.2 — Vercel adapter rewritten to consume `theokit/adapters/web-shim`\n * instead of emitting an inline plain-object Request/Response shim and\n * lazy-importing internal `theo-server/` paths.\n */\n\nexport function renderVercelFunctionEntry(): string {\n return [\n `// Generated by Theo — Vercel Functions adapter`,\n `// Environment variables are resolved at RUNTIME, not build time.`,\n ``,\n `import { randomUUID } from 'node:crypto'`,\n `import { resolve } from 'node:path'`,\n `import { scanServerRoutes, matchRoute, executeRoute, createProductionLoader } from 'theokit/server'`,\n `import { createWebShim } from 'theokit/adapters/web-shim'`,\n ``,\n `// Cold-start cache`,\n `let routesCache = null`,\n `let loaderCache = null`,\n `const serverDir = resolve(process.cwd(), 'server')`,\n ``,\n `// Vercel Functions invoke this default export with a (req, res) pair`,\n `// (Node IncomingMessage-style). We convert it to a Web Request, run the`,\n `// pipeline through the shim, and pipe the Response back.`,\n `export default async function handler(nodeReq, nodeRes) {`,\n ` const url = new URL(nodeReq.url ?? '/', 'http://' + (nodeReq.headers?.host ?? 'localhost'))`,\n ``,\n ` if (!url.pathname.startsWith('/api/')) {`,\n ` nodeRes.statusCode = 404`,\n ` nodeRes.end('Not Found')`,\n ` return`,\n ` }`,\n ``,\n ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,\n ` if (!loaderCache) loaderCache = createProductionLoader()`,\n ``,\n ` const match = matchRoute(url.pathname, routesCache)`,\n ` if (!match) {`,\n ` nodeRes.statusCode = 404`,\n ` nodeRes.setHeader('Content-Type', 'application/json')`,\n ` nodeRes.end(JSON.stringify({ error: { code: 'NOT_FOUND' } }))`,\n ` return`,\n ` }`,\n ``,\n ` // Convert Node-style req to Web Request for the shim`,\n ` const headers = new Headers()`,\n ` for (const [k, v] of Object.entries(nodeReq.headers ?? {})) {`,\n ` if (typeof v === 'string') headers.set(k, v)`,\n ` }`,\n ` let body`,\n ` const method = (nodeReq.method ?? 'GET').toUpperCase()`,\n ` if (method !== 'GET' && method !== 'HEAD') {`,\n ` const chunks = []`,\n ` await new Promise((resolveProm, rejectProm) => {`,\n ` nodeReq.on('data', (c) => chunks.push(c))`,\n ` nodeReq.on('end', () => resolveProm())`,\n ` nodeReq.on('error', rejectProm)`,\n ` })`,\n ` body = Buffer.concat(chunks)`,\n ` }`,\n ` const webRequest = new Request(url.toString(), { method, headers, body })`,\n ``,\n ` const { req, res, toResponse } = createWebShim(webRequest)`,\n ` const requestId = randomUUID()`,\n ` await executeRoute({`,\n ` route: match.route, method, params: match.params,`,\n ` req, res, loadModule: loaderCache, serverDir, requestId,`,\n ` })`,\n ` const webResponse = await toResponse()`,\n ``,\n ` nodeRes.statusCode = webResponse.status`,\n ` webResponse.headers.forEach((v, k) => nodeRes.setHeader(k, v))`,\n ` const text = await webResponse.text()`,\n ` nodeRes.end(text)`,\n `}`,\n ].join('\\n')\n}\n\nexport interface VercelRoutingRule {\n src?: string\n dest?: string\n handle?: string\n}\n\nexport function renderVercelConfigJson(): {\n version: number\n routes: VercelRoutingRule[]\n} {\n return {\n version: 3,\n routes: [\n { src: '/api/(.*)', dest: '/api' },\n { handle: 'filesystem' },\n { src: '/(.*)', dest: '/index.html' },\n ],\n }\n}\n\nexport function renderVercelVcConfigJson(): {\n runtime: string\n handler: string\n launcherType: string\n shouldAddHelpers: boolean\n} {\n return {\n runtime: 'nodejs22.x',\n handler: 'index.mjs',\n launcherType: 'Nodejs',\n shouldAddHelpers: true,\n }\n}\n\nexport const vercelAdapter: DeployAdapter = {\n name: 'vercel',\n\n async build(config: TheoConfig, cwd: string, ctx?: AdapterBuildContext): Promise<void> {\n // Wave 2 (T2.2) — reject polyglot services on this adapter.\n // Per 2026-05-27 owner decision, polyglot is wired via `node` (local\n // docker-compose harness) + `theo-cloud` (Wave 3). Vercel adapter\n // wire-up is deferred to a fresh ADR with demand evidence.\n assertServicesUnsupported('vercel', readManifest(cwd))\n\n // 1. Run the standard Node build first (ctx forwarded so nodeAdapter has makeVitePlugins)\n await nodeAdapter.build(config, cwd, ctx)\n\n const clientDir = resolve(cwd, '.theokit/client')\n const outputDir = resolve(cwd, '.vercel/output')\n\n // 2. Create .vercel/output structure\n mkdirSync(resolve(outputDir, 'static'), { recursive: true })\n mkdirSync(resolve(outputDir, 'functions/api.func'), { recursive: true })\n\n // 3. Copy static assets\n if (existsSync(clientDir)) {\n cpSync(clientDir, resolve(outputDir, 'static'), { recursive: true })\n }\n\n // 4. Emit serverless function entry (now uses shared web-shim)\n writeFileSync(resolve(outputDir, 'functions/api.func/index.mjs'), renderVercelFunctionEntry())\n\n // 5. Emit .vc-config.json\n writeFileSync(\n resolve(outputDir, 'functions/api.func/.vc-config.json'),\n JSON.stringify(renderVercelVcConfigJson(), null, 2),\n )\n\n // 6. Emit config.json (routing)\n writeFileSync(\n resolve(outputDir, 'config.json'),\n JSON.stringify(renderVercelConfigJson(), null, 2),\n )\n\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n ✓ Vercel output → .vercel/output/\\n')\n },\n}\n"],"mappings":";;;;;;;;;;;AAIA,SAAS,YAAY,WAAW,eAAe,cAAc;AAC7D,SAAS,eAAe;AAcjB,SAAS,4BAAoC;AAClD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAQO,SAAS,yBAGd;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,EAAE,KAAK,aAAa,MAAM,OAAO;AAAA,MACjC,EAAE,QAAQ,aAAa;AAAA,MACvB,EAAE,KAAK,SAAS,MAAM,cAAc;AAAA,IACtC;AAAA,EACF;AACF;AAEO,SAAS,2BAKd;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,cAAc;AAAA,IACd,kBAAkB;AAAA,EACpB;AACF;AAEO,IAAM,gBAA+B;AAAA,EAC1C,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAAa,KAA0C;AAKrF,8BAA0B,UAAU,aAAa,GAAG,CAAC;AAGrD,UAAM,YAAY,MAAM,QAAQ,KAAK,GAAG;AAExC,UAAM,YAAY,QAAQ,KAAK,iBAAiB;AAChD,UAAM,YAAY,QAAQ,KAAK,gBAAgB;AAG/C,cAAU,QAAQ,WAAW,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,cAAU,QAAQ,WAAW,oBAAoB,GAAG,EAAE,WAAW,KAAK,CAAC;AAGvE,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,WAAW,QAAQ,WAAW,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IACrE;AAGA,kBAAc,QAAQ,WAAW,8BAA8B,GAAG,0BAA0B,CAAC;AAG7F;AAAA,MACE,QAAQ,WAAW,oCAAoC;AAAA,MACvD,KAAK,UAAU,yBAAyB,GAAG,MAAM,CAAC;AAAA,IACpD;AAGA;AAAA,MACE,QAAQ,WAAW,aAAa;AAAA,MAChC,KAAK,UAAU,uBAAuB,GAAG,MAAM,CAAC;AAAA,IAClD;AAGA,YAAQ,IAAI,mDAAyC;AAAA,EACvD;AACF;","names":[]}
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
defineTheoIntegration,
|
|
8
8
|
theoPlugin,
|
|
9
9
|
theoPluginAsync
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AUNPGO2V.js";
|
|
11
11
|
import "./chunk-PBEH6NXR.js";
|
|
12
12
|
import "./chunk-IHMJV2OK.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-HTNVOG34.js";
|
|
14
|
+
import "./chunk-BLT7PPII.js";
|
|
15
15
|
import "./chunk-BNGBG2SQ.js";
|
|
16
16
|
import "./chunk-CCVC6P6B.js";
|
|
17
17
|
import "./chunk-5ODOE6EF.js";
|
|
@@ -19,7 +19,6 @@ import "./chunk-WS67ONGI.js";
|
|
|
19
19
|
import "./chunk-IEES3CHD.js";
|
|
20
20
|
import "./chunk-3LVRAGAZ.js";
|
|
21
21
|
import "./chunk-6FYD34NX.js";
|
|
22
|
-
import "./chunk-KT3MWNZG.js";
|
|
23
22
|
export {
|
|
24
23
|
IntegrationRouteCollisionError,
|
|
25
24
|
IntegrationVirtualModulePrefixError,
|
|
@@ -28,4 +27,4 @@ export {
|
|
|
28
27
|
theoPlugin,
|
|
29
28
|
theoPluginAsync
|
|
30
29
|
};
|
|
31
|
-
//# sourceMappingURL=vite-plugin-
|
|
30
|
+
//# sourceMappingURL=vite-plugin-AYWW2YTE.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theokit",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"dist"
|
|
110
110
|
],
|
|
111
111
|
"dependencies": {
|
|
112
|
-
"@theokit/agents": "^0.
|
|
112
|
+
"@theokit/agents": "^0.9.0",
|
|
113
113
|
"@theokit/http": "^0.5.4",
|
|
114
114
|
"@vitejs/plugin-react": "^4.7.0",
|
|
115
115
|
"busboy": "^1.6.0",
|
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
"vite": "^6.4.3"
|
|
128
128
|
},
|
|
129
129
|
"peerDependencies": {
|
|
130
|
+
"@theokit/sdk": ">=2.9.0",
|
|
130
131
|
"@theokit/ui": "^0.14.0 || ^0.18.0 || ^0.19.0",
|
|
131
132
|
"db0": "^0.3.0",
|
|
132
133
|
"react": "^19.0.0",
|
|
@@ -137,6 +138,9 @@
|
|
|
137
138
|
"zod": "^4.0.0"
|
|
138
139
|
},
|
|
139
140
|
"peerDependenciesMeta": {
|
|
141
|
+
"@theokit/sdk": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
140
144
|
"@theokit/ui": {
|
|
141
145
|
"optional": true
|
|
142
146
|
},
|
|
@@ -151,7 +155,7 @@
|
|
|
151
155
|
}
|
|
152
156
|
},
|
|
153
157
|
"devDependencies": {
|
|
154
|
-
"@theokit/sdk": "^2.
|
|
158
|
+
"@theokit/sdk": "^2.9.0",
|
|
155
159
|
"@types/busboy": "^1.5.4",
|
|
156
160
|
"@types/pg": "^8.20.0",
|
|
157
161
|
"@types/picomatch": "^4.0.3",
|
package/dist/chunk-KT3MWNZG.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import "tsx/esm";
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
11
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
__require,
|
|
16
|
-
__commonJS
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=chunk-KT3MWNZG.js.map
|