ignotum 0.0.14 → 0.0.16

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.
Files changed (127) hide show
  1. package/README.md +6 -6
  2. package/dist/cli/bin.mjs +6042 -4693
  3. package/dist/cli/bin.mjs.map +1 -1
  4. package/dist/cli/emscripten-module-D5GzfUNv.mjs +533 -0
  5. package/dist/cli/emscripten-module-D5GzfUNv.mjs.map +1 -0
  6. package/dist/cli/ffi-7IElU4Co.mjs +148 -0
  7. package/dist/cli/ffi-7IElU4Co.mjs.map +1 -0
  8. package/dist/cli/managed-release-WHYLZwaS.mjs +10902 -0
  9. package/dist/cli/managed-release-WHYLZwaS.mjs.map +1 -0
  10. package/dist/cli/managed-release.d.mts +1 -0
  11. package/dist/cli/managed-release.mjs +14 -0
  12. package/dist/cli/managed-release.mjs.map +1 -0
  13. package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs +1289 -0
  14. package/dist/cli/module-ES6BEMUI-D36I4NVn.mjs.map +1 -0
  15. package/dist/runtime/{api-Cv3hMbzo.d.ts → api-B4fwp5a2.d.ts} +4 -4
  16. package/dist/runtime/bootstrap-BISQSn_O.js +916 -0
  17. package/dist/runtime/bootstrap-BISQSn_O.js.map +1 -0
  18. package/dist/runtime/bootstrap-BTFScKAc.d.ts +545 -0
  19. package/dist/runtime/client.d.ts +50 -15
  20. package/dist/runtime/client.js +7 -1158
  21. package/dist/runtime/client.js.map +1 -1
  22. package/dist/runtime/{descriptor-DCCz5AHd-_nhlnu8U.js → descriptor-CTY4mtwS-cS43EbwS.js} +3 -32
  23. package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js.map +1 -0
  24. package/dist/runtime/{id-Bt9XWRGL.js → id--1m0NuUL.js} +4 -4
  25. package/dist/runtime/id--1m0NuUL.js.map +1 -0
  26. package/dist/runtime/{id-BzFHf3Wo-DGPCjgrf.d.ts → id-7an3nxTu-DZJSM5xh.d.ts} +2 -2
  27. package/dist/runtime/id-CBOt2kDo.d.ts +1 -0
  28. package/dist/runtime/identity-QjtJRxBD.d.ts +2 -0
  29. package/dist/runtime/image-Djtjy4Z8.js +19 -0
  30. package/dist/runtime/image-Djtjy4Z8.js.map +1 -0
  31. package/dist/runtime/{index-DWU-7TIh.d.ts → index-D4seNHlX.d.ts} +3 -4
  32. package/dist/runtime/internal/api.d.ts +1 -1
  33. package/dist/runtime/internal/api.js +30 -1
  34. package/dist/runtime/internal/api.js.map +1 -0
  35. package/dist/runtime/internal/bootstrap.d.ts +2 -0
  36. package/dist/runtime/internal/bootstrap.js +2 -0
  37. package/dist/runtime/internal/client.d.ts +2 -0
  38. package/dist/runtime/internal/client.js +75 -0
  39. package/dist/runtime/internal/client.js.map +1 -0
  40. package/dist/runtime/internal/host.d.ts +6 -7
  41. package/dist/runtime/internal/host.js +5 -4
  42. package/dist/runtime/internal/host.js.map +1 -1
  43. package/dist/runtime/internal/routes.d.ts +2 -0
  44. package/dist/runtime/internal/routes.js +2 -0
  45. package/dist/runtime/internal/server.d.ts +1 -1
  46. package/dist/runtime/internal/server.js +1 -1
  47. package/dist/runtime/internal/types.d.ts +2 -2
  48. package/dist/runtime/internal/types.js +1 -1
  49. package/dist/runtime/pagination-CBOt2kDo.d.ts +1 -0
  50. package/dist/runtime/pagination-D3qd6s9N.js +33 -0
  51. package/dist/runtime/pagination-D3qd6s9N.js.map +1 -0
  52. package/dist/runtime/result-BkziOG1L.d.ts +1 -0
  53. package/dist/runtime/router-BSBI1oN1.js +2377 -0
  54. package/dist/runtime/router-BSBI1oN1.js.map +1 -0
  55. package/dist/runtime/routes-Ce8TVP-T.js +690 -0
  56. package/dist/runtime/routes-Ce8TVP-T.js.map +1 -0
  57. package/dist/runtime/{schema-DqogEydG.js → schema-D1wOqiNH.js} +4 -3
  58. package/dist/runtime/schema-D1wOqiNH.js.map +1 -0
  59. package/dist/runtime/server.d.ts +5 -6
  60. package/dist/runtime/server.js +2 -2
  61. package/dist/runtime/{sync-Bs8J3fIr.d.ts → sync-D-GK2sv9.d.ts} +3 -2
  62. package/dist/runtime/{api-CAgKDij7.js → sync-DzUwA8W9.js} +22 -41
  63. package/dist/runtime/sync-DzUwA8W9.js.map +1 -0
  64. package/dist/runtime/{types-C1iDP2Yf-BnkfBOQf.d.ts → types-DeCCyqLU-BIm7rU-0.d.ts} +123 -6
  65. package/package.json +30 -5
  66. package/src/cli/agent-files.ts +10 -8
  67. package/src/cli/build/managed-client.ts +411 -0
  68. package/src/cli/build/managed-server.ts +294 -0
  69. package/src/cli/build/managed.ts +67 -0
  70. package/src/cli/build/output.ts +47 -0
  71. package/src/cli/build/server.ts +49 -247
  72. package/src/cli/client-config.ts +1 -140
  73. package/src/cli/client-styles.ts +1 -0
  74. package/src/cli/codegen.ts +9 -4
  75. package/src/cli/command.ts +9 -9
  76. package/src/cli/control-client.ts +88 -103
  77. package/src/cli/deploy.ts +120 -184
  78. package/src/cli/dev.ts +506 -107
  79. package/src/cli/image-assets.ts +499 -0
  80. package/src/cli/managed-client-boundaries.ts +103 -0
  81. package/src/cli/managed-dev-platform.ts +118 -0
  82. package/src/cli/managed-exports.ts +219 -0
  83. package/src/cli/managed-release-bin.ts +13 -0
  84. package/src/cli/managed-release.ts +260 -0
  85. package/src/cli/module-boundaries.ts +11 -1
  86. package/src/cli/new-app.ts +111 -87
  87. package/src/cli/route-codegen.ts +311 -0
  88. package/src/cli/route-static.ts +710 -0
  89. package/src/client/bootstrap.ts +110 -0
  90. package/src/client/errors.ts +13 -7
  91. package/src/client/files.ts +64 -0
  92. package/src/client/id.ts +9 -5
  93. package/src/client/image.ts +28 -0
  94. package/src/client/index.ts +25 -2
  95. package/src/client/managed-client.ts +729 -0
  96. package/src/client/managed-upgrade.ts +283 -0
  97. package/src/client/recovery-journal.ts +195 -0
  98. package/src/client/route-boundaries.ts +184 -0
  99. package/src/client/router-history.ts +152 -0
  100. package/src/client/router-store.ts +644 -0
  101. package/src/client/router.ts +324 -0
  102. package/src/client/routes.ts +28 -0
  103. package/src/client/sync.ts +65 -591
  104. package/src/dev-runtime/database.ts +2 -2
  105. package/src/dev-runtime/functions.ts +3 -3
  106. package/src/dev-runtime/managed-functions.ts +255 -0
  107. package/src/dev-runtime/managed-sync.ts +263 -0
  108. package/src/dev-runtime/managed-websocket.ts +71 -0
  109. package/src/dev-runtime/migrations.ts +11 -0
  110. package/src/dev-runtime/sync.ts +236 -236
  111. package/src/image-assets.d.ts +36 -0
  112. package/src/internal/client.ts +186 -0
  113. package/src/internal/http-paths.ts +0 -3
  114. package/src/internal/routes.ts +45 -0
  115. package/dist/runtime/api-CAgKDij7.js.map +0 -1
  116. package/dist/runtime/descriptor-DCCz5AHd-_nhlnu8U.js.map +0 -1
  117. package/dist/runtime/id-Bt9XWRGL.js.map +0 -1
  118. package/dist/runtime/id-E7JKqQ9r.d.ts +0 -1
  119. package/dist/runtime/pagination-CpYCaRYD-C2Rpp4Cw.d.ts +0 -122
  120. package/dist/runtime/pagination-E7JKqQ9r.d.ts +0 -1
  121. package/dist/runtime/result-DKAA4gpS.d.ts +0 -1
  122. package/dist/runtime/schema-DqogEydG.js.map +0 -1
  123. package/src/cli/build/client.ts +0 -119
  124. package/src/cli/build/public.ts +0 -186
  125. package/src/cli/client-entry.ts +0 -152
  126. package/src/cli/client-plugin.ts +0 -136
  127. package/src/client/app.ts +0 -15
@@ -3,31 +3,12 @@ import process from "node:process";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
5
  import * as Config from "effect/Config";
6
- import {
7
- Array,
8
- Crypto,
9
- Effect,
10
- Encoding,
11
- FileSystem,
12
- Path,
13
- Predicate,
14
- Schema,
15
- Stream,
16
- String,
17
- } from "effect";
6
+ import { Array, Effect, FileSystem, Path, Predicate, Schema, Stream, String } from "effect";
18
7
  import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
19
8
  import { createServer, defaultServerConditions, normalizePath } from "vite";
20
- import type { Plugin, ViteDevServer } from "vite";
9
+ import type { ViteDevServer } from "vite";
21
10
 
22
- import {
23
- ServerBuildManifest,
24
- SchemaSnapshot,
25
- SchemaSnapshotField,
26
- EnvironmentArtifact,
27
- schemaSnapshotPath,
28
- serverEnvironmentPath,
29
- type ServerFunctionArtifact,
30
- } from "@ignotum/contracts/deployment";
11
+ import { SchemaSnapshot, SchemaSnapshotField } from "@ignotum/contracts/version";
31
12
  import { FunctionAddress } from "@ignotum/contracts/runtime/sync";
32
13
  import type { FunctionKind } from "@ignotum/contracts/runtime/functions";
33
14
  import {
@@ -38,31 +19,19 @@ import {
38
19
  isDefinedEnv,
39
20
  isDefinedSchema,
40
21
  ValueDescriptor,
22
+ evolutionRules,
23
+ SchemaDefinitionTypeId,
41
24
  type DefinedSchema,
42
25
  type Tables,
43
26
  } from "@ignotum/contracts/schema";
44
- import {
45
- artifactReference,
46
- encodeCanonical,
47
- encodeSchemaSnapshot,
48
- makeSchemaSnapshot,
49
- utf8Bytes,
50
- } from "@ignotum/deployment";
27
+ import { encodeCanonical, makeSchemaSnapshot } from "@ignotum/versions";
51
28
 
52
29
  import { inspectRuntimeFunctionDefinition } from "../../internal/function-definition.js";
53
30
  import { loadEnvironment } from "../environment.js";
54
31
  import { moduleBoundariesPlugin, BuildBoundaryViolation } from "../module-boundaries.js";
55
- import {
56
- causeMessage,
57
- InvalidBuildOutput,
58
- runViteBuild,
59
- sourceMapPathTransform,
60
- ViteBuildFailed,
61
- type GeneratedChunk,
62
- } from "./artifact.js";
32
+ import { causeMessage, InvalidBuildOutput, ViteBuildFailed } from "./artifact.js";
63
33
 
64
- const hostModuleId = "ignotum:host";
65
- const encodeJavaScriptString = Schema.encodeSync(Schema.fromJsonString(Schema.String));
34
+ import { compileManagedServer } from "./managed-server.js";
66
35
 
67
36
  const DiscoveredServerFunction = Schema.Struct({
68
37
  address: FunctionAddress,
@@ -85,7 +54,15 @@ interface DiscoveredServerFunction {
85
54
  readonly returns?: ValueDescriptor;
86
55
  }
87
56
 
57
+ const DiscoveredConversion = Schema.Struct({
58
+ table: Schema.String,
59
+ field: Schema.String,
60
+ from: ValueDescriptor,
61
+ to: ValueDescriptor,
62
+ });
63
+
88
64
  const ServerDiscoveryManifest = Schema.Struct({
65
+ conversions: Schema.Array(DiscoveredConversion),
89
66
  environment: Schema.Struct({
90
67
  fields: Schema.Array(SchemaSnapshotField),
91
68
  hasDefinition: Schema.Boolean,
@@ -94,19 +71,10 @@ const ServerDiscoveryManifest = Schema.Struct({
94
71
  functions: Schema.Array(DiscoveredServerFunction),
95
72
  schema: SchemaSnapshot,
96
73
  });
97
- type ServerDiscoveryManifest = typeof ServerDiscoveryManifest.Type;
74
+ export type ServerDiscoveryManifest = typeof ServerDiscoveryManifest.Type;
98
75
  const ServerDiscoveryManifestJson = Schema.fromJsonString(ServerDiscoveryManifest);
99
76
  const ServerModuleNamesJson = Schema.fromJsonString(Schema.Array(Schema.String));
100
77
 
101
- export interface ServerBuildResult {
102
- readonly environment: {
103
- readonly bytes: Uint8Array;
104
- readonly path: typeof serverEnvironmentPath;
105
- };
106
- readonly functions: number;
107
- readonly manifestPath: string;
108
- }
109
-
110
78
  export class ServerFunctionDiscoveryFailed extends Schema.TaggedError<ServerFunctionDiscoveryFailed>()(
111
79
  "ServerFunctionDiscoveryFailed",
112
80
  {
@@ -160,6 +128,7 @@ const acquireModuleRunner = Effect.fn("Deploy.acquireServerModuleRunner")(functi
160
128
  },
161
129
  root: appDirectory,
162
130
  server: {
131
+ hmr: false,
163
132
  forwardConsole: false,
164
133
  middlewareMode: true,
165
134
  fs: { strict: true, allow: [appDirectory] },
@@ -397,7 +366,34 @@ export const runServerDiscoveryWorker = Effect.fn("Deploy.runServerDiscoveryWork
397
366
  message: "The application schema does not have canonical Ignotum descriptors.",
398
367
  }),
399
368
  });
369
+ const conversions: Array<typeof DiscoveredConversion.Type> = [];
370
+ const visit = (table: string, field: string, value: Schema.Constraint): void => {
371
+ const rules = evolutionRules(value);
372
+ const to = getValueDescriptor(value);
373
+ for (const previous of rules.previous ?? []) {
374
+ const from = getValueDescriptor(previous.schema);
375
+ if (from !== undefined && to !== undefined) conversions.push({ table, field, from, to });
376
+ visit(table, field, previous.schema);
377
+ }
378
+ const children = rules.children;
379
+ if (children?.type === "object") {
380
+ for (const child of Object.values(children.fields)) visit(table, field, child);
381
+ } else if (children?.type === "union") {
382
+ for (const child of children.members) visit(table, field, child);
383
+ } else if (children !== undefined) visit(table, field, children.value);
384
+ };
385
+ for (const [table, definition] of Object.entries(discovered.schema[SchemaDefinitionTypeId])) {
386
+ for (const [field, value] of Object.entries(definition.fields)) visit(table, field, value);
387
+ }
400
388
  return yield* Schema.encodeEffect(ServerDiscoveryManifestJson)({
389
+ conversions: [
390
+ ...new Map(
391
+ conversions.map((conversion) => [
392
+ encodeCanonical(DiscoveredConversion, conversion),
393
+ conversion,
394
+ ]),
395
+ ).values(),
396
+ ],
401
397
  environment: discovered.environment,
402
398
  functions: discovered.functions,
403
399
  schema,
@@ -513,210 +509,16 @@ const discoverServerFunctions = Effect.fn("Deploy.discoverServerFunctionsIsolate
513
509
  );
514
510
  });
515
511
 
516
- const serverFunctionEntryPlugin = (
517
- definition: DiscoveredServerFunction,
518
- schemaPath?: string,
519
- ): Plugin => {
520
- const entryId = `virtual:ignotum/server-function/${definition.address}`;
521
- const resolvedEntryId = `\0${entryId}`;
522
- const source =
523
- schemaPath === undefined
524
- ? `import { call } from ${encodeJavaScriptString(hostModuleId)}
525
- import { invoke } from "ignotum/internal/host"
526
- import { ${definition.exportName} as definition } from ${encodeJavaScriptString(normalizePath(definition.modulePath))}
527
-
528
- export default async function handler(encodedRequest, encodedEnvironment) {
529
- return invoke(definition, JSON.parse(encodedRequest), call, JSON.parse(encodedEnvironment))
530
- }
531
- `
532
- : `import { evolve } from "ignotum/internal/host"
533
- import schema from ${encodeJavaScriptString(normalizePath(schemaPath))}
534
-
535
- export default function handler(encodedRequest) {
536
- return evolve(schema, encodedRequest)
537
- }
538
- `;
539
-
540
- return {
541
- name: "ignotum:server-function-entry",
542
- load: (id) => (id === resolvedEntryId ? source : undefined),
543
- resolveId: (id) => (id === entryId ? resolvedEntryId : undefined),
544
- };
545
- };
546
-
547
- const buildServerFunction = Effect.fn("Deploy.buildServerFunction")(function* (
548
- appDirectory: string,
549
- serverOutputDirectory: string,
550
- conditions: ReadonlyArray<string>,
551
- definition: DiscoveredServerFunction,
552
- schemaPath?: string,
553
- ) {
554
- const fileSystem = yield* FileSystem.FileSystem;
555
- const path = yield* Path.Path;
556
- const functionDirectory = path.join(serverOutputDirectory, "functions", definition.moduleName);
557
- const transformSourceMapPath = yield* sourceMapPathTransform(appDirectory);
558
- const boundaries = yield* moduleBoundariesPlugin("Server", appDirectory);
559
- const entryId = `virtual:ignotum/server-function/${definition.address}`;
560
- yield* fileSystem.makeDirectory(functionDirectory, { recursive: true });
561
-
562
- const output = yield* runViteBuild(definition.address, {
563
- build: {
564
- cssCodeSplit: false,
565
- emptyOutDir: false,
566
- minify: "oxc",
567
- modulePreload: false,
568
- outDir: functionDirectory,
569
- rolldownOptions: {
570
- external: [hostModuleId],
571
- input: entryId,
572
- preserveEntrySignatures: "strict",
573
- treeshake: {
574
- manualPureFunctions: ["mutation", "query"],
575
- },
576
- output: {
577
- codeSplitting: false,
578
- entryFileNames: `${definition.exportName}-[hash].mjs`,
579
- format: "es",
580
- sourcemapPathTransform: transformSourceMapPath,
581
- },
582
- },
583
- sourcemap: true,
584
- target: "es2020",
585
- },
586
- configFile: false,
587
- logLevel: "warn",
588
- mode: "production",
589
- plugins: [boundaries, serverFunctionEntryPlugin(definition, schemaPath)],
590
- publicDir: false,
591
- resolve: {
592
- conditions: [...conditions],
593
- noExternal: true,
594
- tsconfigPaths: true,
595
- },
596
- root: appDirectory,
597
- });
598
- const entries = Array.filter(
599
- output,
600
- (item): item is GeneratedChunk => item.type === "chunk" && item.isEntry,
601
- );
602
- if (entries.length !== 1) {
603
- return yield* InvalidBuildOutput.make({
604
- message: `${definition.address} did not emit exactly one entry chunk.`,
605
- });
606
- }
607
- const entry = entries[0];
608
- if (entry === undefined) {
609
- return yield* InvalidBuildOutput.make({
610
- message: `${definition.address} did not emit an entry chunk.`,
611
- });
612
- }
613
- if (
614
- entry.dynamicImports.length > 0 ||
615
- (schemaPath === undefined
616
- ? entry.imports.length !== 1 || entry.imports[0] !== hostModuleId
617
- : entry.imports.length !== 0) ||
618
- !entry.exports.includes("default")
619
- ) {
620
- return yield* InvalidBuildOutput.make({
621
- message: `${definition.address} is not a self-contained default-exported handler. Imports: ${entry.imports.join(", ") || "none"}; dynamic imports: ${entry.dynamicImports.join(", ") || "none"}; exports: ${entry.exports.join(", ") || "none"}.`,
622
- });
623
- }
624
-
625
- const relativeFile = normalizePath(path.join("functions", definition.moduleName, entry.fileName));
626
- const relativeSourceMap = `${relativeFile}.map`;
627
- const absoluteFile = path.join(serverOutputDirectory, relativeFile);
628
- const absoluteSourceMap = path.join(serverOutputDirectory, relativeSourceMap);
629
- const sourceMapExists = yield* fileSystem.exists(absoluteSourceMap);
630
- if (!sourceMapExists) {
631
- return yield* InvalidBuildOutput.make({
632
- message: `${definition.address} did not emit a source map.`,
633
- });
634
- }
635
- const bundlePath = normalizePath(path.join("server", relativeFile));
636
- const sourceMapPath = normalizePath(path.join("server", relativeSourceMap));
637
-
638
- const artifact = {
639
- address: definition.address,
640
- kind: definition.kind,
641
- bundle: yield* artifactReference(bundlePath, yield* fileSystem.readFile(absoluteFile)),
642
- sourceMap: yield* artifactReference(
643
- sourceMapPath,
644
- yield* fileSystem.readFile(absoluteSourceMap),
645
- ),
646
- };
647
- const withArgs =
648
- definition.args === undefined ? artifact : { ...artifact, args: definition.args };
649
- const withErrors =
650
- definition.errors === undefined ? withArgs : { ...withArgs, errors: definition.errors };
651
- return definition.returns === undefined
652
- ? (withErrors satisfies ServerFunctionArtifact)
653
- : ({ ...withErrors, returns: definition.returns } satisfies ServerFunctionArtifact);
654
- });
655
-
656
- export const buildServer = Effect.fn("Deploy.buildServer")(function* (
512
+ export const buildManagedServer = Effect.fn("Deploy.buildManagedServer")(function* (
657
513
  appDirectory: string,
658
514
  outputDirectory: string,
659
515
  functionModules: ReadonlyArray<string>,
660
516
  ) {
661
- const fileSystem = yield* FileSystem.FileSystem;
662
- const path = yield* Path.Path;
663
- const conditions = yield* serverConditions();
664
517
  const discovered = yield* discoverServerFunctions(appDirectory, functionModules);
665
- const crypto = yield* Crypto.Crypto;
666
- const environmentArtifact = {
667
- formatVersion: 1,
668
- nonce: Encoding.encodeBase64Url(yield* crypto.randomBytes(32)),
669
- values: discovered.environment.values,
670
- } satisfies EnvironmentArtifact;
671
- const environmentBytes = utf8Bytes(
672
- `${encodeCanonical(EnvironmentArtifact, environmentArtifact)}\n`,
673
- );
674
-
675
- yield* fileSystem.makeDirectory(outputDirectory, { recursive: true });
676
- const encodedSnapshot = encodeSchemaSnapshot(discovered.schema);
677
- const snapshotBytes = utf8Bytes(encodedSnapshot);
678
- yield* fileSystem.writeFile(path.join(outputDirectory, "schema.json"), snapshotBytes);
679
- const functions = yield* Effect.forEach(
680
- discovered.functions,
681
- (definition) => buildServerFunction(appDirectory, outputDirectory, conditions, definition),
682
- { concurrency: 4 },
683
- );
684
- const schemaPath = path.join(appDirectory, "server", "schema.ts");
685
- const schemaEvolution = yield* buildServerFunction(
518
+ return yield* compileManagedServer(
686
519
  appDirectory,
687
520
  outputDirectory,
688
- conditions,
689
- {
690
- address: FunctionAddress.make("api._schema.evolve"),
691
- exportName: "evolve",
692
- kind: "Mutation",
693
- moduleName: "_schema",
694
- modulePath: schemaPath,
695
- },
696
- schemaPath,
521
+ yield* serverConditions(),
522
+ discovered,
697
523
  );
698
- const manifest = {
699
- formatVersion: 4,
700
- schemaEvolution: { bundle: schemaEvolution.bundle, sourceMap: schemaEvolution.sourceMap },
701
- environment: {
702
- artifact: yield* artifactReference(serverEnvironmentPath, environmentBytes),
703
- fields: discovered.environment.fields,
704
- },
705
- schema: yield* artifactReference(schemaSnapshotPath, snapshotBytes),
706
- functions,
707
- } satisfies ServerBuildManifest;
708
- const encodedManifest = `${encodeCanonical(ServerBuildManifest, manifest)}\n`;
709
- const manifestPath = path.join(outputDirectory, "manifest.json");
710
- yield* fileSystem.writeFileString(manifestPath, encodedManifest);
711
- yield* Schema.decodeEffect(Schema.fromJsonString(ServerBuildManifest))(encodedManifest).pipe(
712
- Effect.mapError(() =>
713
- InvalidBuildOutput.make({ message: "Ignotum emitted an invalid server manifest." }),
714
- ),
715
- );
716
-
717
- return {
718
- environment: { bytes: environmentBytes, path: serverEnvironmentPath },
719
- functions: functions.length,
720
- manifestPath,
721
- } satisfies ServerBuildResult;
722
524
  });
@@ -1,29 +1,4 @@
1
- import { createRequire } from "node:module";
2
- import { fileURLToPath } from "node:url";
3
-
4
- import { Effect, FileSystem, Path, Schema } from "effect";
5
- import { defaultClientConditions, normalizePath } from "vite";
6
- import type { Alias } from "vite";
7
-
8
- const clientEntryFileName = "index.tsx";
9
- const clientIconFileName = "icon.svg";
10
- const preactSpecifiers = [
11
- "preact/jsx-dev-runtime",
12
- "preact/jsx-runtime",
13
- "preact/devtools",
14
- "preact/debug",
15
- "preact/hooks",
16
- "preact",
17
- ] as const;
18
- const prefreshSpecifiers = ["@prefresh/core", "@prefresh/utils"] as const;
19
-
20
- export class ClientFileNotFound extends Schema.TaggedError<ClientFileNotFound>()(
21
- "ClientFileNotFound",
22
- {
23
- message: Schema.String,
24
- path: Schema.String,
25
- },
26
- ) {}
1
+ import { Schema } from "effect";
27
2
 
28
3
  export class InvalidClientFile extends Schema.TaggedError<InvalidClientFile>()(
29
4
  "InvalidClientFile",
@@ -32,117 +7,3 @@ export class InvalidClientFile extends Schema.TaggedError<InvalidClientFile>()(
32
7
  path: Schema.String,
33
8
  },
34
9
  ) {}
35
-
36
- export class ClientRuntimeResolutionFailed extends Schema.TaggedError<ClientRuntimeResolutionFailed>()(
37
- "ClientRuntimeResolutionFailed",
38
- {
39
- cause: Schema.Defect(),
40
- message: Schema.String,
41
- },
42
- ) {}
43
-
44
- export interface ClientBuildConfig {
45
- readonly aliases: ReadonlyArray<Alias>;
46
- readonly conditions: ReadonlyArray<string>;
47
- }
48
-
49
- export interface ClientFiles {
50
- readonly clientDirectory: string;
51
- readonly entryPath: string;
52
- readonly iconPath: string | undefined;
53
- }
54
-
55
- export const validateClientFiles = Effect.fn("ClientConfig.validateFiles")(function* (
56
- appDirectory: string,
57
- ) {
58
- const fileSystem = yield* FileSystem.FileSystem;
59
- const path = yield* Path.Path;
60
- const clientDirectory = path.join(appDirectory, "client");
61
- const nestedPublicDirectory = path.join(clientDirectory, "public");
62
- if (yield* fileSystem.exists(nestedPublicDirectory)) {
63
- return yield* InvalidClientFile.make({
64
- message: `Client public files must be placed in the top-level public directory, not ${nestedPublicDirectory}.`,
65
- path: nestedPublicDirectory,
66
- });
67
- }
68
- const entryPath = path.join(clientDirectory, clientEntryFileName);
69
- if (!(yield* fileSystem.exists(entryPath))) {
70
- return yield* ClientFileNotFound.make({
71
- message: `Required client file not found: ${entryPath}`,
72
- path: entryPath,
73
- });
74
- }
75
- const entryInfo = yield* fileSystem.stat(entryPath);
76
- if (entryInfo.type !== "File") {
77
- return yield* InvalidClientFile.make({
78
- message: `Required client entry is not a regular file: ${entryPath}`,
79
- path: entryPath,
80
- });
81
- }
82
-
83
- const iconPath = path.join(clientDirectory, clientIconFileName);
84
- const hasIcon = yield* fileSystem.exists(iconPath);
85
- if (hasIcon) {
86
- const iconInfo = yield* fileSystem.stat(iconPath);
87
- if (iconInfo.type !== "File") {
88
- return yield* InvalidClientFile.make({
89
- message: `Optional client icon is not a regular file: ${iconPath}`,
90
- path: iconPath,
91
- });
92
- }
93
- }
94
-
95
- return {
96
- clientDirectory,
97
- entryPath,
98
- iconPath: hasIcon ? iconPath : undefined,
99
- } satisfies ClientFiles;
100
- });
101
-
102
- export const resolveClientBuildConfig = Effect.fn("ClientConfig.resolve")(function* (
103
- includePrefresh: boolean,
104
- ) {
105
- const fileSystem = yield* FileSystem.FileSystem;
106
- const sourceClientEntry = fileURLToPath(new URL("../../src/client/index.ts", import.meta.url));
107
- const sourceContractsEntry = fileURLToPath(
108
- new URL("../../../contracts/src/schema/index.ts", import.meta.url),
109
- );
110
- const hasSourceClient = yield* fileSystem.exists(sourceClientEntry);
111
- const hasSourceContracts = yield* fileSystem.exists(sourceContractsEntry);
112
- const hasWorkspaceSourceExports = hasSourceClient && hasSourceContracts;
113
-
114
- return yield* Effect.try({
115
- try: (): ClientBuildConfig => {
116
- const prefreshRequire = createRequire(import.meta.resolve("@prefresh/vite"));
117
- const aliases: ReadonlyArray<Alias> = [
118
- {
119
- find: /^tailwindcss$/,
120
- replacement: normalizePath(fileURLToPath(import.meta.resolve("tailwindcss/index.css"))),
121
- },
122
- ...preactSpecifiers.map((specifier) => ({
123
- find: specifier,
124
- replacement: normalizePath(fileURLToPath(import.meta.resolve(specifier))),
125
- })),
126
- ...(includePrefresh
127
- ? prefreshSpecifiers.map((specifier) => ({
128
- find: specifier,
129
- replacement: normalizePath(prefreshRequire.resolve(specifier)),
130
- }))
131
- : []),
132
- ];
133
-
134
- return {
135
- aliases,
136
- conditions: [
137
- ...(hasWorkspaceSourceExports ? ["@ignotum/source"] : []),
138
- ...defaultClientConditions,
139
- ],
140
- };
141
- },
142
- catch: (cause) =>
143
- ClientRuntimeResolutionFailed.make({
144
- cause,
145
- message: "Ignotum could not resolve its client runtime.",
146
- }),
147
- });
148
- });
@@ -0,0 +1 @@
1
+ export const clientStylesSource = '@import "tailwindcss";\n';
@@ -2,7 +2,8 @@ import { Array, Effect, FileSystem, Path, Schema, String } from "effect";
2
2
 
3
3
  import { FunctionNamePart } from "@ignotum/contracts/runtime/sync";
4
4
 
5
- const generatedHeader = "// Generated by `ignotum codegen`. Do not edit.";
5
+ export const generatedHeader = "// Generated by `ignotum codegen`. Do not edit.";
6
+ export const generatedImageTypes = '/// <reference types="ignotum/image-assets" />';
6
7
 
7
8
  export class SchemaNotFound extends Schema.TaggedError<SchemaNotFound>()("SchemaNotFound", {
8
9
  message: Schema.String,
@@ -25,7 +26,7 @@ export class GeneratedFileConflict extends Schema.TaggedError<GeneratedFileConfl
25
26
  },
26
27
  ) {}
27
28
 
28
- interface GeneratedOutput {
29
+ export interface GeneratedOutput {
29
30
  readonly content: string;
30
31
  readonly path: string;
31
32
  }
@@ -99,6 +100,8 @@ const renderApi = (moduleNames: ReadonlyArray<string>): string => {
99
100
  });
100
101
  return `${generatedHeader}
101
102
 
103
+ ${generatedImageTypes}
104
+
102
105
  import { createApi } from "ignotum/internal/api";
103
106
 
104
107
  type Modules = {
@@ -109,7 +112,9 @@ export const api = createApi<Modules>();
109
112
  `;
110
113
  };
111
114
 
112
- const inspectOutput = Effect.fn("Codegen.inspectOutput")(function* (output: GeneratedOutput) {
115
+ export const inspectOutput = Effect.fn("Codegen.inspectOutput")(function* (
116
+ output: GeneratedOutput,
117
+ ) {
113
118
  const fileSystem = yield* FileSystem.FileSystem;
114
119
  const exists = yield* fileSystem.exists(output.path);
115
120
 
@@ -133,7 +138,7 @@ const inspectOutput = Effect.fn("Codegen.inspectOutput")(function* (output: Gene
133
138
  return "write" as const;
134
139
  });
135
140
 
136
- const writeOutput = Effect.fn("Codegen.writeOutput")(function* (output: GeneratedOutput) {
141
+ export const writeOutput = Effect.fn("Codegen.writeOutput")(function* (output: GeneratedOutput) {
137
142
  const fileSystem = yield* FileSystem.FileSystem;
138
143
  const path = yield* Path.Path;
139
144
  const directory = path.dirname(output.path);
@@ -5,7 +5,7 @@ import { AppSlug } from "@ignotum/contracts/control";
5
5
  import { IdGenerator } from "@ignotum/shared/id";
6
6
 
7
7
  import packageJson from "../../package.json" with { type: "json" };
8
- import { generate } from "./codegen.js";
8
+ import { generateClientRoutes } from "./route-codegen.js";
9
9
  import {
10
10
  AuthCredentialStore,
11
11
  login as runLogin,
@@ -13,8 +13,8 @@ import {
13
13
  status as readAuthStatus,
14
14
  } from "./auth-client.js";
15
15
  import { ControlClient } from "./control-client.js";
16
- import { deploy as runDeploy, resumeDeployment } from "./deploy.js";
17
- import { DeploymentId } from "@ignotum/contracts/runtime/identity";
16
+ import { deploy as runDeploy, resumeVersion } from "./deploy.js";
17
+ import { VersionId } from "@ignotum/contracts/runtime/identity";
18
18
  import { resetDevDatabase } from "../dev-runtime/dev-database.js";
19
19
  import { dev as runDev } from "./dev.js";
20
20
  import { createApp } from "./new-app.js";
@@ -80,7 +80,7 @@ const codegen = Command.make(
80
80
  Effect.fn("codegen")(function* () {
81
81
  const path = yield* Path.Path;
82
82
  const terminal = yield* Terminal.Terminal;
83
- const result = yield* generate(path.resolve(".")).pipe(
83
+ const result = yield* generateClientRoutes(path.resolve(".")).pipe(
84
84
  Effect.mapError((error) =>
85
85
  CliError.UserError.make({ cause: error, userMessage: error.message }),
86
86
  ),
@@ -146,8 +146,8 @@ const deploy = Command.make(
146
146
  "deploy",
147
147
  {
148
148
  resume: Flag.string("resume").pipe(
149
- Flag.withDescription("Finish activation of an already uploaded deployment."),
150
- Flag.withSchema(DeploymentId),
149
+ Flag.withDescription("Finish activation of an already uploaded version."),
150
+ Flag.withSchema(VersionId),
151
151
  Flag.optional,
152
152
  ),
153
153
  app: Flag.string("app").pipe(
@@ -160,7 +160,7 @@ const deploy = Command.make(
160
160
  const path = yield* Path.Path;
161
161
  const terminal = yield* Terminal.Terminal;
162
162
  if (Option.isSome(resume)) {
163
- const result = yield* resumeDeployment(
163
+ const result = yield* resumeVersion(
164
164
  path.resolve("."),
165
165
  resume.value,
166
166
  Option.getOrUndefined(app),
@@ -172,7 +172,7 @@ const deploy = Command.make(
172
172
  ),
173
173
  );
174
174
  yield* terminal.display(
175
- `Deployment activated.\n\nApp: ${result.appUrl}\nDeployment: ${result.deploymentId}\n`,
175
+ `Version ${result.versionNumber} is live.\n\nApp: ${result.appUrl}\nVersion ID: ${result.versionId}\n`,
176
176
  );
177
177
  return;
178
178
  }
@@ -185,7 +185,7 @@ const deploy = Command.make(
185
185
  );
186
186
 
187
187
  yield* terminal.display(
188
- `Deployed ${result.clientAssets} client artifact${result.clientAssets === 1 ? "" : "s"} and ${result.serverFunctions} server function${result.serverFunctions === 1 ? "" : "s"}.\n\nApp: ${result.appUrl}\nDeployment: ${result.deploymentId}\n`,
188
+ `Version ${result.versionNumber} is live.\n\nApp: ${result.appUrl}\nVersion ID: ${result.versionId}\n`,
189
189
  );
190
190
  }),
191
191
  ).pipe(Command.withDescription("Build and deploy the application to Ignotum Cloud."));