eraspace 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -7
- package/bin.js +437 -104
- package/package.json +2 -4
- package/sdk/src/client/client.ts +30 -13
- package/sdk/src/client/config.ts +22 -4
- package/sdk/src/client/connection.ts +230 -47
- package/sdk/src/client/errors.ts +10 -1
- package/sdk/src/client/files.ts +389 -0
- package/sdk/src/client/index.ts +4 -3
- package/sdk/src/client/pagination.ts +6 -3
- package/sdk/src/client/request-key.ts +13 -0
- package/sdk/src/client/retry.ts +5 -0
- package/sdk/src/client/room.ts +70 -18
- package/sdk/src/client/router.ts +12 -3
- package/sdk/src/client/storage.ts +127 -10
- package/sdk/src/client/sub-key.ts +16 -0
- package/sdk/src/client/subscription.ts +16 -1
- package/sdk/src/client/transport.ts +423 -0
- package/sdk/src/client/types.ts +7 -0
- package/sdk/src/server/index.ts +44 -6
- package/templates/chat/client/index.tsx +10 -6
- package/templates/chat/client/styles.css +15 -0
- package/templates/chat/server/index.ts +16 -6
- package/templates/poll/client/index.tsx +1 -1
- package/templates/todo/client/index.tsx +1 -1
package/bin.js
CHANGED
|
@@ -241,6 +241,34 @@ __export(Effect_exports, {
|
|
|
241
241
|
zipWith: () => zipWith3
|
|
242
242
|
});
|
|
243
243
|
|
|
244
|
+
// node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Context.js
|
|
245
|
+
var Context_exports = {};
|
|
246
|
+
__export(Context_exports, {
|
|
247
|
+
Reference: () => Reference,
|
|
248
|
+
Service: () => Service,
|
|
249
|
+
ServiceTypeId: () => ServiceTypeId,
|
|
250
|
+
add: () => add,
|
|
251
|
+
addOrOmit: () => addOrOmit,
|
|
252
|
+
addUnsafe: () => addUnsafe,
|
|
253
|
+
empty: () => empty,
|
|
254
|
+
get: () => get,
|
|
255
|
+
getOption: () => getOption,
|
|
256
|
+
getOrElse: () => getOrElse2,
|
|
257
|
+
getOrUndefined: () => getOrUndefined2,
|
|
258
|
+
getOrUndefinedUnsafe: () => getOrUndefinedUnsafe,
|
|
259
|
+
getUnsafe: () => getUnsafe,
|
|
260
|
+
hasSameCache: () => hasSameCache,
|
|
261
|
+
isContext: () => isContext,
|
|
262
|
+
isKey: () => isKey,
|
|
263
|
+
isReference: () => isReference,
|
|
264
|
+
make: () => make5,
|
|
265
|
+
makeUnsafe: () => makeUnsafe,
|
|
266
|
+
merge: () => merge,
|
|
267
|
+
mergeAll: () => mergeAll,
|
|
268
|
+
omit: () => omit,
|
|
269
|
+
pick: () => pick
|
|
270
|
+
});
|
|
271
|
+
|
|
244
272
|
// node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Pipeable.js
|
|
245
273
|
var pipeArguments = (self, args2) => {
|
|
246
274
|
switch (args2.length) {
|
|
@@ -2273,6 +2301,7 @@ var Proto = {
|
|
|
2273
2301
|
};
|
|
2274
2302
|
var hasSameCache = (self, that) => self.cacheRoot === that.cacheRoot;
|
|
2275
2303
|
var isContext = (u) => hasProperty(u, TypeId3);
|
|
2304
|
+
var isKey = (u) => hasProperty(u, ServiceTypeId);
|
|
2276
2305
|
var isReference = (u) => !!u[ReferenceTypeId];
|
|
2277
2306
|
var empty = () => emptyContext2;
|
|
2278
2307
|
var emptyContext2 = /* @__PURE__ */ makeUnsafe(/* @__PURE__ */ new Map());
|
|
@@ -2293,6 +2322,12 @@ var addUnsafe = (self, key, service4) => {
|
|
|
2293
2322
|
parent: impl.overlay
|
|
2294
2323
|
}, impl.depth + 1);
|
|
2295
2324
|
};
|
|
2325
|
+
var addOrOmit = /* @__PURE__ */ dual(3, (self, key, service4) => service4._tag === "None" ? omit(key)(self) : add(self, key, service4.value));
|
|
2326
|
+
var getOrElse2 = /* @__PURE__ */ dual(3, (self, key, orElse6) => {
|
|
2327
|
+
const value4 = lookup(self, key.key);
|
|
2328
|
+
if (value4 !== notFound) return value4;
|
|
2329
|
+
return isReference(key) ? getDefaultValue(key) : orElse6();
|
|
2330
|
+
});
|
|
2296
2331
|
var getOrUndefined2 = /* @__PURE__ */ dual(2, (self, key) => getOrUndefinedUnsafe(self, key.key));
|
|
2297
2332
|
var getOrUndefinedUnsafe = (self, key) => {
|
|
2298
2333
|
const value4 = lookup(self, key);
|
|
@@ -2342,6 +2377,26 @@ var mergeAll = (...ctxs) => {
|
|
|
2342
2377
|
}
|
|
2343
2378
|
return makeUnsafe(map18);
|
|
2344
2379
|
};
|
|
2380
|
+
var pick = (...services) => (self) => {
|
|
2381
|
+
if (services.length <= 1) {
|
|
2382
|
+
const map18 = /* @__PURE__ */ new Map();
|
|
2383
|
+
if (services.length === 1) {
|
|
2384
|
+
const key = services[0].key;
|
|
2385
|
+
const value4 = lookup(self, key);
|
|
2386
|
+
if (value4 !== notFound) map18.set(key, value4);
|
|
2387
|
+
}
|
|
2388
|
+
return makeUnsafe(map18);
|
|
2389
|
+
}
|
|
2390
|
+
const keep = new Set(services.map((key) => key.key));
|
|
2391
|
+
return withFlat(self, (map18) => map18.forEach((_, key) => {
|
|
2392
|
+
if (!keep.has(key)) map18.delete(key);
|
|
2393
|
+
}));
|
|
2394
|
+
};
|
|
2395
|
+
var omit = (...keys4) => (self) => withFlat(self, (map18) => {
|
|
2396
|
+
for (let i = 0; i < keys4.length; i++) {
|
|
2397
|
+
map18.delete(keys4[i].key);
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2345
2400
|
var Reference = Service;
|
|
2346
2401
|
|
|
2347
2402
|
// node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Duration.js
|
|
@@ -2988,7 +3043,7 @@ var merge2 = /* @__PURE__ */ match3({
|
|
|
2988
3043
|
onFailure: identity,
|
|
2989
3044
|
onSuccess: identity
|
|
2990
3045
|
});
|
|
2991
|
-
var
|
|
3046
|
+
var getOrElse3 = /* @__PURE__ */ dual(2, (self, onFailure) => isFailure2(self) ? onFailure(self.failure) : self.success);
|
|
2992
3047
|
var getOrThrowWith2 = /* @__PURE__ */ dual(2, (self, onFailure) => {
|
|
2993
3048
|
if (isSuccess2(self)) {
|
|
2994
3049
|
return self.success;
|
|
@@ -15424,12 +15479,12 @@ var posixImpl = /* @__PURE__ */ make21({
|
|
|
15424
15479
|
resolve,
|
|
15425
15480
|
normalize(path) {
|
|
15426
15481
|
if (path.length === 0) return ".";
|
|
15427
|
-
const
|
|
15482
|
+
const isAbsolute2 = path.charCodeAt(0) === 47;
|
|
15428
15483
|
const trailingSeparator = path.charCodeAt(path.length - 1) === 47;
|
|
15429
|
-
path = normalizeStringPosix(path, !
|
|
15430
|
-
if (path.length === 0 && !
|
|
15484
|
+
path = normalizeStringPosix(path, !isAbsolute2);
|
|
15485
|
+
if (path.length === 0 && !isAbsolute2) path = ".";
|
|
15431
15486
|
if (path.length > 0 && trailingSeparator) path += "/";
|
|
15432
|
-
if (
|
|
15487
|
+
if (isAbsolute2) return "/" + path;
|
|
15433
15488
|
return path;
|
|
15434
15489
|
},
|
|
15435
15490
|
isAbsolute(path) {
|
|
@@ -15649,9 +15704,9 @@ var posixImpl = /* @__PURE__ */ make21({
|
|
|
15649
15704
|
};
|
|
15650
15705
|
if (path.length === 0) return ret;
|
|
15651
15706
|
let code = path.charCodeAt(0);
|
|
15652
|
-
const
|
|
15707
|
+
const isAbsolute2 = code === 47;
|
|
15653
15708
|
let start;
|
|
15654
|
-
if (
|
|
15709
|
+
if (isAbsolute2) {
|
|
15655
15710
|
ret.root = "/";
|
|
15656
15711
|
start = 1;
|
|
15657
15712
|
} else {
|
|
@@ -15687,11 +15742,11 @@ var posixImpl = /* @__PURE__ */ make21({
|
|
|
15687
15742
|
preDotState === 0 || // The (right-most) trimmed path component is exactly '..'
|
|
15688
15743
|
preDotState === 1 && startDot === end3 - 1 && startDot === startPart + 1) {
|
|
15689
15744
|
if (end3 !== -1) {
|
|
15690
|
-
if (startPart === 0 &&
|
|
15745
|
+
if (startPart === 0 && isAbsolute2) ret.base = ret.name = path.slice(1, end3);
|
|
15691
15746
|
else ret.base = ret.name = path.slice(startPart, end3);
|
|
15692
15747
|
}
|
|
15693
15748
|
} else {
|
|
15694
|
-
if (startPart === 0 &&
|
|
15749
|
+
if (startPart === 0 && isAbsolute2) {
|
|
15695
15750
|
ret.name = path.slice(1, startDot);
|
|
15696
15751
|
ret.base = path.slice(1, end3);
|
|
15697
15752
|
} else {
|
|
@@ -15701,7 +15756,7 @@ var posixImpl = /* @__PURE__ */ make21({
|
|
|
15701
15756
|
ret.ext = path.slice(startDot, end3);
|
|
15702
15757
|
}
|
|
15703
15758
|
if (startPart > 0) ret.dir = path.slice(0, startPart - 1);
|
|
15704
|
-
else if (
|
|
15759
|
+
else if (isAbsolute2) ret.dir = "/";
|
|
15705
15760
|
return ret;
|
|
15706
15761
|
},
|
|
15707
15762
|
sep: "/",
|
|
@@ -16361,7 +16416,7 @@ function transformOptionalEffect(f) {
|
|
|
16361
16416
|
transform: f
|
|
16362
16417
|
});
|
|
16363
16418
|
}
|
|
16364
|
-
function
|
|
16419
|
+
function omit2() {
|
|
16365
16420
|
return transformOptional(() => none2());
|
|
16366
16421
|
}
|
|
16367
16422
|
function withDefault(defaultValue) {
|
|
@@ -18565,9 +18620,9 @@ function isPattern(regExp, annotations) {
|
|
|
18565
18620
|
});
|
|
18566
18621
|
}
|
|
18567
18622
|
var bodyOwners = /* @__PURE__ */ new WeakMap();
|
|
18568
|
-
function copy2(ast,
|
|
18569
|
-
const out = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast,
|
|
18570
|
-
if (Reflect.ownKeys(
|
|
18623
|
+
function copy2(ast, changes3) {
|
|
18624
|
+
const out = Object.assign(Object.create(Object.getPrototypeOf(ast)), ast, changes3);
|
|
18625
|
+
if (Reflect.ownKeys(changes3).every((key) => key === "context" || key === "encoding")) {
|
|
18571
18626
|
bodyOwners.set(out, getContextOwner(ast));
|
|
18572
18627
|
}
|
|
18573
18628
|
return out;
|
|
@@ -24947,10 +25002,10 @@ function toFormatter(ast, options3) {
|
|
|
24947
25002
|
}
|
|
24948
25003
|
|
|
24949
25004
|
// node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Struct.js
|
|
24950
|
-
var
|
|
25005
|
+
var pick2 = /* @__PURE__ */ dual(2, (self, keys4) => {
|
|
24951
25006
|
return buildStruct(self, (k, v) => hasPropertyKey(keys4, k) ? [k, v] : void 0);
|
|
24952
25007
|
});
|
|
24953
|
-
var
|
|
25008
|
+
var omit3 = /* @__PURE__ */ dual(2, (self, keys4) => {
|
|
24954
25009
|
return buildStruct(self, (k, v) => !hasPropertyKey(keys4, k) ? [k, v] : void 0);
|
|
24955
25010
|
});
|
|
24956
25011
|
var assign = /* @__PURE__ */ dual(2, (self, that) => {
|
|
@@ -25070,10 +25125,10 @@ var OptionalImpl = class {
|
|
|
25070
25125
|
this.replaceResult = replaceResult;
|
|
25071
25126
|
}
|
|
25072
25127
|
replace(a, s) {
|
|
25073
|
-
return
|
|
25128
|
+
return getOrElse3(this.replaceResult(a, s), () => s);
|
|
25074
25129
|
}
|
|
25075
25130
|
modify(f) {
|
|
25076
|
-
return (s) =>
|
|
25131
|
+
return (s) => getOrElse3(flatMap2(this.getResult(s), (a) => this.replaceResult(f(a), s)), () => s);
|
|
25077
25132
|
}
|
|
25078
25133
|
compose(that) {
|
|
25079
25134
|
return make36(compose3(this.node, that.node));
|
|
@@ -25121,14 +25176,14 @@ var OptionalImpl = class {
|
|
|
25121
25176
|
})));
|
|
25122
25177
|
}
|
|
25123
25178
|
pick(keys4) {
|
|
25124
|
-
return this.compose(makeLens(
|
|
25125
|
-
...
|
|
25179
|
+
return this.compose(makeLens(pick2(keys4), (p, a) => ({
|
|
25180
|
+
...omit3(a, keys4),
|
|
25126
25181
|
...p
|
|
25127
25182
|
})));
|
|
25128
25183
|
}
|
|
25129
25184
|
omit(keys4) {
|
|
25130
|
-
return this.compose(makeLens(
|
|
25131
|
-
...
|
|
25185
|
+
return this.compose(makeLens(omit3(keys4), (o, a) => ({
|
|
25186
|
+
...pick2(a, keys4),
|
|
25132
25187
|
...o
|
|
25133
25188
|
})));
|
|
25134
25189
|
}
|
|
@@ -25174,7 +25229,7 @@ var OptionalImpl = class {
|
|
|
25174
25229
|
);
|
|
25175
25230
|
}
|
|
25176
25231
|
modifyAll(f) {
|
|
25177
|
-
return (s) =>
|
|
25232
|
+
return (s) => getOrElse3(flatMap2(this.getResult(s), (as5) => this.replaceResult(as5.map(f), s)), () => s);
|
|
25178
25233
|
}
|
|
25179
25234
|
};
|
|
25180
25235
|
var IsoImpl = class extends OptionalImpl {
|
|
@@ -25213,7 +25268,7 @@ var PrismImpl = class extends OptionalImpl {
|
|
|
25213
25268
|
return this.set(a);
|
|
25214
25269
|
}
|
|
25215
25270
|
modify(f) {
|
|
25216
|
-
return (s) =>
|
|
25271
|
+
return (s) => getOrElse3(map2(this.getResult(s), (a) => this.set(f(a))), () => s);
|
|
25217
25272
|
}
|
|
25218
25273
|
};
|
|
25219
25274
|
function make36(node) {
|
|
@@ -26001,7 +26056,7 @@ function toIssueEffect(self) {
|
|
|
26001
26056
|
return catchCause3(self, (cause) => failCauseSync2(() => map6(cause, (error2) => error2.issue)));
|
|
26002
26057
|
}
|
|
26003
26058
|
function withDecodingDefaultKey(defaultValue, options3) {
|
|
26004
|
-
const encode5 = options3?.encodingStrategy === "omit" ?
|
|
26059
|
+
const encode5 = options3?.encodingStrategy === "omit" ? omit2() : passthrough2();
|
|
26005
26060
|
return (self) => {
|
|
26006
26061
|
return optionalKey2(toEncoded2(self)).pipe(decodeTo2(self, {
|
|
26007
26062
|
decode: withDefault(toIssueEffect(defaultValue)),
|
|
@@ -26015,7 +26070,7 @@ function withDecodingDefaultTypeKey(defaultValue, options3) {
|
|
|
26015
26070
|
};
|
|
26016
26071
|
}
|
|
26017
26072
|
function withDecodingDefault(defaultValue, options3) {
|
|
26018
|
-
const encode5 = options3?.encodingStrategy === "omit" ?
|
|
26073
|
+
const encode5 = options3?.encodingStrategy === "omit" ? omit2() : passthrough2();
|
|
26019
26074
|
return (self) => {
|
|
26020
26075
|
return optional2(toEncoded2(self)).pipe(decodeTo2(self, {
|
|
26021
26076
|
decode: withDefault(toIssueEffect(defaultValue)),
|
|
@@ -38939,7 +38994,7 @@ var runWith2 = (command2, config2) => {
|
|
|
38939
38994
|
// cli/package.json
|
|
38940
38995
|
var package_default = {
|
|
38941
38996
|
name: "eraspace-cli",
|
|
38942
|
-
version: "0.
|
|
38997
|
+
version: "0.3.0",
|
|
38943
38998
|
private: true,
|
|
38944
38999
|
license: "MIT",
|
|
38945
39000
|
type: "module",
|
|
@@ -39266,6 +39321,48 @@ function formatLog(l) {
|
|
|
39266
39321
|
const data = l.data === void 0 || l.data === null ? "" : " " + c.gray(JSON.stringify(l.data));
|
|
39267
39322
|
return `${c.gray(time3)} ${color(level.padEnd(5))} ${fn3}${l.msg ?? ""}${data}`;
|
|
39268
39323
|
}
|
|
39324
|
+
var SKIPPED = {
|
|
39325
|
+
fetch: "uses fetch",
|
|
39326
|
+
identity: "reads the caller's identity",
|
|
39327
|
+
running: "was running",
|
|
39328
|
+
recovering: "cache recovering",
|
|
39329
|
+
"new index": "reads an index this deploy adds",
|
|
39330
|
+
committed: "a commit changed it during the check",
|
|
39331
|
+
budget: "out of time",
|
|
39332
|
+
limit: "too many entries",
|
|
39333
|
+
interrupted: "interrupted"
|
|
39334
|
+
};
|
|
39335
|
+
var call = (fn3, args2) => {
|
|
39336
|
+
const text2 = Array.isArray(args2) ? args2.map((a) => JSON.stringify(a)).join(", ") : JSON.stringify(args2);
|
|
39337
|
+
return `${fn3}(${text2 && text2.length > 80 ? `${text2.slice(0, 77)}...` : text2 ?? ""})`;
|
|
39338
|
+
};
|
|
39339
|
+
function formatShadow(report) {
|
|
39340
|
+
if (report.note) return c.dim(`Live queries: none checked (${report.note})`);
|
|
39341
|
+
const total = report.total ?? 0;
|
|
39342
|
+
if (total === 0) return c.dim("Live queries: none cached, nothing to check");
|
|
39343
|
+
const errors = report.errors ?? 0;
|
|
39344
|
+
const changed = report.changed ?? 0;
|
|
39345
|
+
const parts2 = [
|
|
39346
|
+
`${report.unchanged ?? 0} unchanged`,
|
|
39347
|
+
changed ? c.yellow(`${changed} change value`) : "0 change value",
|
|
39348
|
+
errors ? c.red(`${errors} now throw`) : "0 now throw",
|
|
39349
|
+
...report.fixed ? [c.green(`${report.fixed} fixed`)] : [],
|
|
39350
|
+
...report.skipped ? [c.dim(`${report.skipped} skipped`)] : []
|
|
39351
|
+
];
|
|
39352
|
+
const at = report.commitSeq ? ` at commit ${report.commitSeq}` : "";
|
|
39353
|
+
const lines2 = [`Live queries: ${report.checked ?? 0} of ${total} checked against the new code${at}: ${parts2.join(", ")}`];
|
|
39354
|
+
for (const e of report.entries ?? []) {
|
|
39355
|
+
const what = call(e.fn, e.args);
|
|
39356
|
+
if (e.outcome === "error") lines2.push(` ${c.red("throws ")} ${what}: ${e.error ? `${e.error.name}: ${e.error.message}` : ""}`);
|
|
39357
|
+
else if (e.outcome === "changed") lines2.push(` ${c.yellow("changes")} ${what}${e.error ? `: ${e.error.name}: ${e.error.message}` : ""}`);
|
|
39358
|
+
else if (e.outcome === "fixed") lines2.push(` ${c.green("fixed ")} ${what}`);
|
|
39359
|
+
else lines2.push(c.dim(` skipped ${what}: ${SKIPPED[e.reason ?? ""] ?? e.reason ?? ""}`));
|
|
39360
|
+
}
|
|
39361
|
+
const listed = report.entries?.length ?? 0;
|
|
39362
|
+
const notable = errors + changed + (report.fixed ?? 0) + (report.skipped ?? 0);
|
|
39363
|
+
if (notable > listed) lines2.push(c.dim(` ... and ${notable - listed} more`));
|
|
39364
|
+
return lines2.join("\n");
|
|
39365
|
+
}
|
|
39269
39366
|
|
|
39270
39367
|
// cli/src/api.ts
|
|
39271
39368
|
var Timestamp = Schema_exports.NullOr(Schema_exports.Union([Schema_exports.Number, Schema_exports.String]));
|
|
@@ -39280,10 +39377,34 @@ var App = Schema_exports.Struct({
|
|
|
39280
39377
|
expiresAt: Schema_exports.optional(Timestamp),
|
|
39281
39378
|
publicInspection: Schema_exports.optional(Schema_exports.Boolean)
|
|
39282
39379
|
});
|
|
39380
|
+
var Count = Schema_exports.optional(Schema_exports.Number);
|
|
39381
|
+
var ShadowReport = Schema_exports.Struct({
|
|
39382
|
+
total: Count,
|
|
39383
|
+
checked: Count,
|
|
39384
|
+
unchanged: Count,
|
|
39385
|
+
changed: Count,
|
|
39386
|
+
errors: Count,
|
|
39387
|
+
fixed: Count,
|
|
39388
|
+
skipped: Count,
|
|
39389
|
+
commitSeq: Count,
|
|
39390
|
+
note: Schema_exports.optional(Schema_exports.String),
|
|
39391
|
+
entries: Schema_exports.optional(
|
|
39392
|
+
Schema_exports.Array(
|
|
39393
|
+
Schema_exports.Struct({
|
|
39394
|
+
fn: Schema_exports.String,
|
|
39395
|
+
args: Schema_exports.optional(Schema_exports.Unknown),
|
|
39396
|
+
outcome: Schema_exports.String,
|
|
39397
|
+
reason: Schema_exports.optional(Schema_exports.String),
|
|
39398
|
+
error: Schema_exports.optional(Schema_exports.Struct({ name: Schema_exports.String, message: Schema_exports.String }))
|
|
39399
|
+
})
|
|
39400
|
+
)
|
|
39401
|
+
)
|
|
39402
|
+
});
|
|
39283
39403
|
var Deployed = Schema_exports.Struct({
|
|
39284
39404
|
...App.fields,
|
|
39285
39405
|
version: Schema_exports.optional(Schema_exports.Number),
|
|
39286
|
-
claimToken: Schema_exports.optional(Schema_exports.String)
|
|
39406
|
+
claimToken: Schema_exports.optional(Schema_exports.String),
|
|
39407
|
+
shadow: Schema_exports.optional(ShadowReport)
|
|
39287
39408
|
});
|
|
39288
39409
|
var AppList = Schema_exports.Struct({ apps: Schema_exports.Array(App) });
|
|
39289
39410
|
var StateChange = Schema_exports.Struct({ state: Schema_exports.String });
|
|
@@ -39448,15 +39569,20 @@ var login = Effect_exports.fnUntraced(function* (config2, browser) {
|
|
|
39448
39569
|
}
|
|
39449
39570
|
return yield* fail12("The sign-in code expired. Run eraspace auth login again.");
|
|
39450
39571
|
});
|
|
39572
|
+
var LogoutResult = Schema_exports.Struct({ ok: Schema_exports.Literal(true) });
|
|
39451
39573
|
var logout = Effect_exports.fnUntraced(function* (config2) {
|
|
39452
39574
|
const session = yield* sessionFor(config2.origin);
|
|
39453
39575
|
if (!session) {
|
|
39454
39576
|
yield* Console_exports.log(`Not signed in to ${config2.origin}.`);
|
|
39455
39577
|
return;
|
|
39456
39578
|
}
|
|
39457
|
-
yield*
|
|
39579
|
+
const response = yield* api(config2, "POST", "/_api/auth/logout", { bearer: Redacted_exports.make(session.token), timeout: "10 seconds" });
|
|
39580
|
+
if (response.status !== 401) {
|
|
39581
|
+
if (response.status >= 400 || response.json.ok === false) return yield* Effect_exports.fail(yield* failure(response));
|
|
39582
|
+
yield* decode5(LogoutResult, response.json, "sign-out");
|
|
39583
|
+
}
|
|
39458
39584
|
yield* removeSession(config2.origin);
|
|
39459
|
-
yield* Console_exports.log(`Signed out of ${config2.origin}.`);
|
|
39585
|
+
yield* Console_exports.log(response.status === 401 ? `Removed invalid session for ${config2.origin}.` : `Signed out of ${config2.origin}.`);
|
|
39460
39586
|
});
|
|
39461
39587
|
var okWhoami = ok(Whoami);
|
|
39462
39588
|
var whoami = Effect_exports.fnUntraced(function* (config2) {
|
|
@@ -39468,7 +39594,7 @@ var whoami = Effect_exports.fnUntraced(function* (config2) {
|
|
|
39468
39594
|
import { basename } from "node:path";
|
|
39469
39595
|
|
|
39470
39596
|
// cli/src/build.ts
|
|
39471
|
-
import { dirname as dirname2, join as join5, relative as relative2, resolve as resolve8, sep } from "node:path";
|
|
39597
|
+
import { dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve as resolve8, sep } from "node:path";
|
|
39472
39598
|
import * as esbuild from "esbuild";
|
|
39473
39599
|
import { parseSync } from "oxc-parser";
|
|
39474
39600
|
var toPosix = (p) => p.split(sep).join("/");
|
|
@@ -39478,14 +39604,12 @@ function scanImports(file2, source) {
|
|
|
39478
39604
|
const fatal = result4.errors.find((e) => e.severity === "Error");
|
|
39479
39605
|
if (fatal) return { imports: [], error: { message: fatal.message, at: fatal.labels[0]?.start ?? 0 } };
|
|
39480
39606
|
const imports = [];
|
|
39481
|
-
for (const
|
|
39482
|
-
if (
|
|
39483
|
-
|
|
39484
|
-
|
|
39485
|
-
|
|
39486
|
-
|
|
39487
|
-
const first = from[0]?.moduleRequest;
|
|
39488
|
-
if (first) imports.push({ spec: first.value, at: first.start, kind: "import-statement" });
|
|
39607
|
+
for (const statement of result4.program.body) {
|
|
39608
|
+
if (statement.type === "ImportDeclaration" && statement.importKind !== "type") {
|
|
39609
|
+
imports.push({ spec: statement.source.value, at: statement.source.start, kind: "import-statement" });
|
|
39610
|
+
} else if ((statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") && statement.exportKind !== "type" && statement.source) {
|
|
39611
|
+
imports.push({ spec: statement.source.value, at: statement.source.start, kind: "import-statement" });
|
|
39612
|
+
}
|
|
39489
39613
|
}
|
|
39490
39614
|
for (const imp of result4.module.dynamicImports) {
|
|
39491
39615
|
const literal = /^\s*(["'`])(.*)\1\s*$/s.exec(source.slice(imp.moduleRequest.start, imp.moduleRequest.end));
|
|
@@ -39493,10 +39617,20 @@ function scanImports(file2, source) {
|
|
|
39493
39617
|
}
|
|
39494
39618
|
return { imports: imports.sort((a, b) => (a.at ?? 0) - (b.at ?? 0)) };
|
|
39495
39619
|
}
|
|
39620
|
+
var MAX_MODULE_BYTES = 256 * 1024;
|
|
39621
|
+
var EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".tsx"];
|
|
39622
|
+
function importCandidates(base) {
|
|
39623
|
+
const stem = /\.m?js$/.test(base) ? base.replace(/\.m?js$/, "") : void 0;
|
|
39624
|
+
return [
|
|
39625
|
+
base,
|
|
39626
|
+
...EXTENSIONS.map((ext) => base + ext),
|
|
39627
|
+
...stem === void 0 ? [] : [".ts", ".mts", ".tsx"].map((ext) => stem + ext),
|
|
39628
|
+
...EXTENSIONS.map((ext) => join5(base, "index" + ext))
|
|
39629
|
+
].filter((candidate) => EXTENSIONS.some((ext) => candidate.endsWith(ext)));
|
|
39630
|
+
}
|
|
39496
39631
|
var resolveRelative = Effect_exports.fnUntraced(function* (fromFile, spec) {
|
|
39497
39632
|
const fs = yield* FileSystem_exports.FileSystem;
|
|
39498
|
-
const
|
|
39499
|
-
for (const candidate of [base, base + ".ts", join5(base, "index.ts")]) {
|
|
39633
|
+
for (const candidate of importCandidates(resolve8(dirname2(fromFile), spec))) {
|
|
39500
39634
|
const info2 = yield* fs.stat(candidate).pipe(Effect_exports.option);
|
|
39501
39635
|
if (info2._tag === "Some" && info2.value.type === "File") return candidate;
|
|
39502
39636
|
}
|
|
@@ -39519,6 +39653,10 @@ var collectServerFiles = Effect_exports.fnUntraced(function* (root) {
|
|
|
39519
39653
|
const rel = toPosix(relative2(root, abs2));
|
|
39520
39654
|
const source = yield* fs.readFileString(abs2);
|
|
39521
39655
|
files[rel] = source;
|
|
39656
|
+
const bytes2 = Buffer.byteLength(source, "utf8");
|
|
39657
|
+
if (bytes2 > MAX_MODULE_BYTES) {
|
|
39658
|
+
errors.push({ file: rel, line: 0, column: 0, message: `module is ${bytes2} bytes, over the server's ${MAX_MODULE_BYTES} byte (256 KiB) limit; split it into smaller modules` });
|
|
39659
|
+
}
|
|
39522
39660
|
const { imports, error: error2 } = scanImports(abs2, source);
|
|
39523
39661
|
if (error2) {
|
|
39524
39662
|
errors.push({ file: rel, ...lineCol(source, error2.at), message: error2.message });
|
|
@@ -39573,6 +39711,7 @@ function clientPlugin(root, sdk, rejected) {
|
|
|
39573
39711
|
setup(build3) {
|
|
39574
39712
|
build3.onResolve({ filter: /^[^./]/ }, async (args2) => {
|
|
39575
39713
|
if (args2.pluginData === "eraspace-sdk") return void 0;
|
|
39714
|
+
if (args2.kind === "entry-point" || isAbsolute(args2.path)) return void 0;
|
|
39576
39715
|
const spec = args2.path;
|
|
39577
39716
|
const fromCapsule = !args2.importer || inCapsule(args2.importer);
|
|
39578
39717
|
if (fromCapsule) {
|
|
@@ -39778,6 +39917,14 @@ var requireTarget = Effect_exports.fnUntraced(function* (config2, explicit) {
|
|
|
39778
39917
|
);
|
|
39779
39918
|
return { ref, claim: claim2 };
|
|
39780
39919
|
});
|
|
39920
|
+
var confirmedDeletes = Effect_exports.fnUntraced(function* (tables) {
|
|
39921
|
+
const out = {};
|
|
39922
|
+
for (const [table2, rows] of Object.entries(tables ?? {})) {
|
|
39923
|
+
if (!/^\d+$/.test(rows)) return yield* fail12(`--delete-table ${table2}=${rows}: the row count must be a whole number, e.g. --delete-table ${table2}=12`);
|
|
39924
|
+
out[table2] = Number(rows);
|
|
39925
|
+
}
|
|
39926
|
+
return out;
|
|
39927
|
+
});
|
|
39781
39928
|
var withoutEnv = Effect_exports.fnUntraced(function* (payload) {
|
|
39782
39929
|
if (Object.keys(payload.env).length) {
|
|
39783
39930
|
yield* Console_exports.warn(
|
|
@@ -39788,23 +39935,13 @@ var withoutEnv = Effect_exports.fnUntraced(function* (payload) {
|
|
|
39788
39935
|
}
|
|
39789
39936
|
return { ...payload, env: {} };
|
|
39790
39937
|
});
|
|
39791
|
-
var okDeployed = ok(Deployed);
|
|
39792
39938
|
var requestedName = (config2, opts) => opts.name !== void 0 ? { name: opts.name } : { name: config2.name ?? toAppName(basename(config2.root)), uniqueName: true };
|
|
39793
39939
|
var createApp = Effect_exports.fnUntraced(function* (config2, payload, extra, opts, signedIn) {
|
|
39794
39940
|
const naming = requestedName(config2, opts);
|
|
39795
|
-
const
|
|
39796
|
-
body: { ...payload, ...extra, ...
|
|
39941
|
+
const response = yield* api(config2, "POST", "/_api/apps", {
|
|
39942
|
+
body: { ...payload, ...extra, ...naming },
|
|
39797
39943
|
timeout: DEPLOY_TIMEOUT
|
|
39798
39944
|
});
|
|
39799
|
-
let response = yield* create(naming);
|
|
39800
|
-
if (response.status === 401 && !signedIn && naming.uniqueName) {
|
|
39801
|
-
yield* Console_exports.warn(
|
|
39802
|
-
c.yellow(
|
|
39803
|
-
`This platform names apps only for signed-in developers, so this app gets an id. Run eraspace auth login and eraspace claim to name it ${naming.name}.`
|
|
39804
|
-
)
|
|
39805
|
-
);
|
|
39806
|
-
response = yield* create({});
|
|
39807
|
-
}
|
|
39808
39945
|
if (response.status === 409 && !naming.uniqueName) {
|
|
39809
39946
|
return yield* fail12(
|
|
39810
39947
|
signedIn ? `The name ${naming.name} is taken. Pick another with --name or in eraspace.json; if it is your app, deploy to it with --app ${naming.name}.` : `The name ${naming.name} is taken. Pick another with --name.`
|
|
@@ -39827,22 +39964,30 @@ var deploy = Effect_exports.fnUntraced(function* (config2, opts = {}) {
|
|
|
39827
39964
|
const build3 = yield* buildCapsule(config2.root);
|
|
39828
39965
|
if (!build3.ok || !build3.payload)
|
|
39829
39966
|
return yield* fail12(yield* formatErrors(build3.errors, config2.root));
|
|
39830
|
-
const
|
|
39967
|
+
const deleteTables = yield* confirmedDeletes(opts.deleteTables);
|
|
39968
|
+
const extra = {
|
|
39969
|
+
...opts.publicInspect ? { publicInspection: true } : {},
|
|
39970
|
+
...Object.keys(deleteTables).length ? { deleteTables } : {},
|
|
39971
|
+
...opts.shadow ? { shadow: true } : {},
|
|
39972
|
+
...opts.failOnQueryErrors ? { failOnQueryErrors: true } : {}
|
|
39973
|
+
};
|
|
39831
39974
|
const signedIn = (yield* bearerFor(config2.origin)) !== void 0;
|
|
39832
39975
|
const t = yield* target(config2);
|
|
39833
39976
|
let result4;
|
|
39834
39977
|
if (t.ref) {
|
|
39835
39978
|
const payload = t.claim ? yield* withoutEnv(build3.payload) : build3.payload;
|
|
39836
|
-
|
|
39837
|
-
|
|
39838
|
-
|
|
39839
|
-
|
|
39840
|
-
|
|
39841
|
-
|
|
39842
|
-
|
|
39843
|
-
|
|
39844
|
-
|
|
39845
|
-
|
|
39979
|
+
const path = `/_api/apps/${enc(t.ref)}/deploy`;
|
|
39980
|
+
const response = yield* api(config2, "POST", path, {
|
|
39981
|
+
body: { ...payload, ...extra },
|
|
39982
|
+
claim: t.claim,
|
|
39983
|
+
timeout: DEPLOY_TIMEOUT
|
|
39984
|
+
});
|
|
39985
|
+
if (response.status >= 400 || response.json.ok === false) {
|
|
39986
|
+
const refused = Schema_exports.decodeUnknownOption(ShadowReport)(response.json.shadow);
|
|
39987
|
+
if (Option_exports.isSome(refused)) yield* Console_exports.log(formatShadow(refused.value));
|
|
39988
|
+
return yield* Effect_exports.fail(yield* failure(response, config2.root));
|
|
39989
|
+
}
|
|
39990
|
+
result4 = yield* decode5(Deployed, response.json, `POST ${path}`);
|
|
39846
39991
|
if (t.claim && result4.claimed)
|
|
39847
39992
|
yield* removeLocalDeploy(config2.root, config2.origin);
|
|
39848
39993
|
} else if (signedIn) {
|
|
@@ -39867,6 +40012,7 @@ var deploy = Effect_exports.fnUntraced(function* (config2, opts = {}) {
|
|
|
39867
40012
|
expiresAt: Number.isFinite(expiresAt) ? expiresAt : null
|
|
39868
40013
|
});
|
|
39869
40014
|
}
|
|
40015
|
+
if (result4.shadow) yield* Console_exports.log(formatShadow(result4.shadow));
|
|
39870
40016
|
const label = result4.names?.[0] ?? result4.id;
|
|
39871
40017
|
yield* Console_exports.log(
|
|
39872
40018
|
`${label}${result4.version === void 0 ? "" : ` v${result4.version}`}: ${result4.url ?? config2.url}`
|
|
@@ -39953,7 +40099,10 @@ var dev = Effect_exports.fnUntraced(function* (config2, opts) {
|
|
|
39953
40099
|
});
|
|
39954
40100
|
|
|
39955
40101
|
// cli/src/logs.ts
|
|
39956
|
-
var
|
|
40102
|
+
var LogTimeouts = Context_exports.Reference(
|
|
40103
|
+
"eraspace/cli/LogTimeouts",
|
|
40104
|
+
{ defaultValue: () => ({ idle: Duration_exports.seconds(75), stalled: Duration_exports.seconds(30), errorBody: Duration_exports.seconds(10) }) }
|
|
40105
|
+
);
|
|
39957
40106
|
var Printed = class {
|
|
39958
40107
|
lines = [];
|
|
39959
40108
|
limit;
|
|
@@ -39994,12 +40143,14 @@ var readOnce = Effect_exports.fnUntraced(function* (config2, path, claim2, follo
|
|
|
39994
40143
|
const response = yield* request3(config2, "GET", path, { claim: claim2 }).pipe(
|
|
39995
40144
|
Effect_exports.timeoutOrElse({ duration: "30 seconds", orElse: () => Effect_exports.fail(new Transient({ message: `The platform at ${config2.origin} did not answer within 30s.` })) })
|
|
39996
40145
|
);
|
|
40146
|
+
const timeouts = yield* LogTimeouts;
|
|
40147
|
+
const errorText = response.text.pipe(Effect_exports.timeout(timeouts.errorBody), Effect_exports.orElseSucceed(() => ""));
|
|
39997
40148
|
if (isGatewayError(response.status)) {
|
|
39998
|
-
yield*
|
|
40149
|
+
yield* errorText;
|
|
39999
40150
|
return yield* new Transient({ message: `The platform at ${config2.origin} is unavailable (HTTP ${response.status}).` });
|
|
40000
40151
|
}
|
|
40001
40152
|
if (response.status >= 400) {
|
|
40002
|
-
const text2 = yield*
|
|
40153
|
+
const text2 = yield* errorText;
|
|
40003
40154
|
let json2 = { ok: false, error: text2.trim() || `HTTP ${response.status}` };
|
|
40004
40155
|
try {
|
|
40005
40156
|
json2 = JSON.parse(text2);
|
|
@@ -40007,12 +40158,15 @@ var readOnce = Effect_exports.fnUntraced(function* (config2, path, claim2, follo
|
|
|
40007
40158
|
}
|
|
40008
40159
|
return yield* Effect_exports.fail(yield* failure({ status: response.status, json: json2 }, config2.root));
|
|
40009
40160
|
}
|
|
40010
|
-
|
|
40161
|
+
const lines2 = response.stream.pipe(
|
|
40162
|
+
Stream_exports.mapError((error2) => new Transient({ message: `The log stream broke: ${describe(error2.cause ?? error2)}` })),
|
|
40163
|
+
Stream_exports.timeoutOrElse({
|
|
40164
|
+
duration: follow ? timeouts.idle : timeouts.stalled,
|
|
40165
|
+
orElse: () => Stream_exports.fail(new Transient({ message: follow ? "The log stream went quiet." : `The platform at ${config2.origin} stopped sending logs.` }))
|
|
40166
|
+
}),
|
|
40011
40167
|
Stream_exports.decodeText(),
|
|
40012
|
-
Stream_exports.splitLines
|
|
40013
|
-
Stream_exports.mapError((error2) => new Transient({ message: `The log stream broke: ${describe(error2.cause ?? error2)}` }))
|
|
40168
|
+
Stream_exports.splitLines
|
|
40014
40169
|
);
|
|
40015
|
-
if (follow) lines2 = lines2.pipe(Stream_exports.timeoutOrElse({ duration: IDLE, orElse: () => Stream_exports.fail(new Transient({ message: "The log stream went quiet." })) }));
|
|
40016
40170
|
const fresh2 = printed.connection();
|
|
40017
40171
|
yield* lines2.pipe(
|
|
40018
40172
|
Stream_exports.filter((line) => line.trim() !== ""),
|
|
@@ -40079,6 +40233,68 @@ var lifecycle = Effect_exports.fnUntraced(function* (config2, action, explicit,
|
|
|
40079
40233
|
const result4 = yield* okState(config2, "POST", `/_api/apps/${enc(t.ref)}/${action}`, { claim: t.claim });
|
|
40080
40234
|
yield* Console_exports.log(`${t.ref}: ${result4.state}`);
|
|
40081
40235
|
});
|
|
40236
|
+
var explain = Effect_exports.fnUntraced(function* (config2, fn3, inputs, json2) {
|
|
40237
|
+
const args2 = yield* parseCallArgs(inputs);
|
|
40238
|
+
const t = yield* requireTarget(config2);
|
|
40239
|
+
const report = yield* okJson(config2, "POST", `/_api/apps/${enc(t.ref)}/explain`, { body: { fn: fn3, args: args2 }, claim: t.claim });
|
|
40240
|
+
if (json2) return yield* Console_exports.log(JSON.stringify(report, null, 2));
|
|
40241
|
+
const scans = report.scans ?? [];
|
|
40242
|
+
yield* Console_exports.log(`${fn3}: read ${String(report.rowsRead)} row${report.rowsRead === 1 ? "" : "s"} in ${scans.length} scan${scans.length === 1 ? "" : "s"} and returned ${String(report.returned)}`);
|
|
40243
|
+
for (const s of scans) yield* Console_exports.log(` ${s.table} via ${s.index} (${s.mode}): read ${s.rowsRead}, kept ${s.kept} of ${s.rows}`);
|
|
40244
|
+
const suggestions = report.suggestions ?? [];
|
|
40245
|
+
if (!suggestions.length) yield* Console_exports.log(c.dim("No index suggestions."));
|
|
40246
|
+
for (const s of suggestions) yield* Console_exports.log(`${c.yellow("suggest")} ${s.message}`);
|
|
40247
|
+
});
|
|
40248
|
+
var profile = Effect_exports.fnUntraced(function* (config2, explicit, seconds2, out) {
|
|
40249
|
+
const t = yield* requireTarget(config2, explicit);
|
|
40250
|
+
yield* Console_exports.error(c.dim(`Profiling ${t.ref} for ${seconds2}s...`));
|
|
40251
|
+
const result4 = yield* okJson(config2, "POST", `/_api/apps/${enc(t.ref)}/profile`, { body: { seconds: seconds2 }, claim: t.claim, timeout: Duration_exports.seconds(seconds2 + 30) });
|
|
40252
|
+
const file2 = out ?? `${t.ref}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.cpuprofile`;
|
|
40253
|
+
const fs = yield* FileSystem_exports.FileSystem;
|
|
40254
|
+
yield* fs.writeFileString(file2, JSON.stringify(result4));
|
|
40255
|
+
const nodes = new Map((result4.nodes ?? []).map((n) => [n.id, n]));
|
|
40256
|
+
const counts = /* @__PURE__ */ new Map();
|
|
40257
|
+
let busy = 0;
|
|
40258
|
+
for (const id2 of result4.samples ?? []) {
|
|
40259
|
+
const f = nodes.get(id2)?.callFrame;
|
|
40260
|
+
if (!f || f.functionName === "(idle)" || f.functionName === "(root)") continue;
|
|
40261
|
+
busy++;
|
|
40262
|
+
const at = f.url ? ` ${f.url}:${f.lineNumber + 1}` : "";
|
|
40263
|
+
const name = `${f.functionName || "(anonymous)"}${at}`;
|
|
40264
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
40265
|
+
}
|
|
40266
|
+
const samples = (result4.samples ?? []).length;
|
|
40267
|
+
yield* Console_exports.log(`${t.ref}: busy in ${busy} of ${samples} samples. Open ${file2} in Chrome DevTools (Performance > Load profile).`);
|
|
40268
|
+
for (const [name, n] of [...counts].sort((a, b) => b[1] - a[1]).slice(0, 10)) {
|
|
40269
|
+
yield* Console_exports.log(` ${(100 * n / Math.max(busy, 1)).toFixed(1).padStart(5)}% ${name}`);
|
|
40270
|
+
}
|
|
40271
|
+
});
|
|
40272
|
+
var changes2 = Effect_exports.fnUntraced(function* (config2, since, limit) {
|
|
40273
|
+
const t = yield* requireTarget(config2);
|
|
40274
|
+
const response = yield* request3(config2, "GET", `/_api/apps/${enc(t.ref)}/changes?since=${since}&limit=${limit}`, { claim: t.claim });
|
|
40275
|
+
const text2 = yield* response.text.pipe(Effect_exports.orElseSucceed(() => ""));
|
|
40276
|
+
if (response.status >= 400) {
|
|
40277
|
+
let json2 = { ok: false, error: text2.trim() || `HTTP ${response.status}` };
|
|
40278
|
+
try {
|
|
40279
|
+
json2 = JSON.parse(text2);
|
|
40280
|
+
} catch {
|
|
40281
|
+
}
|
|
40282
|
+
return yield* Effect_exports.fail(yield* failure({ status: response.status, json: json2 }));
|
|
40283
|
+
}
|
|
40284
|
+
const lines2 = text2.split("\n").filter(Boolean);
|
|
40285
|
+
for (const line of lines2) yield* Console_exports.log(line);
|
|
40286
|
+
const head5 = Number(response.headers["x-eraspace-commit-seq"] ?? 0);
|
|
40287
|
+
const last = lines2.length ? JSON.parse(lines2.at(-1)).seq : since;
|
|
40288
|
+
const more = response.headers["x-eraspace-more"] === "1";
|
|
40289
|
+
yield* Console_exports.error(c.dim(more ? `Latest commit ${head5}; continue with --since ${last}` : `Up to date at commit ${head5}`));
|
|
40290
|
+
});
|
|
40291
|
+
var undo = Effect_exports.fnUntraced(function* (config2, to, yes) {
|
|
40292
|
+
const t = yield* requireTarget(config2);
|
|
40293
|
+
if (!yes) return yield* fail12(`eraspace undo ${to} puts every row of ${t.ref} changed after commit ${to} back as it was. Re-run with --yes.`);
|
|
40294
|
+
const result4 = yield* okJson(config2, "POST", `/_api/apps/${enc(t.ref)}/undo`, { body: { toSeq: to }, claim: t.claim });
|
|
40295
|
+
const undone = Number(result4.undone ?? 0);
|
|
40296
|
+
yield* Console_exports.log(undone ? `${t.ref}: restored ${undone} row${undone === 1 ? "" : "s"} to commit ${to} as commit ${String(result4.commitSeq)}` : `${t.ref}: nothing changed after commit ${to}`);
|
|
40297
|
+
});
|
|
40082
40298
|
var listTokens = Effect_exports.fnUntraced(function* (config2) {
|
|
40083
40299
|
const { tokens } = yield* ok(TokenList)(config2, "GET", "/_api/tokens");
|
|
40084
40300
|
if (!tokens.length) yield* Console_exports.log("No tokens.");
|
|
@@ -40111,7 +40327,7 @@ Pass each argument as JSON, for example eraspace call send '"general"' '{"text":
|
|
|
40111
40327
|
}
|
|
40112
40328
|
return Effect_exports.succeed(args2);
|
|
40113
40329
|
}
|
|
40114
|
-
var
|
|
40330
|
+
var call2 = Effect_exports.fnUntraced(function* (config2, fn3, inputs) {
|
|
40115
40331
|
const args2 = yield* parseCallArgs(inputs);
|
|
40116
40332
|
const t = yield* requireTarget(config2);
|
|
40117
40333
|
const { status, text: text2 } = yield* Effect_exports.gen(function* () {
|
|
@@ -40134,7 +40350,7 @@ import { basename as basename2, join as join7, relative as relative3, resolve as
|
|
|
40134
40350
|
var TEMPLATES = ["chat", "todo", "poll"];
|
|
40135
40351
|
var agents = (cli) => `# eraspace capsule: agent instructions
|
|
40136
40352
|
|
|
40137
|
-
This directory is one eraspace app ("capsule"): a TypeScript server
|
|
40353
|
+
This directory is one eraspace app ("capsule"): a TypeScript server running in V8, a Preact client, a
|
|
40138
40354
|
Postgres-backed database with live queries, and hosting, all through the \`eraspace\` CLI. Treat this directory as the
|
|
40139
40355
|
whole app. Build the requested app by editing the starter files, deploy it, verify it, and report the URL.
|
|
40140
40356
|
|
|
@@ -40211,13 +40427,19 @@ export default capsule({
|
|
|
40211
40427
|
\`paginate({ cursor, numItems })\`. Every table has an implicit \`by_creation\` index. Await every db call.
|
|
40212
40428
|
- Queries and actions have a read-only db. Write in mutations or writable endpoints. Actions can call
|
|
40213
40429
|
\`ctx.runQuery\`, \`ctx.runMutation\` and global \`fetch\`.
|
|
40430
|
+
- Mutations schedule later work with \`ctx.scheduler.runAfter(ms, "mutationName", ...args)\`, which returns a job id.
|
|
40431
|
+
The job commits with the mutation and its run commits once; only mutations can be scheduled.
|
|
40214
40432
|
- Handler arguments are positional and not validated. Check them in the handler.
|
|
40215
40433
|
- \`ctx.auth.userId\` is a per-connection guest id that changes on reload. It is not a login: never use it for
|
|
40216
40434
|
ownership or access control. Every app is public.
|
|
40217
40435
|
- \`ctx.log.info(msg, data?)\` (also debug/warn/error) writes to \`eraspace logs\`.
|
|
40218
40436
|
- Endpoints receive \`req\` with \`method, path, url, headers.get(), query.get(), json(), text()\` and return
|
|
40219
40437
|
\`json(value)\`, \`text(string)\`, \`empty()\`, \`redirect(url)\` or a plain value (sent as JSON).
|
|
40220
|
-
- Removing a table
|
|
40438
|
+
- Removing a populated table needs its row count confirmed: \`${cli} deploy --delete-table <table>=<rows>\` (the
|
|
40439
|
+
refused deploy prints the exact flag). Deletion runs in background batches.
|
|
40440
|
+
- Changing a field's type or making it required fails the deploy while stored rows do not match. Declare a
|
|
40441
|
+
migration instead: \`table({ ... }).migrate(1, row => ({ ...row, done: false }))\` upgrades old rows when read
|
|
40442
|
+
and rewrites them in the background. Number migrations 1, 2, 3, ... and keep each one until its rows are done.
|
|
40221
40443
|
|
|
40222
40444
|
## Client API (\`eraspace/client\`)
|
|
40223
40445
|
|
|
@@ -40226,7 +40448,7 @@ import { createClient } from "eraspace/client";
|
|
|
40226
40448
|
import { useState } from "preact/hooks";
|
|
40227
40449
|
import type app from "../server/index";
|
|
40228
40450
|
|
|
40229
|
-
const client = createClient<typeof app>(
|
|
40451
|
+
const client = createClient<typeof app>();
|
|
40230
40452
|
|
|
40231
40453
|
export default function App() {
|
|
40232
40454
|
const items = client.useQuery("items", "some-list"); // undefined while loading, then live
|
|
@@ -40251,28 +40473,59 @@ export default function App() {
|
|
|
40251
40473
|
- Realtime rooms, not stored: \`const room = client.useRoom(name, meta)\` gives \`room.presence\`,
|
|
40252
40474
|
\`room.broadcast(event, payload)\` and \`room.on(event, cb)\` (call \`on\` inside \`useEffect\` and return its
|
|
40253
40475
|
cleanup). \`client.usePresence(name, meta)\` returns just the members.
|
|
40254
|
-
- Files:
|
|
40255
|
-
Up to 5 MiB per file.
|
|
40476
|
+
- Files: see "File uploads" below.
|
|
40256
40477
|
- Pages: \`Router\`, \`Route\`, \`Link\`, \`useLocation\`, \`useNavigate\`, \`useParams\` from \`eraspace/client\`.
|
|
40478
|
+
- Open tabs reload by themselves after each deploy. Pass \`createClient({ reloadOnDeploy: false })\` to keep the
|
|
40479
|
+
page and react to \`client.onDeployed(version => ...)\` instead.
|
|
40257
40480
|
- Client imports are limited to \`eraspace/client\`, \`preact\`, \`preact/hooks\`, \`@preact/signals\` and relative files.
|
|
40258
40481
|
|
|
40259
|
-
##
|
|
40482
|
+
## File uploads
|
|
40260
40483
|
|
|
40261
|
-
|
|
40262
|
-
destructuring, spread, template literals, Map, Set, JSON, Math, Date, URL, RegExp, fetch) with these limits:
|
|
40484
|
+
Files go to the app's bucket, not the database. Save the returned \`FileRef\` in a row, then preview it.
|
|
40263
40485
|
|
|
40264
|
-
|
|
40265
|
-
|
|
40266
|
-
|
|
40267
|
-
|
|
40268
|
-
|
|
40269
|
-
|
|
40270
|
-
|
|
40271
|
-
|
|
40272
|
-
|
|
40273
|
-
|
|
40274
|
-
|
|
40275
|
-
|
|
40486
|
+
\`\`\`tsx
|
|
40487
|
+
// server/index.ts: declare the file in the schema and copy its fields in the mutation (extra fields are refused).
|
|
40488
|
+
photos: table({ caption: string(), file: object({ key: string(), name: string(), contentType: string(), size: number() }) }),
|
|
40489
|
+
addPhoto: mutation(async (ctx, caption: string, file: { key: string; name: string; contentType: string; size: number }) => {
|
|
40490
|
+
if (!/^[0-9a-f-]{36}$/.test(String(file?.key))) throw new Error("Upload the photo first.");
|
|
40491
|
+
return ctx.db.photos.insert({ caption, file: { key: file.key, name: file.name, contentType: file.contentType, size: file.size } });
|
|
40492
|
+
}),
|
|
40493
|
+
|
|
40494
|
+
// client/index.tsx
|
|
40495
|
+
import { createClient, FileUpload, FilePreview } from "eraspace/client";
|
|
40496
|
+
const addPhoto = client.useMutation("addPhoto");
|
|
40497
|
+
<FileUpload client={client} accept="image/*" onUpload={(file) => addPhoto(caption, file)} />
|
|
40498
|
+
{photos?.map((p) => <FilePreview key={p.id} client={client} file={p.file} />)}
|
|
40499
|
+
\`\`\`
|
|
40500
|
+
|
|
40501
|
+
- \`<FileUpload>\` is a drop zone and file picker with progress and error text. Props: \`accept\`, \`multiple\`,
|
|
40502
|
+
\`maxBytes\`, \`disabled\`, \`class\` (replaces its default style; \`data-dragging\` is set during a drag) and
|
|
40503
|
+
children for the label. \`onUpload(file)\` runs once per stored file; if it throws, the file is deleted again.
|
|
40504
|
+
- \`<FilePreview file={fileRef or key}>\` shows images, video and audio inline, plain text in a frame, and other
|
|
40505
|
+
types (PDF, zip...) as a download card. \`caption={false}\` hides the name/size line; \`class\` replaces its style.
|
|
40506
|
+
Pass the saved \`FileRef\`: a bare key costs a metadata request per preview. Images, video and audio uploaded
|
|
40507
|
+
from the same page show at once from the browser's copy; \`<FileUpload>\` shows an image while it uploads.
|
|
40508
|
+
- Custom UI: \`const { upload, uploading, progress, error, file } = client.useUpload({ accept, maxBytes, onUpload })\`,
|
|
40509
|
+
then \`upload(input.files)\`; \`file\` is the Blob uploading now. Lower level: \`client.storage.upload(blob, { onProgress })\`,
|
|
40510
|
+
\`.url(key)\`, \`.localUrl(key)\`, \`.info(key)\` and \`.delete(key)\`.
|
|
40511
|
+
- \`accept\` and \`maxBytes\` are checked in the browser only; the server stores any type up to its limit.
|
|
40512
|
+
- Up to 5 MiB per file and 100 MiB per app. Anyone with a key can read or delete that file; never upload secrets.
|
|
40513
|
+
|
|
40514
|
+
## Server execution
|
|
40515
|
+
|
|
40516
|
+
Server TypeScript is transformed with oxc and runs as ES modules in V8. JavaScript language features such as
|
|
40517
|
+
classes, closures, async/await, generators, getters, private fields, Proxy, Reflect and BigInt use V8 semantics.
|
|
40518
|
+
Values crossing the database or RPC boundary must be JSON-compatible.
|
|
40519
|
+
|
|
40520
|
+
- Imports support relative deployed files and \`eraspace/server\`. npm packages, Node built-ins and dynamic
|
|
40521
|
+
\`import()\` are unavailable. Apps need no installed server dependencies.
|
|
40522
|
+
- The host supplies \`fetch\`, \`URL\`, \`URLPattern\`, \`Headers\`, \`Request\`, \`Response\`, Web Streams,
|
|
40523
|
+
\`Blob\`, \`File\`, \`FormData\`, \`crypto\` (including \`crypto.subtle\`), \`TextEncoder\`, \`TextDecoder\`,
|
|
40524
|
+
\`CompressionStream\`, timers and \`console\`. Bodies arrive and leave whole rather than incrementally.
|
|
40525
|
+
- Persist state in the database. Module state is not durable and resets when an app unloads or a worker restarts.
|
|
40526
|
+
- Await database calls. Mutations run through one writer per app and reply after their Postgres commit.
|
|
40527
|
+
- Handlers have CPU, wall-clock and heap limits. Keep loops and reads bounded.
|
|
40528
|
+
- Outbound fetch is available to claimed apps in actions and read-only endpoints. Private addresses are blocked.
|
|
40276
40529
|
|
|
40277
40530
|
## Deploys without an account
|
|
40278
40531
|
|
|
@@ -40410,6 +40663,22 @@ var deployFlags = {
|
|
|
40410
40663
|
publicInspect: Flag_exports.Boolean("public-inspect").pipe(Flag_exports.withDefault(false), Flag_exports.withDescription("Let anyone read this app's inspection data and logs"))
|
|
40411
40664
|
};
|
|
40412
40665
|
var deployOptions = (input) => ({ name: value3(input.name), publicInspect: input.publicInspect });
|
|
40666
|
+
var deleteTableFlag = {
|
|
40667
|
+
deleteTable: Flag_exports.KeyValuePair("delete-table").pipe(
|
|
40668
|
+
Flag_exports.withDescription("Delete a table removed from the schema, confirming its current row count: --delete-table <table>=<rows> (repeatable)"),
|
|
40669
|
+
Flag_exports.optional
|
|
40670
|
+
)
|
|
40671
|
+
};
|
|
40672
|
+
var shadowFlags = {
|
|
40673
|
+
shadow: Flag_exports.Boolean("shadow").pipe(
|
|
40674
|
+
Flag_exports.withDefault(false),
|
|
40675
|
+
Flag_exports.withDescription("Before installing, rerun the app's live queries on the new code and report which results change or start throwing")
|
|
40676
|
+
),
|
|
40677
|
+
failOnQueryErrors: Flag_exports.Boolean("fail-on-query-errors").pipe(
|
|
40678
|
+
Flag_exports.withDefault(false),
|
|
40679
|
+
Flag_exports.withDescription("Like --shadow, but refuse the deploy if a live query that returns a value now would throw")
|
|
40680
|
+
)
|
|
40681
|
+
};
|
|
40413
40682
|
var browserFlag = {
|
|
40414
40683
|
browser: Flag_exports.Boolean("browser").pipe(Flag_exports.withDefault(true), Flag_exports.withDescription("Open the sign-in page in a browser; --no-browser only prints the URL"))
|
|
40415
40684
|
};
|
|
@@ -40431,8 +40700,19 @@ var initCommand = Command_exports.make("init", { template: templateFlag }, ({ te
|
|
|
40431
40700
|
var typecheckCommand = Command_exports.make("typecheck", {}, () => Effect_exports.flatMap(config, (c2) => typecheck(c2.root))).pipe(
|
|
40432
40701
|
Command_exports.withDescription("Typecheck server/, client/ and shared/ (no tsconfig.json or node_modules needed)")
|
|
40433
40702
|
);
|
|
40434
|
-
var deployCommand = Command_exports.make(
|
|
40435
|
-
|
|
40703
|
+
var deployCommand = Command_exports.make(
|
|
40704
|
+
"deploy",
|
|
40705
|
+
{ ...deployFlags, ...deleteTableFlag, ...shadowFlags },
|
|
40706
|
+
(input) => Effect_exports.flatMap(
|
|
40707
|
+
config,
|
|
40708
|
+
(c2) => deploy(c2, { ...deployOptions(input), deleteTables: value3(input.deleteTable), shadow: input.shadow, failOnQueryErrors: input.failOnQueryErrors })
|
|
40709
|
+
)
|
|
40710
|
+
).pipe(
|
|
40711
|
+
Command_exports.withDescription("Build and deploy. Without a login this creates a temporary unclaimed app."),
|
|
40712
|
+
Command_exports.withExamples([
|
|
40713
|
+
{ command: "eraspace deploy --delete-table drafts=12", description: "Deploy a schema without drafts, deleting its 12 rows" },
|
|
40714
|
+
{ command: "eraspace deploy --fail-on-query-errors", description: "Deploy only if no live query starts throwing on the new code" }
|
|
40715
|
+
])
|
|
40436
40716
|
);
|
|
40437
40717
|
var devCommand = Command_exports.make("dev", deployFlags, (input) => Effect_exports.flatMap(config, (c2) => dev(c2, deployOptions(input)))).pipe(
|
|
40438
40718
|
Command_exports.withDescription("Deploy, then redeploy whenever server/, client/, shared/ or .env.eraspace.server change")
|
|
@@ -40440,14 +40720,38 @@ var devCommand = Command_exports.make("dev", deployFlags, (input) => Effect_expo
|
|
|
40440
40720
|
var callCommand = Command_exports.make(
|
|
40441
40721
|
"call",
|
|
40442
40722
|
{
|
|
40443
|
-
fn: Argument_exports.String("fn").pipe(Argument_exports.withDescription("Query or
|
|
40723
|
+
fn: Argument_exports.String("fn").pipe(Argument_exports.withDescription("Query, mutation or action name")),
|
|
40444
40724
|
args: Argument_exports.String("json").pipe(Argument_exports.withDescription("Arguments, each one a JSON value"), Argument_exports.variadic())
|
|
40445
40725
|
},
|
|
40446
|
-
({ fn: fn3, args: args2 }) => Effect_exports.flatMap(config, (c2) =>
|
|
40726
|
+
({ fn: fn3, args: args2 }) => Effect_exports.flatMap(config, (c2) => call2(c2, fn3, args2))
|
|
40447
40727
|
).pipe(
|
|
40448
40728
|
Command_exports.withDescription("Call a handler with positional JSON arguments"),
|
|
40449
40729
|
Command_exports.withExamples([{ command: `eraspace call messages '"general"'`, description: "Strings are JSON too, so quote them" }])
|
|
40450
40730
|
);
|
|
40731
|
+
var profileCommand = Command_exports.make(
|
|
40732
|
+
"profile",
|
|
40733
|
+
{
|
|
40734
|
+
app: optionalArg("app", "App name or id (default: this checkout's app)"),
|
|
40735
|
+
seconds: Flag_exports.Int("seconds").pipe(Flag_exports.filter((n) => n >= 1 && n <= 60, (n) => `--seconds must be between 1 and 60, got ${n}`), Flag_exports.withDefault(10), Flag_exports.withDescription("How long to sample")),
|
|
40736
|
+
out: optionalString("out", "Where to write the .cpuprofile (default: <app>-<time>.cpuprofile)")
|
|
40737
|
+
},
|
|
40738
|
+
({ app, seconds: seconds2, out }) => Effect_exports.flatMap(config, (c2) => profile(c2, value3(app), seconds2, value3(out)))
|
|
40739
|
+
).pipe(
|
|
40740
|
+
Command_exports.withDescription("Sample the app's server CPU time and write a .cpuprofile that opens in Chrome DevTools against the TypeScript sources"),
|
|
40741
|
+
Command_exports.withExamples([{ command: "eraspace profile my-app --seconds 10", description: "Profile while you exercise the app" }])
|
|
40742
|
+
);
|
|
40743
|
+
var explainCommand = Command_exports.make(
|
|
40744
|
+
"explain",
|
|
40745
|
+
{
|
|
40746
|
+
fn: Argument_exports.String("fn").pipe(Argument_exports.withDescription("Query name")),
|
|
40747
|
+
args: Argument_exports.String("json").pipe(Argument_exports.withDescription("Arguments, each one a JSON value"), Argument_exports.variadic()),
|
|
40748
|
+
json: Flag_exports.Boolean("json").pipe(Flag_exports.withDefault(false), Flag_exports.withDescription("Print the full report as JSON"))
|
|
40749
|
+
},
|
|
40750
|
+
({ fn: fn3, args: args2, json: json2 }) => Effect_exports.flatMap(config, (c2) => explain(c2, fn3, args2, json2))
|
|
40751
|
+
).pipe(
|
|
40752
|
+
Command_exports.withDescription("Run a query once and suggest indexes for rows it filters in JavaScript"),
|
|
40753
|
+
Command_exports.withExamples([{ command: `eraspace explain todosByStatus '"done"'`, description: "Strings are JSON too, so quote them" }])
|
|
40754
|
+
);
|
|
40451
40755
|
var logsCommand = Command_exports.make(
|
|
40452
40756
|
"logs",
|
|
40453
40757
|
{
|
|
@@ -40492,15 +40796,40 @@ var inspectionCommand = Command_exports.make(
|
|
|
40492
40796
|
},
|
|
40493
40797
|
({ visibility, app }) => Effect_exports.flatMap(config, (c2) => inspection(c2, visibility, value3(app)))
|
|
40494
40798
|
).pipe(Command_exports.withDescription("Open or close inspection and logs to everyone"));
|
|
40495
|
-
var
|
|
40496
|
-
var lifecycleCommand = (action, description) => Command_exports.make(
|
|
40497
|
-
|
|
40799
|
+
var lifecycleApp = optionalArg("app", "App name or id (default: this checkout's app)");
|
|
40800
|
+
var lifecycleCommand = (action, description) => Command_exports.make(action, { app: lifecycleApp }, ({ app }) => Effect_exports.flatMap(config, (c2) => lifecycle(c2, action, value3(app), false))).pipe(
|
|
40801
|
+
Command_exports.withDescription(description)
|
|
40802
|
+
);
|
|
40803
|
+
var changesCommand = Command_exports.make(
|
|
40804
|
+
"changes",
|
|
40498
40805
|
{
|
|
40499
|
-
|
|
40500
|
-
|
|
40806
|
+
since: Flag_exports.Int("since").pipe(Flag_exports.filter((n) => n >= 0, (n) => `--since must be a commit number, got ${n}`), Flag_exports.withDefault(0), Flag_exports.withDescription("Print changes committed after this commit")),
|
|
40807
|
+
limit: Flag_exports.Int("limit").pipe(Flag_exports.filter((n) => n >= 1 && n <= 1e4, (n) => `--limit must be between 1 and 10000, got ${n}`), Flag_exports.withDefault(1e3), Flag_exports.withDescription("Rows to print at most (whole commits)"))
|
|
40808
|
+
},
|
|
40809
|
+
({ since, limit }) => Effect_exports.flatMap(config, (c2) => changes2(c2, since, limit))
|
|
40810
|
+
).pipe(
|
|
40811
|
+
Command_exports.withDescription("Print recent committed row changes as NDJSON (the change log keeps 24 hours by default)"),
|
|
40812
|
+
Command_exports.withExamples([{ command: "eraspace changes --since 120 | jq .", description: "Changes committed after commit 120" }])
|
|
40813
|
+
);
|
|
40814
|
+
var undoCommand = Command_exports.make(
|
|
40815
|
+
"undo",
|
|
40816
|
+
{
|
|
40817
|
+
to: Argument_exports.Int("commit").pipe(Argument_exports.withDescription("Commit number to restore rows to")),
|
|
40818
|
+
yes: Flag_exports.Boolean("yes").pipe(Flag_exports.withAlias("y"), Flag_exports.withDefault(false), Flag_exports.withDescription("Confirm the undo"))
|
|
40819
|
+
},
|
|
40820
|
+
({ to, yes }) => Effect_exports.flatMap(config, (c2) => undo(c2, to, yes))
|
|
40821
|
+
).pipe(
|
|
40822
|
+
Command_exports.withDescription("Put every row changed after a commit back as it was; needs --yes. The undo is a commit too, so it can be undone"),
|
|
40823
|
+
Command_exports.withExamples([{ command: "eraspace undo 120 --yes", description: "Revert everything committed after commit 120" }])
|
|
40824
|
+
);
|
|
40825
|
+
var terminateCommand = Command_exports.make(
|
|
40826
|
+
"terminate",
|
|
40827
|
+
{
|
|
40828
|
+
app: lifecycleApp,
|
|
40829
|
+
yes: Flag_exports.Boolean("yes").pipe(Flag_exports.withAlias("y"), Flag_exports.withDefault(false), Flag_exports.withDescription("Confirm that the app stops permanently"))
|
|
40501
40830
|
},
|
|
40502
|
-
({ app, yes }) => Effect_exports.flatMap(config, (c2) => lifecycle(c2,
|
|
40503
|
-
).pipe(Command_exports.withDescription(
|
|
40831
|
+
({ app, yes }) => Effect_exports.flatMap(config, (c2) => lifecycle(c2, "terminate", value3(app), yes))
|
|
40832
|
+
).pipe(Command_exports.withDescription("Stop permanently (data kept, no more deploys); needs --yes"));
|
|
40504
40833
|
var tokenCommand = Command_exports.make("token").pipe(
|
|
40505
40834
|
Command_exports.withDescription("Create, list and revoke API tokens"),
|
|
40506
40835
|
Command_exports.withSubcommands([
|
|
@@ -40534,6 +40863,8 @@ var command = eraspace.pipe(
|
|
|
40534
40863
|
deployCommand,
|
|
40535
40864
|
devCommand,
|
|
40536
40865
|
callCommand,
|
|
40866
|
+
explainCommand,
|
|
40867
|
+
profileCommand,
|
|
40537
40868
|
logsCommand,
|
|
40538
40869
|
inspectCommand,
|
|
40539
40870
|
authCommand,
|
|
@@ -40543,7 +40874,9 @@ var command = eraspace.pipe(
|
|
|
40543
40874
|
inspectionCommand,
|
|
40544
40875
|
lifecycleCommand("archive", "Stop serving and keep the data"),
|
|
40545
40876
|
lifecycleCommand("restore", "Serve an archived app again"),
|
|
40546
|
-
|
|
40877
|
+
terminateCommand,
|
|
40878
|
+
changesCommand,
|
|
40879
|
+
undoCommand,
|
|
40547
40880
|
tokenCommand,
|
|
40548
40881
|
...hidden
|
|
40549
40882
|
])
|