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.
Files changed (131) hide show
  1. package/README.md +6 -6
  2. package/dist/cli/bin.mjs +6111 -4448
  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 -1138
  21. package/dist/runtime/client.js.map +1 -1
  22. package/dist/runtime/descriptor-CTY4mtwS-cS43EbwS.js +269 -0
  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-D4seNHlX.d.ts +184 -0
  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 +25 -8
  41. package/dist/runtime/internal/host.js +27 -6
  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 -1
  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-1Zs03-iS.js → schema-D1wOqiNH.js} +40 -12
  58. package/dist/runtime/schema-D1wOqiNH.js.map +1 -0
  59. package/dist/runtime/server.d.ts +5 -5
  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-DeCCyqLU-BIm7rU-0.d.ts +341 -0
  65. package/package.json +29 -4
  66. package/src/cli/agent-files.ts +14 -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 +51 -220
  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 +28 -5
  76. package/src/cli/control-client.ts +84 -101
  77. package/src/cli/deploy.ts +133 -177
  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 +66 -592
  104. package/src/dev-runtime/database.ts +77 -2
  105. package/src/dev-runtime/functions.ts +3 -3
  106. package/src/dev-runtime/id.ts +19 -4
  107. package/src/dev-runtime/managed-functions.ts +255 -0
  108. package/src/dev-runtime/managed-sync.ts +263 -0
  109. package/src/dev-runtime/managed-websocket.ts +71 -0
  110. package/src/dev-runtime/migrations.ts +20 -0
  111. package/src/dev-runtime/sync.ts +236 -236
  112. package/src/image-assets.d.ts +36 -0
  113. package/src/internal/client.ts +186 -0
  114. package/src/internal/host.ts +1 -1
  115. package/src/internal/http-paths.ts +0 -3
  116. package/src/internal/routes.ts +45 -0
  117. package/dist/runtime/api-CAgKDij7.js.map +0 -1
  118. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js +0 -154
  119. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js.map +0 -1
  120. package/dist/runtime/id-Bt9XWRGL.js.map +0 -1
  121. package/dist/runtime/id-Dz0apuB3.d.ts +0 -1
  122. package/dist/runtime/index-D54flWtH.d.ts +0 -402
  123. package/dist/runtime/pagination-DnKg3dkI-r5ZUxBBx.d.ts +0 -112
  124. package/dist/runtime/pagination-Dz0apuB3.d.ts +0 -1
  125. package/dist/runtime/result-DKAA4gpS.d.ts +0 -1
  126. package/dist/runtime/schema-1Zs03-iS.js.map +0 -1
  127. package/src/cli/build/client.ts +0 -119
  128. package/src/cli/build/public.ts +0 -186
  129. package/src/cli/client-entry.ts +0 -152
  130. package/src/cli/client-plugin.ts +0 -136
  131. 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,181 +509,16 @@ const discoverServerFunctions = Effect.fn("Deploy.discoverServerFunctionsIsolate
513
509
  );
514
510
  });
515
511
 
516
- const serverFunctionEntryPlugin = (definition: DiscoveredServerFunction): Plugin => {
517
- const entryId = `virtual:ignotum/server-function/${definition.address}`;
518
- const resolvedEntryId = `\0${entryId}`;
519
- const source = `import { call } from ${encodeJavaScriptString(hostModuleId)}
520
- import { invoke } from "ignotum/internal/host"
521
- import { ${definition.exportName} as definition } from ${encodeJavaScriptString(normalizePath(definition.modulePath))}
522
-
523
- export default async function handler(encodedRequest, encodedEnvironment) {
524
- return invoke(definition, JSON.parse(encodedRequest), call, JSON.parse(encodedEnvironment))
525
- }
526
- `;
527
-
528
- return {
529
- name: "ignotum:server-function-entry",
530
- load: (id) => (id === resolvedEntryId ? source : undefined),
531
- resolveId: (id) => (id === entryId ? resolvedEntryId : undefined),
532
- };
533
- };
534
-
535
- const buildServerFunction = Effect.fn("Deploy.buildServerFunction")(function* (
536
- appDirectory: string,
537
- serverOutputDirectory: string,
538
- conditions: ReadonlyArray<string>,
539
- definition: DiscoveredServerFunction,
540
- ) {
541
- const fileSystem = yield* FileSystem.FileSystem;
542
- const path = yield* Path.Path;
543
- const functionDirectory = path.join(serverOutputDirectory, "functions", definition.moduleName);
544
- const transformSourceMapPath = yield* sourceMapPathTransform(appDirectory);
545
- const boundaries = yield* moduleBoundariesPlugin("Server", appDirectory);
546
- const entryId = `virtual:ignotum/server-function/${definition.address}`;
547
- yield* fileSystem.makeDirectory(functionDirectory, { recursive: true });
548
-
549
- const output = yield* runViteBuild(definition.address, {
550
- build: {
551
- cssCodeSplit: false,
552
- emptyOutDir: false,
553
- minify: "oxc",
554
- modulePreload: false,
555
- outDir: functionDirectory,
556
- rolldownOptions: {
557
- external: [hostModuleId],
558
- input: entryId,
559
- preserveEntrySignatures: "strict",
560
- treeshake: {
561
- manualPureFunctions: ["mutation", "query"],
562
- },
563
- output: {
564
- codeSplitting: false,
565
- entryFileNames: `${definition.exportName}-[hash].mjs`,
566
- format: "es",
567
- sourcemapPathTransform: transformSourceMapPath,
568
- },
569
- },
570
- sourcemap: true,
571
- target: "es2020",
572
- },
573
- configFile: false,
574
- logLevel: "warn",
575
- mode: "production",
576
- plugins: [boundaries, serverFunctionEntryPlugin(definition)],
577
- publicDir: false,
578
- resolve: {
579
- conditions: [...conditions],
580
- noExternal: true,
581
- tsconfigPaths: true,
582
- },
583
- root: appDirectory,
584
- });
585
- const entries = Array.filter(
586
- output,
587
- (item): item is GeneratedChunk => item.type === "chunk" && item.isEntry,
588
- );
589
- if (entries.length !== 1) {
590
- return yield* InvalidBuildOutput.make({
591
- message: `${definition.address} did not emit exactly one entry chunk.`,
592
- });
593
- }
594
- const entry = entries[0];
595
- if (entry === undefined) {
596
- return yield* InvalidBuildOutput.make({
597
- message: `${definition.address} did not emit an entry chunk.`,
598
- });
599
- }
600
- if (
601
- entry.dynamicImports.length > 0 ||
602
- entry.imports.length !== 1 ||
603
- entry.imports[0] !== hostModuleId ||
604
- !entry.exports.includes("default")
605
- ) {
606
- return yield* InvalidBuildOutput.make({
607
- 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"}.`,
608
- });
609
- }
610
-
611
- const relativeFile = normalizePath(path.join("functions", definition.moduleName, entry.fileName));
612
- const relativeSourceMap = `${relativeFile}.map`;
613
- const absoluteFile = path.join(serverOutputDirectory, relativeFile);
614
- const absoluteSourceMap = path.join(serverOutputDirectory, relativeSourceMap);
615
- const sourceMapExists = yield* fileSystem.exists(absoluteSourceMap);
616
- if (!sourceMapExists) {
617
- return yield* InvalidBuildOutput.make({
618
- message: `${definition.address} did not emit a source map.`,
619
- });
620
- }
621
- const bundlePath = normalizePath(path.join("server", relativeFile));
622
- const sourceMapPath = normalizePath(path.join("server", relativeSourceMap));
623
-
624
- const artifact = {
625
- address: definition.address,
626
- kind: definition.kind,
627
- bundle: yield* artifactReference(bundlePath, yield* fileSystem.readFile(absoluteFile)),
628
- sourceMap: yield* artifactReference(
629
- sourceMapPath,
630
- yield* fileSystem.readFile(absoluteSourceMap),
631
- ),
632
- };
633
- const withArgs =
634
- definition.args === undefined ? artifact : { ...artifact, args: definition.args };
635
- const withErrors =
636
- definition.errors === undefined ? withArgs : { ...withArgs, errors: definition.errors };
637
- return definition.returns === undefined
638
- ? (withErrors satisfies ServerFunctionArtifact)
639
- : ({ ...withErrors, returns: definition.returns } satisfies ServerFunctionArtifact);
640
- });
641
-
642
- export const buildServer = Effect.fn("Deploy.buildServer")(function* (
512
+ export const buildManagedServer = Effect.fn("Deploy.buildManagedServer")(function* (
643
513
  appDirectory: string,
644
514
  outputDirectory: string,
645
515
  functionModules: ReadonlyArray<string>,
646
516
  ) {
647
- const fileSystem = yield* FileSystem.FileSystem;
648
- const path = yield* Path.Path;
649
- const conditions = yield* serverConditions();
650
517
  const discovered = yield* discoverServerFunctions(appDirectory, functionModules);
651
- const crypto = yield* Crypto.Crypto;
652
- const environmentArtifact = {
653
- formatVersion: 1,
654
- nonce: Encoding.encodeBase64Url(yield* crypto.randomBytes(32)),
655
- values: discovered.environment.values,
656
- } satisfies EnvironmentArtifact;
657
- const environmentBytes = utf8Bytes(
658
- `${encodeCanonical(EnvironmentArtifact, environmentArtifact)}\n`,
659
- );
660
-
661
- yield* fileSystem.makeDirectory(outputDirectory, { recursive: true });
662
- const encodedSnapshot = encodeSchemaSnapshot(discovered.schema);
663
- const snapshotBytes = utf8Bytes(encodedSnapshot);
664
- yield* fileSystem.writeFile(path.join(outputDirectory, "schema.json"), snapshotBytes);
665
- const functions = yield* Effect.forEach(
666
- discovered.functions,
667
- (definition) => buildServerFunction(appDirectory, outputDirectory, conditions, definition),
668
- { concurrency: 4 },
518
+ return yield* compileManagedServer(
519
+ appDirectory,
520
+ outputDirectory,
521
+ yield* serverConditions(),
522
+ discovered,
669
523
  );
670
- const manifest = {
671
- formatVersion: 3,
672
- environment: {
673
- artifact: yield* artifactReference(serverEnvironmentPath, environmentBytes),
674
- fields: discovered.environment.fields,
675
- },
676
- schema: yield* artifactReference(schemaSnapshotPath, snapshotBytes),
677
- functions,
678
- } satisfies ServerBuildManifest;
679
- const encodedManifest = `${encodeCanonical(ServerBuildManifest, manifest)}\n`;
680
- const manifestPath = path.join(outputDirectory, "manifest.json");
681
- yield* fileSystem.writeFileString(manifestPath, encodedManifest);
682
- yield* Schema.decodeEffect(Schema.fromJsonString(ServerBuildManifest))(encodedManifest).pipe(
683
- Effect.mapError(() =>
684
- InvalidBuildOutput.make({ message: "Ignotum emitted an invalid server manifest." }),
685
- ),
686
- );
687
-
688
- return {
689
- environment: { bytes: environmentBytes, path: serverEnvironmentPath },
690
- functions: functions.length,
691
- manifestPath,
692
- } satisfies ServerBuildResult;
693
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,7 +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 } from "./deploy.js";
16
+ import { deploy as runDeploy, resumeVersion } from "./deploy.js";
17
+ import { VersionId } from "@ignotum/contracts/runtime/identity";
17
18
  import { resetDevDatabase } from "../dev-runtime/dev-database.js";
18
19
  import { dev as runDev } from "./dev.js";
19
20
  import { createApp } from "./new-app.js";
@@ -79,7 +80,7 @@ const codegen = Command.make(
79
80
  Effect.fn("codegen")(function* () {
80
81
  const path = yield* Path.Path;
81
82
  const terminal = yield* Terminal.Terminal;
82
- const result = yield* generate(path.resolve(".")).pipe(
83
+ const result = yield* generateClientRoutes(path.resolve(".")).pipe(
83
84
  Effect.mapError((error) =>
84
85
  CliError.UserError.make({ cause: error, userMessage: error.message }),
85
86
  ),
@@ -144,15 +145,37 @@ const dev = Command.make(
144
145
  const deploy = Command.make(
145
146
  "deploy",
146
147
  {
148
+ resume: Flag.string("resume").pipe(
149
+ Flag.withDescription("Finish activation of an already uploaded version."),
150
+ Flag.withSchema(VersionId),
151
+ Flag.optional,
152
+ ),
147
153
  app: Flag.string("app").pipe(
148
154
  Flag.withDescription("Create or link this app slug on the first deploy."),
149
155
  Flag.withSchema(AppSlug),
150
156
  Flag.optional,
151
157
  ),
152
158
  },
153
- Effect.fn("deploy")(function* ({ app }) {
159
+ Effect.fn("deploy")(function* ({ app, resume }) {
154
160
  const path = yield* Path.Path;
155
161
  const terminal = yield* Terminal.Terminal;
162
+ if (Option.isSome(resume)) {
163
+ const result = yield* resumeVersion(
164
+ path.resolve("."),
165
+ resume.value,
166
+ Option.getOrUndefined(app),
167
+ ).pipe(
168
+ // @effect-diagnostics-next-line strictEffectProvide:off This command owns the authenticated control client.
169
+ Effect.provide(ControlClient.layer),
170
+ Effect.mapError((error) =>
171
+ CliError.UserError.make({ cause: error, userMessage: error.message }),
172
+ ),
173
+ );
174
+ yield* terminal.display(
175
+ `Version ${result.versionNumber} is live.\n\nApp: ${result.appUrl}\nVersion ID: ${result.versionId}\n`,
176
+ );
177
+ return;
178
+ }
156
179
  const result = yield* runDeploy(path.resolve("."), Option.getOrUndefined(app)).pipe(
157
180
  // @effect-diagnostics-next-line strictEffectProvide:off The deploy subcommand owns this client layer for its full lifetime.
158
181
  Effect.provide(ControlClient.layer),
@@ -162,7 +185,7 @@ const deploy = Command.make(
162
185
  );
163
186
 
164
187
  yield* terminal.display(
165
- `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`,
166
189
  );
167
190
  }),
168
191
  ).pipe(Command.withDescription("Build and deploy the application to Ignotum Cloud."));