ignotum 0.0.0 → 0.0.2

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 (57) hide show
  1. package/README.md +161 -0
  2. package/dist/cli/bin.d.mts +1 -0
  3. package/dist/cli/bin.mjs +1696 -0
  4. package/dist/cli/bin.mjs.map +1 -0
  5. package/dist/runtime/api-BXXIZc_Q.js +119 -0
  6. package/dist/runtime/api-BXXIZc_Q.js.map +1 -0
  7. package/dist/runtime/api-Cpx57mk3.d.ts +47 -0
  8. package/dist/runtime/client/jsx-dev-runtime.d.ts +2 -0
  9. package/dist/runtime/client/jsx-dev-runtime.js +2 -0
  10. package/dist/runtime/client/jsx-runtime.d.ts +2 -0
  11. package/dist/runtime/client/jsx-runtime.js +2 -0
  12. package/dist/runtime/client.d.ts +29 -0
  13. package/dist/runtime/client.js +364 -0
  14. package/dist/runtime/client.js.map +1 -0
  15. package/dist/runtime/index-BgWROoyk.d.ts +249 -0
  16. package/dist/runtime/internal/api.d.ts +2 -0
  17. package/dist/runtime/internal/api.js +2 -0
  18. package/dist/runtime/internal/server.d.ts +6 -0
  19. package/dist/runtime/internal/server.js +7 -0
  20. package/dist/runtime/internal/server.js.map +1 -0
  21. package/dist/runtime/internal/types.d.ts +2 -0
  22. package/dist/runtime/internal/types.js +2 -0
  23. package/dist/runtime/result-B2W-z2wG.js +136 -0
  24. package/dist/runtime/result-B2W-z2wG.js.map +1 -0
  25. package/dist/runtime/result-C1ZdsM6Y.d.ts +106 -0
  26. package/dist/runtime/schema-CNEVLF7D.js +116 -0
  27. package/dist/runtime/schema-CNEVLF7D.js.map +1 -0
  28. package/dist/runtime/server.d.ts +9 -0
  29. package/dist/runtime/server.js +9 -0
  30. package/dist/runtime/server.js.map +1 -0
  31. package/package.json +81 -2
  32. package/src/cli/agent-files.ts +35 -0
  33. package/src/cli/bin.ts +5 -0
  34. package/src/cli/client-plugin.ts +66 -0
  35. package/src/cli/codegen.ts +203 -0
  36. package/src/cli/command.ts +155 -0
  37. package/src/cli/dev.ts +206 -0
  38. package/src/cli/new-project.ts +377 -0
  39. package/src/cli/package-manager.ts +55 -0
  40. package/src/client/errors.ts +83 -0
  41. package/src/client/hooks.ts +141 -0
  42. package/src/client/index.ts +90 -0
  43. package/src/client/jsx-dev-runtime.ts +2 -0
  44. package/src/client/jsx-runtime.ts +2 -0
  45. package/src/client/query.ts +17 -0
  46. package/src/client/sync.ts +487 -0
  47. package/src/dev-runtime/database.ts +374 -0
  48. package/src/dev-runtime/dev-database.ts +199 -0
  49. package/src/dev-runtime/functions.ts +293 -0
  50. package/src/dev-runtime/id.ts +11 -0
  51. package/src/dev-runtime/sync.ts +473 -0
  52. package/src/internal/api.ts +141 -0
  53. package/src/internal/http-paths.ts +5 -0
  54. package/src/internal/server.ts +3 -0
  55. package/src/internal/types.ts +2 -0
  56. package/src/raw.d.ts +4 -0
  57. package/src/server/index.ts +8 -0
@@ -0,0 +1,293 @@
1
+ import {
2
+ FunctionUnavailable,
3
+ InvalidArguments,
4
+ UnknownFunction,
5
+ WrongFunctionKind,
6
+ type FunctionKind,
7
+ type FunctionResolutionError,
8
+ } from "@ignotum/contracts/runtime/functions";
9
+ import {
10
+ getFunctionSchema,
11
+ type FunctionSchemaCarrier,
12
+ type RuntimeSchemaDefinition,
13
+ } from "@ignotum/contracts/runtime/schema";
14
+ import { ErrorValueSchema, type ErrorValue } from "@ignotum/contracts/runtime/result";
15
+ import {
16
+ FunctionAddress,
17
+ apiFunctionParts,
18
+ datePathsOf,
19
+ datePathsOfObject,
20
+ type TransportValue,
21
+ type WireResult,
22
+ } from "@ignotum/contracts/runtime/sync";
23
+ import { FunctionRuntime } from "@ignotum/runtime/functions";
24
+ import { Cause, Context, Effect, Layer, Path, Predicate, Schema, Semaphore } from "effect";
25
+ import { nanoid } from "nanoid";
26
+ import type { ViteDevServer } from "vite";
27
+ import { normalizePath } from "vite";
28
+
29
+ import { LocalDatabase, type LocalMutationContext, type LocalQueryContext } from "./database.js";
30
+
31
+ export { FunctionUnavailable, InvalidArguments, UnknownFunction, WrongFunctionKind };
32
+ export type { FunctionKind };
33
+
34
+ type RuntimeFields = Readonly<Record<string, Schema.ConstraintDecoder<unknown>>>;
35
+
36
+ declare const FunctionArgumentsTypeId: unique symbol;
37
+ export interface FunctionArguments {
38
+ readonly [FunctionArgumentsTypeId]?: never;
39
+ }
40
+
41
+ export type RuntimeHandlerContext = LocalMutationContext | LocalQueryContext;
42
+
43
+ export interface RuntimeFunctionDefinition {
44
+ readonly _tag: FunctionKind;
45
+ readonly args?: RuntimeFields;
46
+ readonly returns?: Schema.Codec<unknown, unknown>;
47
+ readonly errors?: Schema.Codec<unknown, unknown>;
48
+ readonly handler: (
49
+ context: RuntimeHandlerContext,
50
+ args: FunctionArguments,
51
+ ) => Generator<Effect.Effect<unknown, ErrorValue, never>, TransportValue | void, never>;
52
+ }
53
+
54
+ export interface ResolvedFunction {
55
+ readonly definition: RuntimeFunctionDefinition;
56
+ readonly args: FunctionArguments;
57
+ readonly schema: RuntimeSchemaDefinition;
58
+ }
59
+
60
+ interface RuntimeFunctionCandidate extends FunctionSchemaCarrier {
61
+ readonly _tag?: unknown;
62
+ readonly args?: unknown;
63
+ readonly returns?: unknown;
64
+ readonly errors?: unknown;
65
+ readonly handler?: unknown;
66
+ }
67
+
68
+ const inspectRuntimeFunctionDefinition = (
69
+ value: RuntimeFunctionCandidate,
70
+ ):
71
+ | { readonly definition: RuntimeFunctionDefinition; readonly schema: RuntimeSchemaDefinition }
72
+ | undefined => {
73
+ const schema = getFunctionSchema(value);
74
+ if (
75
+ schema === undefined ||
76
+ !Predicate.hasProperty(value, "_tag") ||
77
+ (value._tag !== "Mutation" && value._tag !== "Query") ||
78
+ (Predicate.hasProperty(value, "args") &&
79
+ value.args !== undefined &&
80
+ !Predicate.isObject(value.args)) ||
81
+ (Predicate.hasProperty(value, "returns") &&
82
+ value.returns !== undefined &&
83
+ !Schema.isSchema(value.returns)) ||
84
+ (Predicate.hasProperty(value, "errors") &&
85
+ value.errors !== undefined &&
86
+ !Schema.isSchema(value.errors)) ||
87
+ !Predicate.hasProperty(value, "handler") ||
88
+ !Predicate.isFunction(value.handler)
89
+ ) {
90
+ return undefined;
91
+ }
92
+
93
+ if (
94
+ Predicate.hasProperty(value, "args") &&
95
+ Predicate.isObject(value.args) &&
96
+ !Object.values(value.args).every(Schema.isSchema)
97
+ ) {
98
+ return undefined;
99
+ }
100
+
101
+ // SAFETY: every RuntimeFunctionDefinition property and argument Schema was
102
+ // checked above. Predicate.hasProperty does not retain the literal tag type.
103
+ return { definition: value as RuntimeFunctionDefinition, schema };
104
+ };
105
+
106
+ export class FunctionRegistry extends Context.Service<
107
+ FunctionRegistry,
108
+ {
109
+ readonly resolve: (
110
+ functionAddress: FunctionAddress,
111
+ kind: FunctionKind,
112
+ args: Schema.Json,
113
+ ) => Effect.Effect<ResolvedFunction, FunctionResolutionError>;
114
+ }
115
+ >()("ignotum/dev-runtime/functions/FunctionRegistry") {
116
+ static layer(server: ViteDevServer, projectDirectory: string) {
117
+ return Layer.effect(
118
+ FunctionRegistry,
119
+ Effect.gen(function* () {
120
+ const path = yield* Path.Path;
121
+ return FunctionRegistry.of({
122
+ resolve: Effect.fn("FunctionRegistry.resolve")(function* (functionAddress, kind, args) {
123
+ const { functionName, moduleName } = apiFunctionParts(functionAddress);
124
+ const modulePath = normalizePath(
125
+ path.join(projectDirectory, "server", `${moduleName}.ts`),
126
+ );
127
+ const loadedModule = yield* Effect.tryPromise({
128
+ try: () => server.ssrLoadModule(modulePath),
129
+ catch: (cause) =>
130
+ FunctionUnavailable.make({
131
+ cause,
132
+ function: functionAddress,
133
+ message: `Could not load ${functionAddress}.`,
134
+ }),
135
+ });
136
+ const candidate = loadedModule[functionName];
137
+
138
+ const inspected = Predicate.isObject(candidate)
139
+ ? inspectRuntimeFunctionDefinition(candidate)
140
+ : undefined;
141
+ if (inspected === undefined) {
142
+ return yield* UnknownFunction.make({
143
+ function: functionAddress,
144
+ message: `Unknown server function ${functionAddress}.`,
145
+ });
146
+ }
147
+ const definition = inspected.definition;
148
+
149
+ if (definition._tag !== kind) {
150
+ return yield* WrongFunctionKind.make({
151
+ actual: definition._tag,
152
+ expected: kind,
153
+ function: functionAddress,
154
+ message: `${functionAddress} is a ${definition._tag.toLowerCase()}, not a ${kind.toLowerCase()}.`,
155
+ });
156
+ }
157
+
158
+ const decodedArgs = yield* Schema.decodeEffect(
159
+ Schema.toCodecJson(Schema.Struct(definition.args ?? {})),
160
+ )(args).pipe(
161
+ Effect.mapError(() =>
162
+ InvalidArguments.make({
163
+ function: functionAddress,
164
+ message: `Invalid arguments for ${functionAddress}.`,
165
+ }),
166
+ ),
167
+ );
168
+
169
+ return { args: decodedArgs, definition, schema: inspected.schema };
170
+ }),
171
+ });
172
+ }),
173
+ );
174
+ }
175
+ }
176
+
177
+ class MutationApplicationFailure extends Schema.TaggedError<MutationApplicationFailure>()(
178
+ "MutationApplicationFailure",
179
+ { error: Schema.Json },
180
+ ) {}
181
+
182
+ export class FunctionExecutor extends Context.Service<
183
+ FunctionExecutor,
184
+ {
185
+ readonly execute: (
186
+ functionAddress: FunctionAddress,
187
+ kind: FunctionKind,
188
+ resolved: ResolvedFunction,
189
+ ) => Effect.Effect<WireResult>;
190
+ }
191
+ >()("ignotum/dev-runtime/functions/FunctionExecutor") {
192
+ static readonly layer = Layer.effect(
193
+ FunctionExecutor,
194
+ Effect.gen(function* () {
195
+ const database = yield* LocalDatabase;
196
+ const mutationSemaphore = yield* Semaphore.make(1);
197
+
198
+ const encodeSuccess = (
199
+ definition: RuntimeFunctionDefinition,
200
+ value: TransportValue | void,
201
+ ) =>
202
+ definition.returns === undefined
203
+ ? Schema.encodeUnknownEffect(Schema.Undefined)(value).pipe(
204
+ Effect.orDie,
205
+ Effect.as<WireResult>({ type: "Success" }),
206
+ )
207
+ : Schema.encodeEffect(Schema.toCodecJson(definition.returns))(value).pipe(
208
+ Effect.orDie,
209
+ Effect.map((encoded): WireResult => {
210
+ const result: WireResult = { type: "Success", value: encoded };
211
+ const dates = datePathsOf(value);
212
+ return dates.length === 0 ? result : { ...result, dates };
213
+ }),
214
+ );
215
+
216
+ const encodeFailure = (definition: RuntimeFunctionDefinition, error: ErrorValue) =>
217
+ Schema.encodeEffect(Schema.toCodecJson(definition.errors ?? ErrorValueSchema))(error).pipe(
218
+ Effect.orDie,
219
+ Effect.map((encoded): WireResult => {
220
+ const result: WireResult = { type: "Failure", error: encoded };
221
+ const dates = datePathsOfObject(error);
222
+ return dates.length === 0 ? result : { ...result, dates };
223
+ }),
224
+ );
225
+
226
+ return FunctionExecutor.of({
227
+ execute: Effect.fn("FunctionExecutor.execute")(function* (functionAddress, kind, resolved) {
228
+ const invoke = (context: RuntimeHandlerContext) =>
229
+ Effect.gen(() => resolved.definition.handler(context, resolved.args)).pipe(
230
+ Effect.matchEffect({
231
+ onFailure: (error) => encodeFailure(resolved.definition, error),
232
+ onSuccess: (value) => encodeSuccess(resolved.definition, value),
233
+ }),
234
+ );
235
+
236
+ const execution =
237
+ kind === "Query"
238
+ ? database.queryTransaction(resolved.schema, invoke)
239
+ : mutationSemaphore.withPermits(1)(
240
+ database
241
+ .mutationTransaction(resolved.schema, (context) =>
242
+ invoke(context).pipe(
243
+ Effect.flatMap((result) =>
244
+ result.type === "Failure"
245
+ ? Effect.fail(MutationApplicationFailure.make({ error: result.error }))
246
+ : Effect.succeed(result),
247
+ ),
248
+ ),
249
+ )
250
+ .pipe(
251
+ Effect.catchTags({
252
+ MutationApplicationFailure: ({ error }) =>
253
+ Effect.succeed({ type: "Failure" as const, error }),
254
+ }),
255
+ ),
256
+ );
257
+
258
+ return yield* execution.pipe(
259
+ Effect.orDie,
260
+ Effect.catchCauseIf(
261
+ (cause) => !Cause.hasInterruptsOnly(cause),
262
+ (cause) => {
263
+ const requestId = nanoid();
264
+ return Effect.logError(`${functionAddress} failed during execution.`).pipe(
265
+ Effect.annotateLogs({ cause, function: functionAddress, requestId }),
266
+ Effect.as({
267
+ type: "Failure" as const,
268
+ error: { _tag: "InternalServerError", requestId },
269
+ }),
270
+ );
271
+ },
272
+ ),
273
+ );
274
+ }),
275
+ });
276
+ }),
277
+ );
278
+ }
279
+
280
+ /** Adapts the Vite/SQLite development implementation to the shared runtime contract. */
281
+ export const functionRuntimeLayer = Layer.effect(
282
+ FunctionRuntime,
283
+ Effect.gen(function* () {
284
+ const registry = yield* FunctionRegistry;
285
+ const executor = yield* FunctionExecutor;
286
+ return FunctionRuntime.of({
287
+ prepare: Effect.fn("DevFunctionRuntime.prepare")(function* (functionAddress, kind, args) {
288
+ const resolved = yield* registry.resolve(functionAddress, kind, args);
289
+ return { execute: executor.execute(functionAddress, kind, resolved) };
290
+ }),
291
+ });
292
+ }),
293
+ );
@@ -0,0 +1,11 @@
1
+ import { GeneratedId, IdAlphabet, IdLength } from "@ignotum/contracts/runtime/id";
2
+ import { IdGenerator } from "@ignotum/runtime/id";
3
+ import { Effect, Layer } from "effect";
4
+ import { customAlphabet } from "nanoid";
5
+
6
+ export const idGeneratorLayer = Layer.sync(IdGenerator, () => {
7
+ const nanoid = customAlphabet(IdAlphabet, IdLength);
8
+ return IdGenerator.of({
9
+ generate: Effect.sync(() => GeneratedId.make(nanoid())),
10
+ });
11
+ });