ignotum 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/cli/bin.mjs +6111 -4448
- package/dist/cli/bin.mjs.map +1 -1
- package/dist/cli/emscripten-module-D5GzfUNv.mjs +533 -0
- package/dist/cli/emscripten-module-D5GzfUNv.mjs.map +1 -0
- package/dist/cli/ffi-7IElU4Co.mjs +148 -0
- package/dist/cli/ffi-7IElU4Co.mjs.map +1 -0
- package/dist/cli/managed-release-WHYLZwaS.mjs +10902 -0
- package/dist/cli/managed-release-WHYLZwaS.mjs.map +1 -0
- package/dist/cli/managed-release.d.mts +1 -0
- package/dist/cli/managed-release.mjs +14 -0
- package/dist/cli/managed-release.mjs.map +1 -0
- package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs +1289 -0
- package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs.map +1 -0
- package/dist/runtime/{api-Cv3hMbzo.d.ts → api-B4fwp5a2.d.ts} +4 -4
- package/dist/runtime/bootstrap-BISQSn_O.js +916 -0
- package/dist/runtime/bootstrap-BISQSn_O.js.map +1 -0
- package/dist/runtime/bootstrap-BTFScKAc.d.ts +545 -0
- package/dist/runtime/client.d.ts +50 -15
- package/dist/runtime/client.js +7 -1138
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js +269 -0
- package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js.map +1 -0
- package/dist/runtime/{id-Bt9XWRGL.js → id--1m0NuUL.js} +4 -4
- package/dist/runtime/id--1m0NuUL.js.map +1 -0
- package/dist/runtime/{id-BzFHf3Wo-DGPCjgrf.d.ts → id-7an3nxTu-DZJSM5xh.d.ts} +2 -2
- package/dist/runtime/id-CBOt2kDo.d.ts +1 -0
- package/dist/runtime/identity-QjtJRxBD.d.ts +2 -0
- package/dist/runtime/image-Djtjy4Z8.js +19 -0
- package/dist/runtime/image-Djtjy4Z8.js.map +1 -0
- package/dist/runtime/index-D4seNHlX.d.ts +184 -0
- package/dist/runtime/internal/api.d.ts +1 -1
- package/dist/runtime/internal/api.js +30 -1
- package/dist/runtime/internal/api.js.map +1 -0
- package/dist/runtime/internal/bootstrap.d.ts +2 -0
- package/dist/runtime/internal/bootstrap.js +2 -0
- package/dist/runtime/internal/client.d.ts +2 -0
- package/dist/runtime/internal/client.js +75 -0
- package/dist/runtime/internal/client.js.map +1 -0
- package/dist/runtime/internal/host.d.ts +25 -8
- package/dist/runtime/internal/host.js +27 -6
- package/dist/runtime/internal/host.js.map +1 -1
- package/dist/runtime/internal/routes.d.ts +2 -0
- package/dist/runtime/internal/routes.js +2 -0
- package/dist/runtime/internal/server.d.ts +1 -1
- package/dist/runtime/internal/server.js +1 -1
- package/dist/runtime/internal/types.d.ts +2 -1
- package/dist/runtime/internal/types.js +1 -1
- package/dist/runtime/pagination-CBOt2kDo.d.ts +1 -0
- package/dist/runtime/pagination-D3qd6s9N.js +33 -0
- package/dist/runtime/pagination-D3qd6s9N.js.map +1 -0
- package/dist/runtime/result-BkziOG1L.d.ts +1 -0
- package/dist/runtime/router-BSBI1oN1.js +2377 -0
- package/dist/runtime/router-BSBI1oN1.js.map +1 -0
- package/dist/runtime/routes-Ce8TVP-T.js +690 -0
- package/dist/runtime/routes-Ce8TVP-T.js.map +1 -0
- package/dist/runtime/{schema-1Zs03-iS.js → schema-D1wOqiNH.js} +40 -12
- package/dist/runtime/schema-D1wOqiNH.js.map +1 -0
- package/dist/runtime/server.d.ts +5 -5
- package/dist/runtime/server.js +2 -2
- package/dist/runtime/{sync-Bs8J3fIr.d.ts → sync-D-GK2sv9.d.ts} +3 -2
- package/dist/runtime/{api-CAgKDij7.js → sync-DzUwA8W9.js} +22 -41
- package/dist/runtime/sync-DzUwA8W9.js.map +1 -0
- package/dist/runtime/types-DeCCyqLU-BIm7rU-0.d.ts +341 -0
- package/package.json +29 -4
- package/src/cli/agent-files.ts +14 -8
- package/src/cli/build/managed-client.ts +411 -0
- package/src/cli/build/managed-server.ts +294 -0
- package/src/cli/build/managed.ts +67 -0
- package/src/cli/build/output.ts +47 -0
- package/src/cli/build/server.ts +51 -220
- package/src/cli/client-config.ts +1 -140
- package/src/cli/client-styles.ts +1 -0
- package/src/cli/codegen.ts +9 -4
- package/src/cli/command.ts +28 -5
- package/src/cli/control-client.ts +84 -101
- package/src/cli/deploy.ts +133 -177
- package/src/cli/dev.ts +506 -107
- package/src/cli/image-assets.ts +499 -0
- package/src/cli/managed-client-boundaries.ts +103 -0
- package/src/cli/managed-dev-platform.ts +118 -0
- package/src/cli/managed-exports.ts +219 -0
- package/src/cli/managed-release-bin.ts +13 -0
- package/src/cli/managed-release.ts +260 -0
- package/src/cli/module-boundaries.ts +11 -1
- package/src/cli/new-app.ts +111 -87
- package/src/cli/route-codegen.ts +311 -0
- package/src/cli/route-static.ts +710 -0
- package/src/client/bootstrap.ts +110 -0
- package/src/client/errors.ts +13 -7
- package/src/client/files.ts +64 -0
- package/src/client/id.ts +9 -5
- package/src/client/image.ts +28 -0
- package/src/client/index.ts +25 -2
- package/src/client/managed-client.ts +729 -0
- package/src/client/managed-upgrade.ts +283 -0
- package/src/client/recovery-journal.ts +195 -0
- package/src/client/route-boundaries.ts +184 -0
- package/src/client/router-history.ts +152 -0
- package/src/client/router-store.ts +644 -0
- package/src/client/router.ts +324 -0
- package/src/client/routes.ts +28 -0
- package/src/client/sync.ts +66 -592
- package/src/dev-runtime/database.ts +77 -2
- package/src/dev-runtime/functions.ts +3 -3
- package/src/dev-runtime/id.ts +19 -4
- package/src/dev-runtime/managed-functions.ts +255 -0
- package/src/dev-runtime/managed-sync.ts +263 -0
- package/src/dev-runtime/managed-websocket.ts +71 -0
- package/src/dev-runtime/migrations.ts +20 -0
- package/src/dev-runtime/sync.ts +236 -236
- package/src/image-assets.d.ts +36 -0
- package/src/internal/client.ts +186 -0
- package/src/internal/host.ts +1 -1
- package/src/internal/http-paths.ts +0 -3
- package/src/internal/routes.ts +45 -0
- package/dist/runtime/api-CAgKDij7.js.map +0 -1
- package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js +0 -154
- package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js.map +0 -1
- package/dist/runtime/id-Bt9XWRGL.js.map +0 -1
- package/dist/runtime/id-Dz0apuB3.d.ts +0 -1
- package/dist/runtime/index-D54flWtH.d.ts +0 -402
- package/dist/runtime/pagination-DnKg3dkI-r5ZUxBBx.d.ts +0 -112
- package/dist/runtime/pagination-Dz0apuB3.d.ts +0 -1
- package/dist/runtime/result-DKAA4gpS.d.ts +0 -1
- package/dist/runtime/schema-1Zs03-iS.js.map +0 -1
- package/src/cli/build/client.ts +0 -119
- package/src/cli/build/public.ts +0 -186
- package/src/cli/client-entry.ts +0 -152
- package/src/cli/client-plugin.ts +0 -136
- package/src/client/app.ts +0 -15
|
@@ -43,10 +43,15 @@ import type {
|
|
|
43
43
|
RuntimeTableDefinition,
|
|
44
44
|
} from "@ignotum/contracts/runtime/schema";
|
|
45
45
|
import {
|
|
46
|
+
applyValueDefaults,
|
|
47
|
+
descriptorFields,
|
|
48
|
+
evolveStoredFields,
|
|
46
49
|
getValueDescriptor,
|
|
47
50
|
SchemaDefinitionTypeId,
|
|
48
51
|
type ValueDescriptor,
|
|
49
52
|
} from "@ignotum/contracts/schema";
|
|
53
|
+
import { SchemaSnapshot } from "@ignotum/contracts/version";
|
|
54
|
+
import { encodeCanonical } from "@ignotum/versions";
|
|
50
55
|
import type { FileValue } from "@ignotum/contracts/schema/file";
|
|
51
56
|
|
|
52
57
|
import { DevelopmentDatabase } from "./migrations.js";
|
|
@@ -272,9 +277,13 @@ const encodeFields = Effect.fn("LocalDatabase.encodeFields")(function* (
|
|
|
272
277
|
operation: Extract<DatabaseOperation, "insert" | "patch" | "replace">,
|
|
273
278
|
id: string | null,
|
|
274
279
|
) {
|
|
275
|
-
|
|
280
|
+
const normalized = yield* Effect.try({
|
|
281
|
+
try: () => (operation === "patch" ? value : applyValueDefaults(table.schema, value)),
|
|
282
|
+
catch: (cause) => schemaMismatch(tableName, id, operation, cause),
|
|
283
|
+
});
|
|
284
|
+
return yield* Schema.encodeUnknownEffect(fieldsCodec(table), {
|
|
276
285
|
onExcessProperty: "error",
|
|
277
|
-
})(
|
|
286
|
+
})(normalized).pipe(Effect.mapError((cause) => schemaMismatch(tableName, id, operation, cause)));
|
|
278
287
|
});
|
|
279
288
|
|
|
280
289
|
const decodeFields = Effect.fn("LocalDatabase.decodeFields")(function* (
|
|
@@ -492,6 +501,70 @@ export class LocalDatabase extends Context.Service<LocalDatabase, LocalDatabaseS
|
|
|
492
501
|
}
|
|
493
502
|
});
|
|
494
503
|
|
|
504
|
+
const prepareSchema = Effect.fn("LocalDatabase.prepareSchema")(function* (
|
|
505
|
+
schema: RuntimeSchemaDefinition,
|
|
506
|
+
) {
|
|
507
|
+
const definitions = schema[SchemaDefinitionTypeId];
|
|
508
|
+
const snapshot = encodeCanonical(SchemaSnapshot, {
|
|
509
|
+
formatVersion: 2,
|
|
510
|
+
tables: Object.entries(definitions)
|
|
511
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
512
|
+
.map(([name, definition]) => ({
|
|
513
|
+
name,
|
|
514
|
+
fields: descriptorFields(definition.fields),
|
|
515
|
+
indexes: Object.values(definition.indexes).sort((left, right) =>
|
|
516
|
+
left.name.localeCompare(right.name),
|
|
517
|
+
),
|
|
518
|
+
})),
|
|
519
|
+
});
|
|
520
|
+
const current = yield* sql<{
|
|
521
|
+
snapshot: string;
|
|
522
|
+
}>`SELECT snapshot FROM schema_state WHERE singleton = 1`;
|
|
523
|
+
if (current[0]?.snapshot === snapshot) return;
|
|
524
|
+
let changedData = false;
|
|
525
|
+
const storedTables = yield* sql<{ id: string; name: string }>`SELECT id, name FROM tables`;
|
|
526
|
+
for (const stored of storedTables) {
|
|
527
|
+
if (Object.hasOwn(definitions, stored.name)) continue;
|
|
528
|
+
const documents =
|
|
529
|
+
yield* sql`SELECT 1 FROM documents WHERE tableId = ${stored.id} LIMIT 1`;
|
|
530
|
+
if (documents.length > 0)
|
|
531
|
+
return yield* Effect.die(`Cannot remove non-empty table '${stored.name}'.`);
|
|
532
|
+
yield* sql`DELETE FROM tables WHERE id = ${stored.id}`;
|
|
533
|
+
}
|
|
534
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
535
|
+
const table = yield* resolveTable(schema, name);
|
|
536
|
+
let cursor = "";
|
|
537
|
+
let changed = false;
|
|
538
|
+
while (true) {
|
|
539
|
+
const rows = yield* sql<{ id: string; fields: string }>`
|
|
540
|
+
SELECT id, fields FROM documents WHERE tableId = ${table.id} AND id > ${cursor}
|
|
541
|
+
ORDER BY id LIMIT 100
|
|
542
|
+
`;
|
|
543
|
+
if (rows.length === 0) break;
|
|
544
|
+
for (const row of rows) {
|
|
545
|
+
const fields = yield* Effect.try({
|
|
546
|
+
try: () =>
|
|
547
|
+
JSON.stringify(evolveStoredFields(definition.schema, JSON.parse(row.fields))),
|
|
548
|
+
catch: () =>
|
|
549
|
+
new Error(
|
|
550
|
+
`Document '${row.id}' in '${name}' does not match the new schema or its conversion rules.`,
|
|
551
|
+
),
|
|
552
|
+
}).pipe(Effect.orDie);
|
|
553
|
+
if (fields !== row.fields) {
|
|
554
|
+
yield* sql`UPDATE documents SET fields = ${fields} WHERE tableId = ${table.id} AND id = ${row.id}`;
|
|
555
|
+
changed = true;
|
|
556
|
+
changedData = true;
|
|
557
|
+
}
|
|
558
|
+
cursor = row.id;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (changed) yield* sql`DELETE FROM indexes WHERE tableId = ${table.id}`;
|
|
562
|
+
}
|
|
563
|
+
yield* sql`INSERT INTO schema_state (singleton, snapshot) VALUES (1, ${snapshot})
|
|
564
|
+
ON CONFLICT (singleton) DO UPDATE SET snapshot = excluded.snapshot`;
|
|
565
|
+
if (current.length > 0 || changedData) yield* advanceRevision(undefined);
|
|
566
|
+
});
|
|
567
|
+
|
|
495
568
|
const indexKeys = Effect.fn("LocalDatabase.indexKeys")(function* (
|
|
496
569
|
table: ResolvedTable,
|
|
497
570
|
row: StoredDocument,
|
|
@@ -1026,6 +1099,7 @@ export class LocalDatabase extends Context.Service<LocalDatabase, LocalDatabaseS
|
|
|
1026
1099
|
) =>
|
|
1027
1100
|
sql.withTransaction(
|
|
1028
1101
|
Effect.gen(function* () {
|
|
1102
|
+
yield* prepareSchema(schema).pipe(Effect.orDie);
|
|
1029
1103
|
yield* prepareIndexes(schema).pipe(Effect.orDie);
|
|
1030
1104
|
const dependencies = makeDependencyRecorder<ReadDependency>();
|
|
1031
1105
|
const context = Object.freeze({
|
|
@@ -1044,6 +1118,7 @@ export class LocalDatabase extends Context.Service<LocalDatabase, LocalDatabaseS
|
|
|
1044
1118
|
) =>
|
|
1045
1119
|
sql.withTransaction(
|
|
1046
1120
|
Effect.gen(function* () {
|
|
1121
|
+
yield* prepareSchema(schema).pipe(Effect.orDie);
|
|
1047
1122
|
yield* prepareIndexes(schema).pipe(Effect.orDie);
|
|
1048
1123
|
const invalidations = makeDependencyRecorder<WriteInvalidation>();
|
|
1049
1124
|
const context = Object.freeze({
|
|
@@ -68,7 +68,7 @@ export type RuntimeHandlerContext = (LocalMutationContext | LocalQueryContext) &
|
|
|
68
68
|
export type RuntimeFunctionDefinition = InternalRuntimeFunctionDefinition<RuntimeHandlerContext>;
|
|
69
69
|
|
|
70
70
|
const loadDevelopmentEnvironment = Effect.fn("DevelopmentEnvironment.load")(function* (
|
|
71
|
-
server: ViteDevServer,
|
|
71
|
+
server: Pick<ViteDevServer, "ssrLoadModule">,
|
|
72
72
|
appDirectory: string,
|
|
73
73
|
) {
|
|
74
74
|
const fileSystem = yield* FileSystem.FileSystem;
|
|
@@ -100,7 +100,7 @@ export class DevelopmentEnvironment extends Context.Service<
|
|
|
100
100
|
readonly snapshot: Effect.Effect<LoadedEnvironment, unknown>;
|
|
101
101
|
}
|
|
102
102
|
>()("ignotum/dev-runtime/functions/DevelopmentEnvironment") {
|
|
103
|
-
static layer(server: ViteDevServer, appDirectory: string) {
|
|
103
|
+
static layer(server: Pick<ViteDevServer, "ssrLoadModule">, appDirectory: string) {
|
|
104
104
|
return Layer.effect(
|
|
105
105
|
DevelopmentEnvironment,
|
|
106
106
|
Effect.gen(function* () {
|
|
@@ -151,7 +151,7 @@ export class FunctionRegistry extends Context.Service<
|
|
|
151
151
|
) => Effect.Effect<ResolvedFunction, FunctionResolutionError>;
|
|
152
152
|
}
|
|
153
153
|
>()("ignotum/dev-runtime/functions/FunctionRegistry") {
|
|
154
|
-
static layer(server: ViteDevServer, appDirectory: string) {
|
|
154
|
+
static layer(server: Pick<ViteDevServer, "ssrLoadModule">, appDirectory: string) {
|
|
155
155
|
return Layer.effect(
|
|
156
156
|
FunctionRegistry,
|
|
157
157
|
Effect.gen(function* () {
|
package/src/dev-runtime/id.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Context,
|
|
3
|
+
Effect,
|
|
4
|
+
FileSystem,
|
|
5
|
+
Layer,
|
|
6
|
+
Path,
|
|
7
|
+
Predicate,
|
|
8
|
+
Record,
|
|
9
|
+
Schema,
|
|
10
|
+
Semaphore,
|
|
11
|
+
} from "effect";
|
|
2
12
|
import {
|
|
3
13
|
DevelopmentUsername,
|
|
4
14
|
developmentSelectionKey,
|
|
@@ -31,9 +41,14 @@ export const LocalCompletion = Schema.Struct({
|
|
|
31
41
|
|
|
32
42
|
const projection = (user: User, share: ReadonlyArray<string>): User => ({
|
|
33
43
|
id: user.id,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
...Record.filter(
|
|
45
|
+
{
|
|
46
|
+
name: share.includes("name") ? user.name : undefined,
|
|
47
|
+
email: share.includes("email") ? user.email : undefined,
|
|
48
|
+
image: share.includes("image") ? user.image : undefined,
|
|
49
|
+
},
|
|
50
|
+
Predicate.isNotUndefined,
|
|
51
|
+
),
|
|
37
52
|
});
|
|
38
53
|
|
|
39
54
|
export class LocalId extends Context.Service<
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
|
+
import { Crypto, Effect, Encoding, FileSystem, Path, Predicate, Schema } from "effect";
|
|
3
|
+
import type { ViteDevServer } from "vite";
|
|
4
|
+
import { parseSync } from "oxc-parser";
|
|
5
|
+
import { prepareManagedExecution, type ManagedExecution } from "@ignotum/runtime/quickjs";
|
|
6
|
+
import { apiFunctionParts } from "@ignotum/contracts/runtime/sync";
|
|
7
|
+
import { getFunctionEnvironment } from "@ignotum/contracts/runtime/schema";
|
|
8
|
+
import * as serverNamespace from "../server/index.js";
|
|
9
|
+
import * as internalServerNamespace from "../internal/server.js";
|
|
10
|
+
import * as apiNamespace from "../internal/api.js";
|
|
11
|
+
import { validateManagedReleaseGraph } from "@ignotum/versions/managed-release";
|
|
12
|
+
import { descriptorCodec } from "@ignotum/runtime/schema";
|
|
13
|
+
import { SchemaSnapshotField } from "@ignotum/contracts/version";
|
|
14
|
+
import { attachValueDescriptor, defineEnv, emptyEnv } from "@ignotum/contracts/schema";
|
|
15
|
+
import type { ManagedServerBuildResult } from "../cli/build/managed-server.js";
|
|
16
|
+
import type { buildManagedReleaseGraph } from "../cli/managed-release.js";
|
|
17
|
+
|
|
18
|
+
type DevelopmentModule = Awaited<ReturnType<ViteDevServer["ssrLoadModule"]>>;
|
|
19
|
+
const literal = Schema.encodeSync(Schema.fromJsonString(Schema.String));
|
|
20
|
+
|
|
21
|
+
export class ManagedFunctionLoadFailed extends Schema.TaggedError<ManagedFunctionLoadFailed>()(
|
|
22
|
+
"ManagedFunctionLoadFailed",
|
|
23
|
+
{ cause: Schema.Defect() },
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
export interface ManagedFunctionBuild extends Pick<
|
|
27
|
+
ManagedServerBuildResult,
|
|
28
|
+
"server" | "artifacts" | "bytes"
|
|
29
|
+
> {
|
|
30
|
+
readonly environment?: {
|
|
31
|
+
readonly declaration: Pick<ManagedServerBuildResult["environment"]["declaration"], "fields">;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ManagedFunctionLoader {
|
|
36
|
+
readonly reload: (build: ManagedFunctionBuild) => Effect.Effect<void, ManagedFunctionLoadFailed>;
|
|
37
|
+
readonly invalidate: (cause: unknown) => void;
|
|
38
|
+
readonly ssrLoadModule: (filename: string) => Promise<DevelopmentModule>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ManagedFunctionSelection {
|
|
42
|
+
readonly guest: Effect.Success<ReturnType<typeof buildManagedReleaseGraph>>;
|
|
43
|
+
readonly release: ManagedExecution["release"];
|
|
44
|
+
readonly requirements: ManagedExecution["requirements"];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Verify the deployed graph contract, then run app code with the local executor's SDK bindings. */
|
|
48
|
+
export const createManagedFunctionLoader = Effect.fn("ManagedFunctions.createLoader")(function* (
|
|
49
|
+
appDirectory: string,
|
|
50
|
+
selection: ManagedFunctionSelection,
|
|
51
|
+
) {
|
|
52
|
+
const fs = yield* FileSystem.FileSystem;
|
|
53
|
+
const crypto = yield* Crypto.Crypto;
|
|
54
|
+
const path = yield* Path.Path;
|
|
55
|
+
const directory = yield* fs.makeTempDirectoryScoped({ prefix: "ignotum-managed-functions-" });
|
|
56
|
+
const modules = new Map<string, Promise<DevelopmentModule>>();
|
|
57
|
+
let current = new Map<string, ReadonlyArray<{ readonly name: string; readonly url: string }>>();
|
|
58
|
+
let environment: Promise<DevelopmentModule> = Promise.resolve({ default: emptyEnv });
|
|
59
|
+
let failure: unknown;
|
|
60
|
+
const load = (url: string): Promise<DevelopmentModule> => {
|
|
61
|
+
let loaded = modules.get(url);
|
|
62
|
+
if (loaded === undefined) {
|
|
63
|
+
loaded = import(/* @vite-ignore */ url);
|
|
64
|
+
modules.set(url, loaded);
|
|
65
|
+
}
|
|
66
|
+
return loaded;
|
|
67
|
+
};
|
|
68
|
+
const namespaces = new Map<string, DevelopmentModule>([
|
|
69
|
+
["ignotum/server", serverNamespace],
|
|
70
|
+
["ignotum/internal/server", internalServerNamespace],
|
|
71
|
+
["ignotum/internal/api", apiNamespace],
|
|
72
|
+
]);
|
|
73
|
+
const guest = yield* validateManagedReleaseGraph(
|
|
74
|
+
selection.guest.manifestBytes,
|
|
75
|
+
[...selection.guest.files].map(([path, bytes]) => ({ path, bytes })),
|
|
76
|
+
);
|
|
77
|
+
const bridges = new Map<string, string>();
|
|
78
|
+
for (const [specifier, namespace] of namespaces) {
|
|
79
|
+
const entry = guest.manifest.modules.find(
|
|
80
|
+
(module) => module.path === guest.manifest.entries[specifier],
|
|
81
|
+
);
|
|
82
|
+
if (entry === undefined || entry.exports.some((name) => !Object.hasOwn(namespace, name))) {
|
|
83
|
+
throw new Error(`The local SDK cannot supply managed entry ${specifier}.`);
|
|
84
|
+
}
|
|
85
|
+
const filename = path.join(directory, `${encodeURIComponent(specifier)}.mjs`);
|
|
86
|
+
const key = Symbol.for(filename);
|
|
87
|
+
// A facade captures the existing SDK namespace so schemas and codecs retain their owner.
|
|
88
|
+
// The handoff exists only while this closed, generated facade is initialized.
|
|
89
|
+
yield* Effect.acquireUseRelease(
|
|
90
|
+
Effect.sync(() => {
|
|
91
|
+
Reflect.set(globalThis, key, namespace);
|
|
92
|
+
}),
|
|
93
|
+
() =>
|
|
94
|
+
Effect.gen(function* () {
|
|
95
|
+
const source =
|
|
96
|
+
`const namespace = globalThis[Symbol.for(${literal(filename)})];\n` +
|
|
97
|
+
entry.exports
|
|
98
|
+
.map(
|
|
99
|
+
(name, index) =>
|
|
100
|
+
`const binding${index} = namespace[${literal(name)}]; export { binding${index} as ${name} };`,
|
|
101
|
+
)
|
|
102
|
+
.join("\n");
|
|
103
|
+
yield* fs.writeFileString(filename, source);
|
|
104
|
+
const url = pathToFileURL(filename).href;
|
|
105
|
+
yield* Effect.tryPromise({
|
|
106
|
+
try: () => load(url),
|
|
107
|
+
catch: (cause) => ManagedFunctionLoadFailed.make({ cause }),
|
|
108
|
+
});
|
|
109
|
+
bridges.set(specifier, url);
|
|
110
|
+
}),
|
|
111
|
+
() =>
|
|
112
|
+
Effect.sync(() => {
|
|
113
|
+
Reflect.deleteProperty(globalThis, key);
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const reload = Effect.fn("ManagedFunctions.reload")(
|
|
118
|
+
function* (build: ManagedFunctionBuild) {
|
|
119
|
+
const prepared = yield* prepareManagedExecution(
|
|
120
|
+
{
|
|
121
|
+
graph: build.server.graph,
|
|
122
|
+
artifacts: build.artifacts,
|
|
123
|
+
bytes: build.bytes,
|
|
124
|
+
entry: build.server.schema.entry,
|
|
125
|
+
release: selection.release,
|
|
126
|
+
requirements: selection.requirements,
|
|
127
|
+
guest: {
|
|
128
|
+
manifestBytes: selection.guest.manifestBytes,
|
|
129
|
+
files: [...selection.guest.files].map(([path, bytes]) => ({ path, bytes })),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
"schema",
|
|
133
|
+
).pipe(Effect.provideService(Crypto.Crypto, crypto));
|
|
134
|
+
const urls = new Map(
|
|
135
|
+
prepared.modules
|
|
136
|
+
.filter((module) => module.owner === "App")
|
|
137
|
+
.map((module) => [
|
|
138
|
+
module.id,
|
|
139
|
+
pathToFileURL(path.join(directory, `${encodeURIComponent(module.id)}.mjs`)).href,
|
|
140
|
+
]),
|
|
141
|
+
);
|
|
142
|
+
for (const module of prepared.modules.filter((module) => module.owner === "App")) {
|
|
143
|
+
const source = prepared.load(module.id);
|
|
144
|
+
const parsed = parseSync(`${module.id}.mjs`, source, { sourceType: "module" });
|
|
145
|
+
if (parsed.module.dynamicImports.length > 0)
|
|
146
|
+
throw new Error("Managed local modules cannot use dynamic imports.");
|
|
147
|
+
if (parsed.errors.length > 0)
|
|
148
|
+
throw new Error(`Could not parse managed module ${module.id}.`);
|
|
149
|
+
const replacements: Array<{ start: number; end: number; value: string }> = [];
|
|
150
|
+
for (const statement of parsed.program.body) {
|
|
151
|
+
if (
|
|
152
|
+
statement.type !== "ImportDeclaration" &&
|
|
153
|
+
statement.type !== "ExportNamedDeclaration" &&
|
|
154
|
+
statement.type !== "ExportAllDeclaration"
|
|
155
|
+
)
|
|
156
|
+
continue;
|
|
157
|
+
if (statement.source === null) continue;
|
|
158
|
+
const targetId = prepared.normalize(module.id, statement.source.value);
|
|
159
|
+
const target = bridges.get(statement.source.value) ?? urls.get(targetId);
|
|
160
|
+
if (target === undefined) throw new Error("Missing verified managed module target.");
|
|
161
|
+
replacements.push({
|
|
162
|
+
start: statement.source.start,
|
|
163
|
+
end: statement.source.end,
|
|
164
|
+
value: literal(target),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
let rewritten = source;
|
|
168
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
169
|
+
rewritten =
|
|
170
|
+
rewritten.slice(0, replacement.start) +
|
|
171
|
+
replacement.value +
|
|
172
|
+
rewritten.slice(replacement.end);
|
|
173
|
+
}
|
|
174
|
+
if (module.sourceMap !== undefined) {
|
|
175
|
+
rewritten = rewritten.replace(/^\/\/# sourceMappingURL=.*$/gm, "");
|
|
176
|
+
rewritten += `\n//# sourceMappingURL=data:application/json;base64,${Encoding.encodeBase64(module.sourceMap.bytes)}\n`;
|
|
177
|
+
}
|
|
178
|
+
yield* fs.writeFileString(fileURLToPath(urls.get(module.id)!), rewritten);
|
|
179
|
+
}
|
|
180
|
+
const next = new Map<string, Array<{ name: string; url: string }>>();
|
|
181
|
+
next.set(path.resolve(appDirectory, "server/schema.ts"), [
|
|
182
|
+
{ name: "default", url: urls.get(build.server.schema.entry.module)! },
|
|
183
|
+
]);
|
|
184
|
+
for (const fn of build.server.functions) {
|
|
185
|
+
const { moduleName, functionName } = apiFunctionParts(fn.address);
|
|
186
|
+
const filename = path.resolve(appDirectory, "server", `${moduleName}.ts`);
|
|
187
|
+
const entries = next.get(filename) ?? [];
|
|
188
|
+
entries.push({ name: functionName, url: urls.get(fn.entry.module)! });
|
|
189
|
+
next.set(filename, entries);
|
|
190
|
+
}
|
|
191
|
+
const first = build.server.functions[0];
|
|
192
|
+
const fields = yield* Schema.decodeEffect(Schema.Array(SchemaSnapshotField))(
|
|
193
|
+
build.environment?.declaration.fields ?? [],
|
|
194
|
+
);
|
|
195
|
+
const declaredEnvironment =
|
|
196
|
+
fields.length === 0
|
|
197
|
+
? emptyEnv
|
|
198
|
+
: defineEnv(() =>
|
|
199
|
+
Object.fromEntries(
|
|
200
|
+
fields.map((field) => [
|
|
201
|
+
field.name,
|
|
202
|
+
attachValueDescriptor(
|
|
203
|
+
Schema.make<Schema.Codec<unknown>>(descriptorCodec(field.value).ast),
|
|
204
|
+
field.value,
|
|
205
|
+
),
|
|
206
|
+
]),
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
environment =
|
|
211
|
+
first === undefined
|
|
212
|
+
? Promise.resolve({ default: declaredEnvironment })
|
|
213
|
+
: load(urls.get(first.entry.module)!).then((loaded) => {
|
|
214
|
+
const candidate = loaded[first.entry.exportName];
|
|
215
|
+
return {
|
|
216
|
+
default: Predicate.isObject(candidate)
|
|
217
|
+
? getFunctionEnvironment(candidate)
|
|
218
|
+
: undefined,
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
yield* Effect.tryPromise({
|
|
222
|
+
try: () => environment,
|
|
223
|
+
catch: (cause) => ManagedFunctionLoadFailed.make({ cause }),
|
|
224
|
+
});
|
|
225
|
+
current = next;
|
|
226
|
+
failure = undefined;
|
|
227
|
+
},
|
|
228
|
+
Effect.tapCause((cause) =>
|
|
229
|
+
Effect.sync(() => {
|
|
230
|
+
failure = cause;
|
|
231
|
+
}),
|
|
232
|
+
),
|
|
233
|
+
Effect.mapError((cause) => ManagedFunctionLoadFailed.make({ cause })),
|
|
234
|
+
);
|
|
235
|
+
const loader: ManagedFunctionLoader = {
|
|
236
|
+
reload,
|
|
237
|
+
invalidate: (cause: unknown): void => {
|
|
238
|
+
failure = cause ?? new Error("Managed server compilation failed.");
|
|
239
|
+
},
|
|
240
|
+
ssrLoadModule: async (filename: string): Promise<DevelopmentModule> => {
|
|
241
|
+
if (failure !== undefined) throw failure;
|
|
242
|
+
const normalized = path.resolve(filename);
|
|
243
|
+
if (normalized === path.resolve(appDirectory, "server/env.ts")) return environment;
|
|
244
|
+
const entries = current.get(normalized);
|
|
245
|
+
if (entries === undefined)
|
|
246
|
+
throw new Error(`Module is outside the managed server graph: ${filename}`);
|
|
247
|
+
return Object.fromEntries(
|
|
248
|
+
await Promise.all(
|
|
249
|
+
entries.map(async (entry) => [entry.name, (await load(entry.url)).default]),
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
return loader;
|
|
255
|
+
});
|