ignotum 0.0.6 → 0.0.8

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 (54) hide show
  1. package/README.md +85 -101
  2. package/dist/cli/bin.mjs +1487 -168
  3. package/dist/cli/bin.mjs.map +1 -1
  4. package/dist/runtime/{api-DtX8qPrq.js → api-BTeMI5tx.js} +27 -3
  5. package/dist/runtime/api-BTeMI5tx.js.map +1 -0
  6. package/dist/runtime/{api-D2bsAz4T.d.ts → api-D9lV-5av.d.ts} +7 -10
  7. package/dist/runtime/client.d.ts +27 -3
  8. package/dist/runtime/client.js +468 -68
  9. package/dist/runtime/client.js.map +1 -1
  10. package/dist/runtime/{descriptor-t6BOEGw9-BTHR-ZMv.js → descriptor-C5VA9qRl-C338iC6l.js} +63 -5
  11. package/dist/runtime/descriptor-C5VA9qRl-C338iC6l.js.map +1 -0
  12. package/dist/runtime/file-BXf63ulU.js +166 -0
  13. package/dist/runtime/file-BXf63ulU.js.map +1 -0
  14. package/dist/runtime/{result-BgcHn25t.d.ts → id-Btwac71X-DGj0DQuu.d.ts} +56 -4
  15. package/dist/runtime/{index-Dl_VQGmA.d.ts → index-CF04_Dps.d.ts} +96 -86
  16. package/dist/runtime/internal/api.d.ts +2 -2
  17. package/dist/runtime/internal/api.js +1 -1
  18. package/dist/runtime/internal/host.d.ts +25 -8
  19. package/dist/runtime/internal/host.js +209 -15
  20. package/dist/runtime/internal/host.js.map +1 -1
  21. package/dist/runtime/internal/server.d.ts +1 -1
  22. package/dist/runtime/internal/server.js +1 -1
  23. package/dist/runtime/internal/types.d.ts +1 -1
  24. package/dist/runtime/internal/types.js +1 -1
  25. package/dist/runtime/pagination-BNFhAjns.d.ts +1 -0
  26. package/dist/runtime/pagination-DrOowBve-Bnipd34u.d.ts +84 -0
  27. package/dist/runtime/{schema-B-jMZEbs.js → schema-DJfwfq87.js} +108 -30
  28. package/dist/runtime/schema-DJfwfq87.js.map +1 -0
  29. package/dist/runtime/server.d.ts +3 -3
  30. package/dist/runtime/server.js +2 -2
  31. package/dist/runtime/server.js.map +1 -1
  32. package/dist/runtime/sync-mIXn9eKv.d.ts +8 -0
  33. package/package.json +2 -2
  34. package/src/cli/agent-files.ts +56 -13
  35. package/src/cli/app-configuration.ts +4 -1
  36. package/src/cli/build/server.ts +83 -6
  37. package/src/cli/new-app.ts +15 -0
  38. package/src/client/files.ts +168 -0
  39. package/src/client/hooks.ts +166 -17
  40. package/src/client/index.ts +9 -1
  41. package/src/client/sync.ts +137 -21
  42. package/src/dev-runtime/database.ts +621 -78
  43. package/src/dev-runtime/files.ts +338 -0
  44. package/src/dev-runtime/functions.ts +14 -6
  45. package/src/dev-runtime/migrations.ts +44 -0
  46. package/src/dev-runtime/sync.ts +123 -5
  47. package/src/internal/api.ts +16 -1
  48. package/src/server/index.ts +8 -1
  49. package/dist/runtime/api-DtX8qPrq.js.map +0 -1
  50. package/dist/runtime/descriptor-t6BOEGw9-BTHR-ZMv.js.map +0 -1
  51. package/dist/runtime/id-Btwac71X-B9OeBzvq.d.ts +0 -9
  52. package/dist/runtime/id-D570vudg.js +0 -26
  53. package/dist/runtime/id-D570vudg.js.map +0 -1
  54. package/dist/runtime/schema-B-jMZEbs.js.map +0 -1
@@ -1,12 +1,127 @@
1
- import { a as InvocationId, c as SubscriptionId, i as IdAlphabet, l as TableId, n as DeploymentId, r as GeneratedId, s as RuntimeRequestNonce, t as AppId } from "./id-D570vudg.js";
2
- import { g as encodeTransportObject, h as decodeTransportValue, l as failureFromWire, s as Result$1, t as ValueDescriptor, u as pending } from "./descriptor-t6BOEGw9-BTHR-ZMv.js";
3
- import { a as FunctionAddress, c as ServerMessageJson, d as WireSuccess, f as AppStateRevision, i as DeploymentChangedCloseCode, l as WireFailure, m as InvocationKey, n as functionPathOf, o as Operation, p as DeploymentGeneration, r as ClientMessageJson, s as ProtocolErrorCode, u as WireResult } from "./api-DtX8qPrq.js";
4
- import { Array, Cause, Context, Deferred, Duration, Effect, ErrorReporter, Fiber, HashMap, Layer, ManagedRuntime, Predicate, Schedule, Schema, String } from "effect";
5
- import { useCallback, useContext, useDebugValue, useDebugValue as useDebugValue$1, useEffect, useEffect as useEffect$1, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useReducer, useRef, useState, useState as useState$1 } from "preact/hooks";
1
+ import { C as RuntimeRequestNonce, T as TableId, _ as AppId, a as RuntimeFileMetadata, b as IdAlphabet, c as fileFormats, d as fileLimits, f as fileMimeTypes, h as makeFileValue, l as fileGrantUrlOf, m as isFileValue, o as RuntimeFileOccurrence, p as grantFileValue, r as FileId, u as fileIdOf, v as DeploymentId, w as SubscriptionId, x as InvocationId, y as GeneratedId } from "./file-BXf63ulU.js";
2
+ import { b as decodeTransportValue, c as upgrade, d as Result$1, h as pending, m as inspectResult, o as PaginationPageSize, p as failureFromWire, s as initial, t as ValueDescriptor, x as encodeTransportObject } from "./descriptor-C5VA9qRl-C338iC6l.js";
3
+ import { a as FunctionAddress, c as ServerMessageJson, d as WireSuccess, f as AppStateRevision, i as DeploymentChangedCloseCode, l as WireFailure, m as InvocationKey, n as functionPathOf, o as Operation, p as DeploymentGeneration, r as ClientMessageJson, s as ProtocolErrorCode, u as WireResult } from "./api-BTeMI5tx.js";
4
+ import { Array as Array$1, Cause, Context, Deferred, Duration, Effect, ErrorReporter, Fiber, HashMap, Layer, ManagedRuntime, Predicate, Schedule, Schema, String } from "effect";
5
+ import { customAlphabet } from "nanoid";
6
+ import { useCallback, useCallback as useCallback$1, useContext, useDebugValue, useDebugValue as useDebugValue$1, useEffect, useEffect as useEffect$1, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useReducer, useRef, useState, useState as useState$1 } from "preact/hooks";
6
7
  import * as Socket from "effect/unstable/socket/Socket";
7
8
  import * as BrowserCrypto from "@effect/platform-browser/BrowserCrypto";
8
- import { customAlphabet } from "nanoid";
9
9
  import { Component, Fragment, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray } from "preact";
10
+ //#region ../shared/dist/id.js
11
+ const decodeGenerated = (definition, payload) => Schema.decodeSync(definition)(definition.idPrefix === void 0 ? payload : `${definition.idPrefix}_${payload}`);
12
+ var IdGenerator = class IdGenerator extends Context.Service()("@ignotum/shared/id/IdGenerator") {
13
+ static layer = Layer.sync(IdGenerator, () => {
14
+ const generatePayload = customAlphabet(IdAlphabet, 24);
15
+ return IdGenerator.of({ generate: (definition) => Effect.sync(() => decodeGenerated(definition, generatePayload())) });
16
+ });
17
+ static deterministic = (startAt = 1) => Layer.sync(IdGenerator, () => {
18
+ const counters = /* @__PURE__ */ new Map();
19
+ return IdGenerator.of({ generate: (definition) => Effect.sync(() => {
20
+ const counter = (counters.get(definition.idPrefix) ?? startAt - 1) + 1;
21
+ counters.set(definition.idPrefix, counter);
22
+ return decodeGenerated(definition, counter.toString(36).padStart(24, "0"));
23
+ }) });
24
+ });
25
+ };
26
+ //#endregion
27
+ //#region src/client/files.ts
28
+ const Files = { url: (file) => {
29
+ const url = fileGrantUrlOf(file);
30
+ if (url === void 0) throw new Error("This FileValue has no active query grant.");
31
+ return url;
32
+ } };
33
+ const formatByMime = {};
34
+ for (const format of fileFormats) formatByMime[fileMimeTypes[format]] = format;
35
+ const collectNativeFiles = (value, path, occurrences) => {
36
+ if (value instanceof File) {
37
+ occurrences.push({
38
+ path,
39
+ value
40
+ });
41
+ return;
42
+ }
43
+ if (isFileValue(value) || Predicate.isDate(value)) return;
44
+ if (globalThis.Array.isArray(value)) {
45
+ for (const [index, child] of value.entries()) collectNativeFiles(child, [...path, index], occurrences);
46
+ return;
47
+ }
48
+ if (!Predicate.isObject(value)) return;
49
+ for (const [key, child] of Object.entries(value)) collectNativeFiles(child, [...path, key], occurrences);
50
+ };
51
+ const replaceNativeFiles = (value, replacements) => {
52
+ if (value instanceof File) return replacements.get(value);
53
+ if (isFileValue(value) || Predicate.isDate(value)) return value;
54
+ if (globalThis.Array.isArray(value)) return value.map((child) => replaceNativeFiles(child, replacements));
55
+ if (!Predicate.isObject(value)) return value;
56
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, replaceNativeFiles(child, replacements)]));
57
+ };
58
+ const prepareMutationArguments = Effect.fn("SyncClient.prepareMutationArguments")(function* (input) {
59
+ const ids = yield* IdGenerator;
60
+ const native = [];
61
+ collectNativeFiles(input, [], native);
62
+ const unique = new Set(native.map(({ value }) => value));
63
+ if (unique.size > fileLimits.filesPerMutation) throw new Error(`A mutation may upload at most ${fileLimits.filesPerMutation} files.`);
64
+ if ([...unique].reduce((total, file) => total + file.size, 0) > fileLimits.mutationBytes) throw new Error(`Mutation uploads may total at most ${fileLimits.mutationBytes} bytes.`);
65
+ const replacements = /* @__PURE__ */ new Map();
66
+ const nativeFiles = /* @__PURE__ */ new Map();
67
+ for (const file of unique) {
68
+ const format = formatByMime[file.type.toLowerCase()];
69
+ if (format === void 0) throw new Error(`File '${file.name}' has unsupported media type '${file.type || "unknown"}'.`);
70
+ if (file.size === 0 || file.size > fileLimits.fileBytes) throw new Error(`File '${file.name}' must be between 1 and ${fileLimits.fileBytes} bytes.`);
71
+ if (new TextEncoder().encode(file.name).byteLength > fileLimits.filenameBytes) throw new Error(`File '${file.name}' has a filename that is too long.`);
72
+ const id = yield* ids.generate(FileId);
73
+ replacements.set(file, makeFileValue(id, {
74
+ format,
75
+ name: file.name,
76
+ size: file.size
77
+ }));
78
+ nativeFiles.set(id, file);
79
+ }
80
+ return {
81
+ value: replaceNativeFiles(input, replacements),
82
+ nativeFiles,
83
+ occurrences: native.map(({ path, value: file }) => {
84
+ const replacement = replacements.get(file);
85
+ if (replacement === void 0) throw new Error("A native file replacement is missing.");
86
+ return {
87
+ path,
88
+ file: {
89
+ id: fileIdOf(replacement),
90
+ format: replacement.format,
91
+ name: replacement.name,
92
+ size: replacement.size
93
+ }
94
+ };
95
+ })
96
+ };
97
+ });
98
+ const pathKey = (path) => JSON.stringify(path);
99
+ const applyFileGrants = (value, grants) => {
100
+ const byPath = new Map(grants.map((grant) => [pathKey(grant.path), grant]));
101
+ const visit = (current, path) => {
102
+ const grant = byPath.get(pathKey(path));
103
+ if (grant !== void 0) {
104
+ if (!isFileValue(current) || fileIdOf(current) !== grant.file.id) throw new Error("A query file grant does not match its result value.");
105
+ return grantFileValue(current, grant.url);
106
+ }
107
+ if (globalThis.Array.isArray(current)) return current.map((child, index) => visit(child, [...path, index]));
108
+ if (!Predicate.isObject(current) || Predicate.isDate(current) || isFileValue(current)) return current;
109
+ return Object.fromEntries(Object.entries(current).map(([key, child]) => [key, visit(child, [...path, key])]));
110
+ };
111
+ return visit(value, []);
112
+ };
113
+ //#endregion
114
+ //#region ../contracts/dist/json.js
115
+ const encodeScalar = (value) => Schema.decodeSync(Schema.String)(JSON.stringify(value));
116
+ const encodeCanonicalJson = (value) => {
117
+ if (value === null || Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) return encodeScalar(value);
118
+ if (Predicate.isObject(value)) return `{${Array$1.map(Array$1.sort(String.Order)(Object.keys(value)), (key) => {
119
+ const field = Schema.decodeUnknownSync(Schema.Json)(value[key]);
120
+ return `${encodeScalar(key)}:${encodeCanonicalJson(field)}`;
121
+ }).join(",")}}`;
122
+ return `[${Schema.decodeUnknownSync(Schema.Array(Schema.Json))(value).map(encodeCanonicalJson).join(",")}]`;
123
+ };
124
+ //#endregion
10
125
  //#region src/client/errors.ts
11
126
  /** @effect-diagnostics globalConsole:skip-file */
12
127
  var ConnectionUnavailable = class extends Schema.TaggedError()("ConnectionUnavailable", {
@@ -55,16 +170,14 @@ const skip = Object.freeze(new QuerySkipToken());
55
170
  const Query = { skip };
56
171
  const isQuerySkip = (value) => value === skip;
57
172
  //#endregion
58
- //#region ../contracts/dist/json.js
59
- const encodeScalar = (value) => Schema.decodeSync(Schema.String)(JSON.stringify(value));
60
- const encodeCanonicalJson = (value) => {
61
- if (value === null || Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) return encodeScalar(value);
62
- if (Predicate.isObject(value)) return `{${Array.map(Array.sort(String.Order)(Object.keys(value)), (key) => {
63
- const field = Schema.decodeUnknownSync(Schema.Json)(value[key]);
64
- return `${encodeScalar(key)}:${encodeCanonicalJson(field)}`;
65
- }).join(",")}}`;
66
- return `[${Schema.decodeUnknownSync(Schema.Array(Schema.Json))(value).map(encodeCanonicalJson).join(",")}]`;
67
- };
173
+ //#region ../contracts/dist/runtime/index.js
174
+ const IndexIdentity = Schema.String.pipe(Schema.brand("ignotum/runtime/IndexIdentity"));
175
+ const EncodedIndexKey = Schema.String.check(Schema.isPattern(/^(?:[0-9a-f]{2})*$/)).pipe(Schema.brand("ignotum/runtime/EncodedIndexKey"));
176
+ Schema.Union([
177
+ Schema.Boolean,
178
+ Schema.Finite,
179
+ Schema.String
180
+ ]);
68
181
  Schema.String.pipe(Schema.brand("ignotum/hosted/QueryKey"));
69
182
  const TableDependency = Schema.Struct({
70
183
  type: Schema.Literal("Table"),
@@ -75,20 +188,71 @@ const DocumentDependency = Schema.Struct({
75
188
  tableId: TableId,
76
189
  id: GeneratedId
77
190
  });
78
- const DependencyKey = Schema.Union([TableDependency, DocumentDependency]);
79
- const DependencySet = Schema.Array(DependencyKey);
80
- const InvalidationSet = Schema.Array(DependencyKey);
191
+ const IndexRangeDependency = Schema.Struct({
192
+ type: Schema.Literal("IndexRange"),
193
+ tableId: TableId,
194
+ index: IndexIdentity,
195
+ lower: Schema.optional(EncodedIndexKey),
196
+ upper: Schema.optional(EncodedIndexKey)
197
+ });
198
+ const IndexPointInvalidation = Schema.Struct({
199
+ type: Schema.Literal("IndexPoint"),
200
+ tableId: TableId,
201
+ index: IndexIdentity,
202
+ key: EncodedIndexKey
203
+ });
204
+ const ReadDependency = Schema.Union([
205
+ TableDependency,
206
+ DocumentDependency,
207
+ IndexRangeDependency
208
+ ]);
209
+ const WriteInvalidation = Schema.Union([
210
+ TableDependency,
211
+ DocumentDependency,
212
+ IndexPointInvalidation
213
+ ]);
214
+ Schema.Union([ReadDependency, IndexPointInvalidation]);
215
+ const DependencySet = Schema.Array(ReadDependency);
216
+ const InvalidationSet = Schema.Array(WriteInvalidation);
81
217
  const RuntimeQueryResult = Schema.Struct({
82
218
  type: Schema.Literal("Query"),
83
219
  result: WireResult,
84
220
  dependencies: DependencySet,
85
- observedRevision: AppStateRevision
221
+ observedRevision: AppStateRevision,
222
+ files: Schema.optional(Schema.Array(RuntimeFileOccurrence))
223
+ });
224
+ const FileReferenceAdded = Schema.Struct({
225
+ type: Schema.Literal("AddReference"),
226
+ file: RuntimeFileMetadata,
227
+ tableId: TableId,
228
+ documentId: GeneratedId
229
+ });
230
+ const FileReferenceRemoved = Schema.Struct({
231
+ type: Schema.Literal("RemoveReference"),
232
+ fileId: FileId,
233
+ tableId: TableId,
234
+ documentId: GeneratedId
235
+ });
236
+ const FileReferenceEffect = Schema.Union([FileReferenceAdded, FileReferenceRemoved]);
237
+ const FileEffectBatch = Schema.Struct({
238
+ id: InvocationId,
239
+ committedRevision: AppStateRevision,
240
+ effects: Schema.Array(FileReferenceEffect)
86
241
  });
242
+ Schema.Struct({ appId: AppId });
243
+ Schema.Struct({ batches: Schema.Array(FileEffectBatch) });
244
+ Schema.Struct({
245
+ appId: AppId,
246
+ invocationId: InvocationId,
247
+ acknowledgedAt: Schema.Int
248
+ });
249
+ Schema.Struct({ completed: Schema.Literal(true) });
87
250
  const RuntimeMutationSuccess = Schema.Struct({
88
251
  type: Schema.Literal("Mutation"),
89
252
  result: WireSuccess,
90
253
  invalidations: InvalidationSet,
91
- committedRevision: AppStateRevision
254
+ committedRevision: AppStateRevision,
255
+ fileEffects: Schema.optional(FileEffectBatch)
92
256
  });
93
257
  const RuntimeMutationFailure = Schema.Struct({
94
258
  type: Schema.Literal("Mutation"),
@@ -111,7 +275,8 @@ const QueryInvocation = Schema.Struct({
111
275
  const MutationInvocation = Schema.Struct({
112
276
  ...InvocationBase,
113
277
  type: Schema.Literal("Mutation"),
114
- invocationId: InvocationId
278
+ invocationId: InvocationId,
279
+ files: Schema.optional(Schema.Array(RuntimeFileOccurrence))
115
280
  });
116
281
  Schema.Union([QueryInvocation, MutationInvocation]);
117
282
  Schema.Struct({
@@ -150,23 +315,6 @@ Schema.Union([
150
315
  RuntimeInvocationUnavailable
151
316
  ]);
152
317
  //#endregion
153
- //#region ../shared/dist/id.js
154
- const decodeGenerated = (definition, payload) => Schema.decodeSync(definition)(definition.idPrefix === void 0 ? payload : `${definition.idPrefix}_${payload}`);
155
- var IdGenerator = class IdGenerator extends Context.Service()("@ignotum/shared/id/IdGenerator") {
156
- static layer = Layer.sync(IdGenerator, () => {
157
- const generatePayload = customAlphabet(IdAlphabet, 24);
158
- return IdGenerator.of({ generate: (definition) => Effect.sync(() => decodeGenerated(definition, generatePayload())) });
159
- });
160
- static deterministic = (startAt = 1) => Layer.sync(IdGenerator, () => {
161
- const counters = /* @__PURE__ */ new Map();
162
- return IdGenerator.of({ generate: (definition) => Effect.sync(() => {
163
- const counter = (counters.get(definition.idPrefix) ?? startAt - 1) + 1;
164
- counters.set(definition.idPrefix, counter);
165
- return decodeGenerated(definition, counter.toString(36).padStart(24, "0"));
166
- }) });
167
- });
168
- };
169
- //#endregion
170
318
  //#region ../contracts/dist/runtime/functions.js
171
319
  const FunctionKind = Schema.Literals(["Mutation", "Query"]);
172
320
  Schema.TaggedError()("UnknownFunction", {
@@ -221,47 +369,89 @@ const ClientRoute = Schema.Struct({
221
369
  pathname: ClientPath,
222
370
  artifact: ArtifactReference
223
371
  });
224
- Schema.Struct({
372
+ const DeploymentInventoryV1 = Schema.Struct({
225
373
  formatVersion: Schema.Literal(1),
226
374
  files: Schema.Array(ArtifactFile)
227
375
  });
228
- Schema.Struct({
376
+ initial(DeploymentInventoryV1);
377
+ const ClientManifestV1 = Schema.Struct({
229
378
  formatVersion: Schema.Literal(1),
230
379
  shell: ArtifactReference,
231
380
  routes: Schema.Array(ClientRoute)
232
381
  });
382
+ initial(ClientManifestV1);
233
383
  const ClientRoutingRoute = Schema.Struct({
234
384
  pathname: ClientPath,
235
385
  artifact: ArtifactPath
236
386
  });
237
- Schema.Struct({
387
+ const ClientRoutingV1 = Schema.Struct({
238
388
  formatVersion: Schema.Literal(1),
239
389
  shell: ArtifactPath,
240
390
  routes: Schema.Array(ClientRoutingRoute)
241
391
  });
392
+ initial(ClientRoutingV1);
242
393
  const SchemaSnapshotField = Schema.Struct({
243
394
  name: Schema.String,
244
395
  value: ValueDescriptor
245
396
  });
246
- const SchemaSnapshotTable = Schema.Struct({
397
+ const SchemaSnapshotIndex = Schema.Struct({
398
+ name: Schema.String,
399
+ fields: Schema.Array(Schema.String)
400
+ });
401
+ const SchemaSnapshotTableV1 = Schema.Struct({
247
402
  name: Schema.String,
248
403
  fields: Schema.Array(SchemaSnapshotField)
249
404
  });
250
- Schema.Struct({
405
+ const SchemaSnapshotV1 = Schema.Struct({
251
406
  formatVersion: Schema.Literal(1),
407
+ tables: Schema.Array(SchemaSnapshotTableV1)
408
+ });
409
+ const SchemaSnapshotTable = Schema.Struct({
410
+ name: Schema.String,
411
+ fields: Schema.Array(SchemaSnapshotField),
412
+ indexes: Schema.Array(SchemaSnapshotIndex)
413
+ });
414
+ const SchemaSnapshotV2 = Schema.Struct({
415
+ formatVersion: Schema.Literal(2),
252
416
  tables: Schema.Array(SchemaSnapshotTable)
253
417
  });
418
+ upgrade(initial(SchemaSnapshotV1), SchemaSnapshotV2, (snapshot) => ({
419
+ formatVersion: 2,
420
+ tables: snapshot.tables.map((table) => ({
421
+ ...table,
422
+ indexes: []
423
+ }))
424
+ }));
425
+ const ServerFunctionArtifactV1 = Schema.Struct({
426
+ address: FunctionAddress,
427
+ kind: FunctionKind,
428
+ bundle: ArtifactReference,
429
+ sourceMap: ArtifactReference
430
+ });
254
431
  const ServerFunctionArtifact = Schema.Struct({
255
432
  address: FunctionAddress,
256
433
  kind: FunctionKind,
434
+ args: Schema.optional(ValueDescriptor),
435
+ returns: Schema.optional(ValueDescriptor),
436
+ errors: Schema.optional(ValueDescriptor),
257
437
  bundle: ArtifactReference,
258
438
  sourceMap: ArtifactReference
259
439
  });
260
- Schema.Struct({
440
+ const ServerBuildManifestV1 = Schema.Struct({
261
441
  formatVersion: Schema.Literal(1),
262
442
  schema: ArtifactReference,
443
+ functions: Schema.Array(ServerFunctionArtifactV1)
444
+ });
445
+ const ServerBuildManifestV2 = Schema.Struct({
446
+ formatVersion: Schema.Literal(2),
447
+ schema: ArtifactReference,
263
448
  functions: Schema.Array(ServerFunctionArtifact)
264
449
  });
450
+ upgrade(initial(ServerBuildManifestV1), ServerBuildManifestV2, (manifest) => ({
451
+ formatVersion: 2,
452
+ schema: manifest.schema,
453
+ functions: manifest.functions
454
+ }));
265
455
  ArtifactPath.make("inventory.json");
266
456
  ArtifactPath.make("client/manifest.json");
267
457
  ArtifactPath.make("client/shell.html");
@@ -273,8 +463,19 @@ ArtifactPath.make("server/schema.json");
273
463
  //#region ../contracts/dist/runtime/transport.js
274
464
  const runtimeInvocationPath = "/v1/invoke";
275
465
  const runtimeRevisionPath = "/v1/revision";
466
+ const runtimeIndexPreparePath = "/v1/indexes/prepare";
467
+ const runtimeIndexCommitPath = "/v1/indexes/commit";
468
+ const runtimeFileEffectsPath = "/v1/files/effects";
469
+ const runtimeFileEffectsAcknowledgePath = "/v1/files/effects/acknowledge";
276
470
  const appSyncPath = `/_ignotum/v1/sync`;
277
- const RuntimeRequestPath = Schema.Literals([runtimeInvocationPath, runtimeRevisionPath]);
471
+ const RuntimeRequestPath = Schema.Literals([
472
+ runtimeInvocationPath,
473
+ runtimeRevisionPath,
474
+ runtimeIndexPreparePath,
475
+ runtimeIndexCommitPath,
476
+ runtimeFileEffectsPath,
477
+ runtimeFileEffectsAcknowledgePath
478
+ ]);
278
479
  const RuntimeRequestTimestamp = Schema.FiniteFromString.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0)), Schema.brand("ignotum/runtime/RequestTimestamp"));
279
480
  const RuntimeRequestSignature = Sha256.pipe(Schema.brand("ignotum/runtime/RequestSignature"));
280
481
  Schema.Struct({
@@ -293,6 +494,11 @@ Schema.Struct({
293
494
  deploymentId: Schema.optional(DeploymentId),
294
495
  bodySha256: Sha256
295
496
  });
497
+ Schema.Struct({
498
+ appId: AppId,
499
+ deploymentId: DeploymentId
500
+ });
501
+ Schema.Struct({ completed: Schema.Literal(true) });
296
502
  const RuntimeErrorCode = Schema.Literals([
297
503
  "InvalidRequest",
298
504
  "InvalidSignature",
@@ -391,7 +597,7 @@ const makeQueryCache = (allocateId, sendLifecycleMessage) => {
391
597
  }
392
598
  };
393
599
  };
394
- const resultFromWire = Effect.fn("SyncClient.resultFromWire")((wire) => Effect.succeed(wire.type === "Success" ? Result$1.succeed(wire.value === void 0 ? void 0 : decodeTransportValue(wire.value, wire.dates ?? [])) : failureFromWire(wire.error, wire.dates)));
600
+ const resultFromWire = Effect.fn("SyncClient.resultFromWire")((wire, files = []) => Effect.succeed(wire.type === "Success" ? Result$1.succeed(wire.value === void 0 ? void 0 : applyFileGrants(decodeTransportValue(wire.value, wire.dates ?? []), files)) : failureFromWire(wire.error, wire.dates)));
395
601
  const shouldReloadForCloseCode = (code) => code === DeploymentChangedCloseCode;
396
602
  const handshakesMatch = (first, next) => first.appId === next.appId && first.deploymentId === next.deploymentId && first.generation === next.generation;
397
603
  const handshakeDecision = (remembered, acceptedOnConnection, next) => {
@@ -435,14 +641,19 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
435
641
  const pendingInvocation = HashMap.getUnsafe(invocations, id);
436
642
  if (pendingInvocation === void 0) return Effect.void;
437
643
  invocations = HashMap.remove(invocations, id);
438
- return reportClientError(error).pipe(Effect.andThen(Deferred.fail(pendingInvocation.deferred, error)), Effect.asVoid);
644
+ return reportClientError(error).pipe(Effect.andThen(Deferred.fail(pendingInvocation.prepared, error)), Effect.andThen(Deferred.fail(pendingInvocation.deferred, error)), Effect.asVoid);
439
645
  };
440
646
  const handleMessage = Effect.fn("SyncClient.handleMessage")(function* (message) {
441
647
  switch (message.type) {
442
648
  case "Snapshot":
443
649
  if (queryCache.getById(message.id) === void 0) return;
444
- yield* resultFromWire(message.result).pipe(Effect.tap((result) => Effect.sync(() => queryCache.setResult(message.id, result, message.revision))));
650
+ yield* resultFromWire(message.result, message.files ?? []).pipe(Effect.tap((result) => Effect.sync(() => queryCache.setResult(message.id, result, message.revision))));
445
651
  return;
652
+ case "MutationPrepared": {
653
+ const pendingInvocation = HashMap.getUnsafe(invocations, message.id);
654
+ if (pendingInvocation !== void 0) yield* Deferred.succeed(pendingInvocation.prepared, message.uploads);
655
+ return;
656
+ }
446
657
  case "Result": {
447
658
  const pendingInvocation = HashMap.getUnsafe(invocations, message.id);
448
659
  if (pendingInvocation === void 0) return;
@@ -532,24 +743,94 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
532
743
  });
533
744
  const mutate = Effect.fn("SyncClient.mutate")(function* (functionAddress, args) {
534
745
  const invocationId = yield* ids.generate(InvocationId);
746
+ const preparedArguments = yield* prepareMutationArguments(args).pipe(Effect.provideService(IdGenerator, ids), Effect.mapError((cause) => InvalidMutationArguments.make({
747
+ cause,
748
+ function: functionAddress,
749
+ message: `Mutation arguments for ${functionAddress} contain an unsupported file or value.`
750
+ })), Effect.tapError(reportClientError));
751
+ const jsonArgs = yield* Effect.try({
752
+ try: () => encodeTransportObject(preparedArguments.value),
753
+ catch: (cause) => InvalidMutationArguments.make({
754
+ cause,
755
+ function: functionAddress,
756
+ message: `Mutation arguments for ${functionAddress} contain an unsupported value.`
757
+ })
758
+ }).pipe(Effect.tapError(reportClientError));
535
759
  const deferred = yield* Deferred.make();
536
- const message = {
760
+ const prepared = yield* Deferred.make();
761
+ const invokeMessage = {
537
762
  type: "Invoke",
538
763
  id: invocationId,
539
764
  kind: "Mutation",
540
765
  function: functionAddress,
541
- args
766
+ args: jsonArgs
767
+ };
768
+ const message = preparedArguments.occurrences.length === 0 ? invokeMessage : {
769
+ type: "PrepareMutation",
770
+ id: invocationId,
771
+ kind: "Mutation",
772
+ function: functionAddress,
773
+ args: jsonArgs,
774
+ files: preparedArguments.occurrences
542
775
  };
543
776
  invocations = HashMap.set(invocations, invocationId, {
544
777
  deferred,
545
778
  function: functionAddress,
546
- message
779
+ message,
780
+ prepared
547
781
  });
548
- if (writer !== void 0) yield* sendWith(writer, message).pipe(Effect.catchTags({
549
- ConnectionUnavailable: reportClientError,
550
- InvalidClientMessage: (error) => rejectInvocation(invocationId, error)
551
- }));
552
- return yield* Deferred.await(deferred);
782
+ return yield* Effect.gen(function* () {
783
+ if (writer !== void 0) yield* sendWith(writer, message).pipe(Effect.catchTags({
784
+ ConnectionUnavailable: reportClientError,
785
+ InvalidClientMessage: (error) => rejectInvocation(invocationId, error)
786
+ }));
787
+ if (message.type === "PrepareMutation") {
788
+ const uploads = yield* Deferred.await(prepared);
789
+ const uploaded = /* @__PURE__ */ new Set();
790
+ for (const upload of uploads) {
791
+ if (upload.url === void 0 || uploaded.has(upload.file.id)) continue;
792
+ const uploadUrl = upload.url;
793
+ uploaded.add(upload.file.id);
794
+ const file = preparedArguments.nativeFiles.get(upload.file.id);
795
+ if (file === void 0) return yield* InvalidServerMessage.make({
796
+ cause: /* @__PURE__ */ new Error(`Missing native file '${upload.file.id}'.`),
797
+ message: "The server prepared an unknown application file."
798
+ });
799
+ const response = yield* Effect.tryPromise({
800
+ try: () => fetch(uploadUrl, {
801
+ method: "PUT",
802
+ body: file,
803
+ headers: { "content-type": file.type }
804
+ }),
805
+ catch: (cause) => ConnectionUnavailable.make({
806
+ cause,
807
+ message: `File '${file.name}' could not be uploaded.`
808
+ })
809
+ });
810
+ if (!response.ok) {
811
+ const detail = yield* Effect.promise(() => response.text());
812
+ return yield* InvalidMutationArguments.make({
813
+ cause: new Error(detail),
814
+ function: functionAddress,
815
+ message: `File '${file.name}' was rejected by application storage.`
816
+ });
817
+ }
818
+ }
819
+ const pendingInvocation = HashMap.getUnsafe(invocations, invocationId);
820
+ if (pendingInvocation === void 0) return yield* Deferred.await(deferred);
821
+ invocations = HashMap.set(invocations, invocationId, {
822
+ ...pendingInvocation,
823
+ message: invokeMessage
824
+ });
825
+ if (writer !== void 0) yield* sendWith(writer, invokeMessage).pipe(Effect.catchTags({
826
+ ConnectionUnavailable: reportClientError,
827
+ InvalidClientMessage: (error) => rejectInvocation(invocationId, error)
828
+ }));
829
+ }
830
+ return yield* Deferred.await(deferred);
831
+ }).pipe(Effect.tapError(() => Effect.sync(() => {
832
+ invocations = HashMap.remove(invocations, invocationId);
833
+ })));
553
834
  });
554
835
  return SyncClient.of({
555
836
  mutate,
@@ -619,21 +900,140 @@ function useQuery(reference, args) {
619
900
  if (Schema.is(ClientInfrastructureError)(result)) throw result;
620
901
  return result;
621
902
  }
903
+ function usePaginatedQuery(reference, args, options = {}) {
904
+ const functionPath = functionPathOf(reference);
905
+ const skipped = isQuerySkip(args);
906
+ const input = args === void 0 || skipped ? {} : args;
907
+ const jsonArgs = encodeTransportObject(input);
908
+ const pageSize = Schema.decodeSync(PaginationPageSize)(options.pageSize ?? 20);
909
+ const identity = skipped ? `skip:${functionPath}` : syncClientInternals.queryIdentity(functionPath, jsonArgs);
910
+ const [requestedPages, setRequestedPages] = useState$1(1);
911
+ const [refresh, setRefresh] = useState$1(0);
912
+ const [state, setState] = useState$1(() => ({
913
+ identity,
914
+ result: pending()
915
+ }));
916
+ const loadMore = useCallback$1(() => setRequestedPages((count) => count + 1), []);
917
+ useDebugValue$1({
918
+ args: input,
919
+ function: functionPath,
920
+ result: state.result
921
+ });
922
+ useEffect$1(() => {
923
+ setRequestedPages(1);
924
+ setState({
925
+ identity,
926
+ result: pending()
927
+ });
928
+ }, [identity, pageSize]);
929
+ useEffect$1(() => {
930
+ if (skipped) return;
931
+ let active = true;
932
+ const releases = [];
933
+ syncRuntime.runPromise(Effect.gen(function* () {
934
+ const client = yield* SyncClient;
935
+ const items = [];
936
+ let cursor = null;
937
+ for (let pageNumber = 0; pageNumber < requestedPages; pageNumber += 1) {
938
+ const pageArgs = encodeTransportObject({
939
+ ...input,
940
+ pagination: {
941
+ cursor,
942
+ pageSize
943
+ }
944
+ });
945
+ const observer = yield* client.observe(functionPath, pageArgs);
946
+ if (!active) {
947
+ yield* observer.release;
948
+ return;
949
+ }
950
+ const releaseSubscription = observer.subscribe(() => {
951
+ if (active) setRefresh((version) => version + 1);
952
+ });
953
+ releases.push(() => {
954
+ releaseSubscription();
955
+ syncRuntime.runFork(observer.release);
956
+ });
957
+ const snapshot = observer.getSnapshot();
958
+ if (Schema.is(ClientInfrastructureError)(snapshot)) {
959
+ setState({
960
+ identity,
961
+ result: snapshot
962
+ });
963
+ return;
964
+ }
965
+ const inspected = inspectResult(snapshot);
966
+ if (inspected === void 0) {
967
+ setState({
968
+ identity,
969
+ result: items.length === 0 ? pending() : Result$1.succeed({
970
+ items,
971
+ loadMore,
972
+ status: "LoadingMore"
973
+ })
974
+ });
975
+ return;
976
+ }
977
+ if (inspected.type === "Failure") {
978
+ setState({
979
+ identity,
980
+ result: snapshot
981
+ });
982
+ return;
983
+ }
984
+ const page = inspected.value;
985
+ items.push(...page.items);
986
+ cursor = page.nextCursor;
987
+ if (cursor === null) {
988
+ setState({
989
+ identity,
990
+ result: Result$1.succeed({
991
+ items,
992
+ loadMore,
993
+ status: "Exhausted"
994
+ })
995
+ });
996
+ return;
997
+ }
998
+ }
999
+ setState({
1000
+ identity,
1001
+ result: Result$1.succeed({
1002
+ items,
1003
+ loadMore,
1004
+ status: "CanLoadMore"
1005
+ })
1006
+ });
1007
+ })).catch((error) => {
1008
+ if (active && Schema.is(ClientInfrastructureError)(error)) setState({
1009
+ identity,
1010
+ result: error
1011
+ });
1012
+ });
1013
+ return () => {
1014
+ active = false;
1015
+ for (const release of releases) release();
1016
+ };
1017
+ }, [
1018
+ functionPath,
1019
+ identity,
1020
+ loadMore,
1021
+ pageSize,
1022
+ refresh,
1023
+ requestedPages,
1024
+ skipped
1025
+ ]);
1026
+ if (skipped || state.identity !== identity) return pending();
1027
+ const result = state.result;
1028
+ if (Schema.is(ClientInfrastructureError)(result)) throw result;
1029
+ return result;
1030
+ }
622
1031
  const useMutation = (reference) => useMemo$1(() => {
623
1032
  const mutate = (args) => {
624
1033
  const functionPath = functionPathOf(reference);
625
1034
  const input = args === void 0 ? {} : args;
626
1035
  return syncRuntime.runPromise(Effect.gen(function* () {
627
- const client = yield* SyncClient;
628
- const jsonArgs = yield* Effect.try({
629
- try: () => encodeTransportObject(input),
630
- catch: (cause) => InvalidMutationArguments.make({
631
- cause,
632
- function: functionPath,
633
- message: `Mutation arguments for ${functionPath} contain an unsupported value.`
634
- })
635
- }).pipe(Effect.tapError(reportClientError));
636
- return yield* client.mutate(functionPath, jsonArgs);
1036
+ return yield* (yield* SyncClient).mutate(functionPath, input);
637
1037
  }));
638
1038
  };
639
1039
  return mutate;
@@ -648,6 +1048,6 @@ const app = (definition) => {
648
1048
  //#region src/client/index.ts
649
1049
  const Result = { match: Result$1.match };
650
1050
  //#endregion
651
- export { Component, Fragment, Query, Result, app, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray, useCallback, useContext, useDebugValue, useEffect, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMutation, useQuery, useReducer, useRef, useState };
1051
+ export { Component, Files, Fragment, Query, Result, app, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray, useCallback, useContext, useDebugValue, useEffect, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMutation, usePaginatedQuery, useQuery, useReducer, useRef, useState };
652
1052
 
653
1053
  //# sourceMappingURL=client.js.map