querysub 0.152.0 → 0.154.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/package.json +6 -6
- package/src/-b-authorities/cloudflareHelpers.ts +11 -2
- package/src/3-path-functions/PathFunctionRunner.ts +168 -97
- package/src/3-path-functions/PathFunctionRunnerMain.ts +8 -2
- package/src/3-path-functions/pathFunctionLoader.ts +11 -6
- package/src/3-path-functions/syncSchema.ts +10 -1
- package/src/4-deploy/edgeBootstrap.ts +10 -1
- package/src/4-querysub/Querysub.ts +77 -3
- package/src/4-querysub/QuerysubController.ts +22 -2
- package/src/4-querysub/permissions.ts +33 -2
- package/src/4-querysub/querysubPrediction.ts +52 -18
- package/src/archiveapps/archiveGCEntry.tsx +38 -0
- package/src/archiveapps/archiveJoinEntry.ts +121 -0
- package/src/archiveapps/archiveMergeEntry.tsx +47 -0
- package/src/archiveapps/compressTest.tsx +59 -0
- package/src/archiveapps/lockTest.ts +127 -0
- package/src/config.ts +5 -0
- package/src/diagnostics/managementPages.tsx +55 -0
- package/src/diagnostics/misc-pages/ArchiveInspect.tsx +325 -0
- package/src/diagnostics/misc-pages/ArchiveViewer.tsx +781 -0
- package/src/diagnostics/misc-pages/ArchiveViewerTable.tsx +156 -0
- package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +573 -0
- package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +129 -0
- package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +431 -0
- package/src/diagnostics/misc-pages/RequireAuditPage.tsx +218 -0
- package/src/diagnostics/misc-pages/SnapshotViewer.tsx +206 -0
- package/src/diagnostics/misc-pages/TimeRangeView.tsx +648 -0
- package/src/diagnostics/misc-pages/archiveViewerFilter.tsx +221 -0
- package/src/diagnostics/misc-pages/archiveViewerShared.tsx +76 -0
- package/src/email/postmark.tsx +40 -0
- package/src/email/sendgrid.tsx +44 -0
- package/src/functional/UndoWatch.tsx +133 -0
- package/src/functional/diff.ts +858 -0
- package/src/functional/promiseCache.ts +67 -0
- package/src/functional/random.ts +9 -0
- package/src/functional/runCommand.ts +42 -0
- package/src/functional/runOnce.ts +7 -0
- package/src/functional/stats.ts +61 -0
- package/src/functional/throttleRerender.tsx +80 -0
- package/src/library-components/AspectSizedComponent.tsx +88 -0
- package/src/library-components/Histogram.tsx +338 -0
- package/src/library-components/InlinePopup.tsx +67 -0
- package/src/library-components/Notifications.tsx +153 -0
- package/src/library-components/RenderIfVisible.tsx +80 -0
- package/src/library-components/SimpleNotification.tsx +133 -0
- package/src/library-components/TabbedUI.tsx +39 -0
- package/src/library-components/animateAnyElement.tsx +65 -0
- package/src/library-components/errorNotifications.tsx +81 -0
- package/src/library-components/placeholder.ts +18 -0
- package/src/misc/format2.ts +48 -0
- package/src/misc.ts +33 -0
- package/src/misc2.ts +5 -0
- package/src/server.ts +2 -1
- package/src/storage/diskCache.ts +227 -0
- package/src/storage/diskCache2.ts +122 -0
- package/src/storage/fileSystemPointer.ts +72 -0
- package/src/user-implementation/LoginPage.tsx +78 -0
- package/src/user-implementation/RequireAuditPage.tsx +219 -0
- package/src/user-implementation/SecurityPage.tsx +212 -0
- package/src/user-implementation/UserPage.tsx +320 -0
- package/src/user-implementation/addSuperUser.ts +21 -0
- package/src/user-implementation/canSeeSource.ts +41 -0
- package/src/user-implementation/loginEmail.tsx +159 -0
- package/src/user-implementation/setEmailKey.ts +20 -0
- package/src/user-implementation/userData.ts +974 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.154.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
|
|
25
25
|
"pako": "^2.1.0",
|
|
26
26
|
"preact": "^10.11.3",
|
|
27
|
-
"socket-function": "^0.
|
|
27
|
+
"socket-function": "^0.87.0",
|
|
28
28
|
"terser": "^5.31.0",
|
|
29
29
|
"typesafecss": "^0.6.3",
|
|
30
30
|
"yaml": "^2.5.0",
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"server": "yarn typenode ./src/server.ts",
|
|
38
38
|
"serversharded": "yarn server --authority ./pathremain.json & yarn server --authority ./patha.json & yarn server --authority ./pathb.json & yarn server --authority ./pathc.json & yarn server --authority ./pathd.json",
|
|
39
|
-
"function": "yarn typenode ./src/3-path-functions/PathFunctionRunnerMain.ts --function --local
|
|
40
|
-
"deploy": "yarn typenode ./src/4-deploy/deployMain.ts --domain querysub.com --local
|
|
39
|
+
"function": "yarn typenode ./src/3-path-functions/PathFunctionRunnerMain.ts --function --local",
|
|
40
|
+
"deploy": "yarn typenode ./src/4-deploy/deployMain.ts --domain querysub.com --local",
|
|
41
41
|
"type": "yarn tsc --noEmit",
|
|
42
42
|
"depend": "yarn --silent depcruise src --include-only \"^src\" --config --output-type dot | dot -T svg > dependency-graph.svg",
|
|
43
|
-
"test": "yarn typenode ./src/test/test.tsx --local
|
|
44
|
-
"test2": "yarn typenode ./src/4-dom/qreactTest.tsx --local
|
|
43
|
+
"test": "yarn typenode ./src/test/test.tsx --local",
|
|
44
|
+
"test2": "yarn typenode ./src/4-dom/qreactTest.tsx --local"
|
|
45
45
|
},
|
|
46
46
|
"bin": {
|
|
47
47
|
"querysub-deploy": "./bin/deploy.js",
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { httpsRequest } from "socket-function/src/https";
|
|
2
2
|
import { getArchives } from "../-a-archives/archives";
|
|
3
3
|
import { lazy } from "socket-function/src/caching";
|
|
4
|
-
|
|
4
|
+
import { getStorageDir, getSubFolder } from "../fs";
|
|
5
|
+
import fs from "fs";
|
|
5
6
|
export const keys = getArchives("keys");
|
|
6
7
|
|
|
7
8
|
export const getCloudflareCreds = lazy(async (): Promise<{ key: string; email: string }> => {
|
|
8
9
|
let credsJSON = await keys.get("cloudflare.json");
|
|
9
|
-
if (!credsJSON)
|
|
10
|
+
if (!credsJSON) {
|
|
11
|
+
let localPath = getStorageDir() + "cloudflare.json";
|
|
12
|
+
if (fs.existsSync(localPath)) {
|
|
13
|
+
credsJSON = fs.readFileSync(localPath);
|
|
14
|
+
await keys.set("cloudflare.json", credsJSON);
|
|
15
|
+
} else {
|
|
16
|
+
throw new Error(`b2:/keys/cloudflare.json is missing. It should contain { "key": "your-key", "email": "your-email" }`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
10
19
|
return JSON.parse(credsJSON.toString());
|
|
11
20
|
});
|
|
12
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { delay, runInfinitePoll } from "socket-function/src/batching";
|
|
2
2
|
import { cache } from "socket-function/src/caching";
|
|
3
3
|
import { blue, magenta, red, yellow } from "socket-function/src/formatting/logColors";
|
|
4
|
-
import { sha256HashBuffer, timeInHour } from "socket-function/src/misc";
|
|
4
|
+
import { sha256HashBuffer, timeInHour, timeInSecond } from "socket-function/src/misc";
|
|
5
5
|
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
6
6
|
import { getLowUint32, getShortNumber } from "../bits";
|
|
7
7
|
import { registerDynamicResource } from "../diagnostics/trackResources";
|
|
@@ -15,14 +15,15 @@ import { __getRoutingHash, authorityStorage, compareTime, debugTime, epochTime,
|
|
|
15
15
|
import { getModuleFromSpec, watchModuleHotreloads } from "./pathFunctionLoader";
|
|
16
16
|
import debugbreak from "debugbreak";
|
|
17
17
|
import { parseArgs } from "./PathFunctionHelpers";
|
|
18
|
-
import { PERMISSIONS_FUNCTION_ID, getExportPath } from "./syncSchema";
|
|
18
|
+
import { PERMISSIONS_FUNCTION_ID, getAllDevelopmentModulesIds, getDevelopmentModule, getExportPath, getModuleRelativePath, getSchemaObject } from "./syncSchema";
|
|
19
19
|
import { formatTime } from "socket-function/src/formatting/format";
|
|
20
20
|
import { getControllerNodeIdList, set_debug_getFunctionRunnerShards } from "../-g-core-values/NodeCapabilities";
|
|
21
21
|
import { diskLog } from "../diagnostics/logs/diskLogger";
|
|
22
22
|
import { FilterSelector, Filterable, doesMatch } from "../misc/filterable";
|
|
23
23
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
24
24
|
import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
|
|
25
|
-
import { isLocal } from "../config";
|
|
25
|
+
import { getDomain, isLocal } from "../config";
|
|
26
|
+
import { getGitRef, getGitURL } from "../4-deploy/git";
|
|
26
27
|
|
|
27
28
|
export const functionSchema = rawSchema<{
|
|
28
29
|
[domainName: string]: {
|
|
@@ -220,7 +221,6 @@ export class PathFunctionRunner {
|
|
|
220
221
|
Querysub.keys(functionSchema()[domainName].PathFunctionRunner[moduleId].Results, fullFraction);
|
|
221
222
|
});
|
|
222
223
|
|
|
223
|
-
let callsToCall = new Map<string, { call: CallSpec; functionSpec: FunctionSpec }>();
|
|
224
224
|
let runningCalls = new Set<string>();
|
|
225
225
|
|
|
226
226
|
let callIdsToUnwatch = new Set<{ filePath: string; callId: string; }>();
|
|
@@ -258,89 +258,106 @@ export class PathFunctionRunner {
|
|
|
258
258
|
watcher.pendingWatches.paths.delete(getProxyPath(() => data()[filePath].Results[callId]));
|
|
259
259
|
}
|
|
260
260
|
callIdsToUnwatch.clear();
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
|
|
262
|
+
let moduleIds = Object.keys(data());
|
|
263
|
+
if (isLocal()) {
|
|
264
|
+
moduleIds.push(...getAllDevelopmentModulesIds());
|
|
265
|
+
moduleIds = Array.from(new Set(moduleIds));
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
let triggerPaths = new Set<string>();
|
|
266
|
-
for (let
|
|
267
|
-
|
|
269
|
+
for (let moduleId of moduleIds) {
|
|
270
|
+
watchModuleCalls(moduleId);
|
|
271
|
+
|
|
272
|
+
let callPath = getProxyPath(() => data()[moduleId].Calls);
|
|
268
273
|
triggerPaths.add(callPath);
|
|
269
274
|
|
|
270
275
|
// ALSO, Result changes (result rejections), can trigger a run
|
|
271
|
-
let resultPath = getProxyPath(() => data()[
|
|
276
|
+
let resultPath = getProxyPath(() => data()[moduleId].Results);
|
|
272
277
|
triggerPaths.add(resultPath);
|
|
273
278
|
}
|
|
274
279
|
|
|
275
|
-
let callPathLength =
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
let callPathLength = (
|
|
281
|
+
1 // domain
|
|
282
|
+
+ 1 // PathFunctionRunner
|
|
283
|
+
+ 1 // module
|
|
284
|
+
+ 1 // Calls
|
|
285
|
+
+ 1 // callId
|
|
286
|
+
);
|
|
279
287
|
|
|
280
288
|
let newCalls: { call: CallSpec; functionSpec: FunctionSpec }[] = [];
|
|
281
289
|
|
|
282
290
|
if (watcher.triggeredByChanges) {
|
|
283
291
|
for (let path of watcher.triggeredByChanges.paths) {
|
|
284
|
-
let
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
292
|
+
let callsOrResult = getPathIndex(path, 3);
|
|
293
|
+
if (callsOrResult !== "Calls" && callsOrResult !== "Results") continue;
|
|
294
|
+
if (getPathIndex(path, 1) !== "PathFunctionRunner") continue;
|
|
295
|
+
if (getPathIndex(path, 0) !== domainName) continue;
|
|
296
|
+
|
|
297
|
+
const callId = getPathIndex(path, 4);
|
|
298
|
+
if (!callId) continue;
|
|
299
|
+
let moduleId = getPathIndex(path, 2);
|
|
300
|
+
if (!moduleId) continue;
|
|
301
|
+
if (!moduleIds.includes(moduleId)) continue;
|
|
302
|
+
|
|
303
|
+
let moduleData = data()[moduleId];
|
|
304
|
+
|
|
305
|
+
let result = atomicObjectRead(moduleData.Results[callId]);
|
|
306
|
+
if (result) continue;
|
|
307
|
+
|
|
308
|
+
let callData = atomicObjectRead(moduleData.Calls[callId]);
|
|
309
|
+
if (!callData) continue;
|
|
310
|
+
|
|
311
|
+
// Ignore invalid data
|
|
312
|
+
if (callData.DomainName !== domainName) continue;
|
|
313
|
+
if (callData.ModuleId !== moduleId) continue;
|
|
314
|
+
if (callData.CallId !== callId) continue;
|
|
315
|
+
|
|
316
|
+
const functionId = callData.FunctionId;
|
|
317
|
+
let functionSpec = atomicObjectRead(moduleData.Sources[functionId]);
|
|
318
|
+
if (!functionSpec) {
|
|
319
|
+
if (!isSynced(moduleData.Sources[callData.FunctionId])) {
|
|
310
320
|
continue;
|
|
311
321
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (runningCalls.has(callId)) continue;
|
|
319
|
-
runningCalls.add(callId);
|
|
320
|
-
|
|
321
|
-
let limitCount = queueLimitCounts.get(callId) || 0;
|
|
322
|
-
limitCount++;
|
|
323
|
-
queueLimitCounts.set(callId, limitCount);
|
|
324
|
-
// NOTE: Calls are event writes, so... they should just clean themselves up, after we ignore them for long enough.
|
|
325
|
-
if (limitCount >= MAX_QUEUE_COUNT) {
|
|
326
|
-
// Only error the first time, as we don't need need that many errors
|
|
327
|
-
if (limitCount === MAX_QUEUE_COUNT) {
|
|
328
|
-
console.error(`Queue limit reached (${limitCount}) for ${getDebugName(callData, functionSpec, true)}`);
|
|
329
|
-
}
|
|
322
|
+
if (isLocal()) {
|
|
323
|
+
functionSpec = getDevFunctionSpecFromCall(callData);
|
|
324
|
+
if (!functionSpec) continue;
|
|
325
|
+
} else {
|
|
326
|
+
console.warn(yellow(`Cannot call ${callData.DomainName}.${callData.ModuleId}.Sources.${callData.FunctionId} because it is not deployed`));
|
|
330
327
|
continue;
|
|
331
328
|
}
|
|
329
|
+
}
|
|
332
330
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
331
|
+
// If we haven't synced the result, we can't know if it is ready or not
|
|
332
|
+
// (we'll run anyways once it is synced). Most other parts are immutable,
|
|
333
|
+
// but the result is by definition not, so... it is one of the values we check!
|
|
334
|
+
if (!isSynced(moduleData.Results[callId])) continue;
|
|
335
|
+
|
|
336
|
+
if (runningCalls.has(callId)) continue;
|
|
337
|
+
runningCalls.add(callId);
|
|
338
|
+
|
|
339
|
+
let limitCount = queueLimitCounts.get(callId) || 0;
|
|
340
|
+
limitCount++;
|
|
341
|
+
queueLimitCounts.set(callId, limitCount);
|
|
342
|
+
// NOTE: Calls are event writes, so... they should just clean themselves up, after we ignore them for long enough.
|
|
343
|
+
if (limitCount >= MAX_QUEUE_COUNT) {
|
|
344
|
+
// Only error the first time, as we don't need need that many errors
|
|
345
|
+
if (limitCount === MAX_QUEUE_COUNT) {
|
|
346
|
+
console.error(`Queue limit reached (${limitCount}) for ${getDebugName(callData, functionSpec, true)}`);
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (PathFunctionRunner.DEBUG_CALLS) {
|
|
352
|
+
console.log(`QUEUING ${getDebugName(callData, functionSpec, true)}`);
|
|
353
|
+
let resultsPath = getProxyPath(() => moduleData.Results[callId]);
|
|
354
|
+
let history = authorityStorage.getValuePlusHistory(resultsPath);
|
|
355
|
+
console.log(` History: ${history.length}`);
|
|
356
|
+
for (let { valid, time, canGCValue } of history) {
|
|
357
|
+
console.log(` ${valid ? "✅" : "❌"} ${debugTime(time)} ${canGCValue ? "value is undefined" : ""}`);
|
|
341
358
|
}
|
|
342
|
-
newCalls.push({ call: callData, functionSpec });
|
|
343
359
|
}
|
|
360
|
+
newCalls.push({ call: callData, functionSpec });
|
|
344
361
|
}
|
|
345
362
|
}
|
|
346
363
|
|
|
@@ -368,6 +385,27 @@ export class PathFunctionRunner {
|
|
|
368
385
|
});
|
|
369
386
|
registerDynamicResource("paths|PathFunctionRunner.watches", () => watcher.lastWatches.paths.size);
|
|
370
387
|
registerDynamicResource("paths|PathFunctionRunner.outstandingCalls", () => outstandingCalls);
|
|
388
|
+
|
|
389
|
+
// If local, periodically check all modules
|
|
390
|
+
// NOTE: `yarn deploy` commits the moduleIds to the database, but calls do not, as this would require either an extra
|
|
391
|
+
// read, or an extra write. So this is somewhat a hack, but also fairly safe.
|
|
392
|
+
if (isLocal()) {
|
|
393
|
+
let prevModuleIds = new Set<string>();
|
|
394
|
+
runInfinitePoll(timeInSecond * 15, () => {
|
|
395
|
+
let moduleIds = getAllDevelopmentModulesIds();
|
|
396
|
+
let changed = false;
|
|
397
|
+
for (let moduleId of moduleIds) {
|
|
398
|
+
if (!prevModuleIds.has(moduleId)) {
|
|
399
|
+
changed = true;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
prevModuleIds = new Set(moduleIds);
|
|
404
|
+
if (changed) {
|
|
405
|
+
watcher.explicitlyTrigger();
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
}
|
|
371
409
|
}
|
|
372
410
|
|
|
373
411
|
|
|
@@ -520,43 +558,48 @@ export class PathFunctionRunner {
|
|
|
520
558
|
let syncedModule = skipPermissions(() =>
|
|
521
559
|
functionSchema()[callPath.DomainName].PathFunctionRunner[callPath.ModuleId]
|
|
522
560
|
);
|
|
523
|
-
let syncedSpec = skipPermissions(() =>
|
|
524
|
-
atomicObjectRead(syncedModule.Sources[callPath.FunctionId])
|
|
525
|
-
);
|
|
526
|
-
if (!syncedSpec) {
|
|
527
|
-
throw new Error(`Function spec not found for ${getDebugName(callPath, functionSpec, true)}`);
|
|
528
|
-
}
|
|
529
561
|
|
|
530
|
-
//
|
|
531
|
-
if
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
562
|
+
// Make sure we are running the latest function (by checking it here, we also lock the values,
|
|
563
|
+
// so if it is updated we atomically rerun / reject all calls which were racing with us).
|
|
564
|
+
if (!isLocal()) {
|
|
565
|
+
let syncedSpec = skipPermissions(() =>
|
|
566
|
+
atomicObjectRead(syncedModule.Sources[callPath.FunctionId])
|
|
567
|
+
);
|
|
568
|
+
if (!syncedSpec) {
|
|
569
|
+
throw new Error(`Function spec not found for ${getDebugName(callPath, functionSpec, true)}`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// (We also need to depend on the RIGHT function spec).
|
|
573
|
+
if (
|
|
574
|
+
syncedSpec && (
|
|
575
|
+
syncedSpec.DomainName !== functionSpec.DomainName
|
|
576
|
+
|| syncedSpec.ModuleId !== functionSpec.ModuleId
|
|
577
|
+
|| syncedSpec.FilePath !== functionSpec.FilePath
|
|
578
|
+
|| syncedSpec.FunctionId !== functionSpec.FunctionId
|
|
579
|
+
|| syncedSpec.exportPathStr !== functionSpec.exportPathStr
|
|
580
|
+
|| syncedSpec.FilePath !== functionSpec.FilePath
|
|
581
|
+
|| syncedSpec.gitRef !== functionSpec.gitRef
|
|
582
|
+
|| syncedSpec.gitURL !== functionSpec.gitURL
|
|
583
|
+
)
|
|
584
|
+
) {
|
|
585
|
+
isFunctionSpecOutdated = true;
|
|
586
|
+
functionSpec = {
|
|
587
|
+
DomainName: syncedSpec.DomainName,
|
|
588
|
+
ModuleId: syncedSpec.ModuleId,
|
|
589
|
+
FilePath: syncedSpec.FilePath,
|
|
590
|
+
FunctionId: syncedSpec.FunctionId,
|
|
591
|
+
exportPathStr: syncedSpec.exportPathStr,
|
|
592
|
+
gitRef: syncedSpec.gitRef,
|
|
593
|
+
gitURL: syncedSpec.gitURL,
|
|
594
|
+
};
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
554
597
|
}
|
|
555
598
|
|
|
556
599
|
let evalTimeStart = Date.now();
|
|
557
600
|
try {
|
|
558
601
|
let args = parseArgs(callPath);
|
|
559
|
-
overrideCurrentCall({ spec: callPath, fnc:
|
|
602
|
+
overrideCurrentCall({ spec: callPath, fnc: functionSpec, }, () => {
|
|
560
603
|
baseFunction(...args);
|
|
561
604
|
});
|
|
562
605
|
} finally {
|
|
@@ -671,6 +714,34 @@ export async function preloadFunctions(specs: FunctionSpec[]) {
|
|
|
671
714
|
}));
|
|
672
715
|
}
|
|
673
716
|
|
|
717
|
+
export function getDevFunctionSpecFromCall(call: {
|
|
718
|
+
DomainName: string;
|
|
719
|
+
ModuleId: string;
|
|
720
|
+
FunctionId: string;
|
|
721
|
+
}): FunctionSpec | undefined {
|
|
722
|
+
let domainName = call.DomainName;
|
|
723
|
+
let moduleId = call.ModuleId;
|
|
724
|
+
let functionId = call.FunctionId;
|
|
725
|
+
|
|
726
|
+
let devModule = getDevelopmentModule(moduleId);
|
|
727
|
+
if (!devModule) {
|
|
728
|
+
console.warn(yellow(`Cannot call ${domainName}.${moduleId}.Sources.${functionId} because the module is not deployed and not referenced in deploy.ts`));
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
let filePath = getModuleRelativePath(devModule);
|
|
732
|
+
let gitURL = getGitURL();
|
|
733
|
+
let gitRef = getGitRef();
|
|
734
|
+
return {
|
|
735
|
+
DomainName: domainName,
|
|
736
|
+
ModuleId: moduleId,
|
|
737
|
+
FunctionId: functionId,
|
|
738
|
+
exportPathStr: getExportPath(functionId),
|
|
739
|
+
FilePath: filePath,
|
|
740
|
+
gitURL,
|
|
741
|
+
gitRef,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
674
745
|
class FunctionPreloaderBase {
|
|
675
746
|
async preloadFunctions(specs: FunctionSpec[]) {
|
|
676
747
|
if (isLocal()) return;
|
|
@@ -22,13 +22,13 @@ import { PathValueProxyWatcher } from "../2-proxy/PathValueProxyWatcher";
|
|
|
22
22
|
import { PermissionsCheck } from "../4-querysub/permissions";
|
|
23
23
|
import { Querysub } from "../4-querysub/QuerysubController";
|
|
24
24
|
import { timeInMinute } from "socket-function/src/misc";
|
|
25
|
-
import { getDomain, isPublic } from "../config";
|
|
25
|
+
import { devDebugbreak, getDomain, isLocal, isPublic } from "../config";
|
|
26
26
|
import { publishMachineARecords } from "../-e-certs/EdgeCertController";
|
|
27
27
|
import { green } from "socket-function/src/formatting/logColors";
|
|
28
28
|
import { parseFilterSelector } from "../misc/filterable";
|
|
29
|
+
import path from "path";
|
|
29
30
|
|
|
30
31
|
async function main() {
|
|
31
|
-
|
|
32
32
|
listenOnDebugger("FunctionRunner");
|
|
33
33
|
Error.stackTraceLimit = 20;
|
|
34
34
|
|
|
@@ -68,5 +68,11 @@ async function main() {
|
|
|
68
68
|
PermissionsChecker: PermissionsCheck,
|
|
69
69
|
filterSelector,
|
|
70
70
|
});
|
|
71
|
+
|
|
72
|
+
if (isLocal()) {
|
|
73
|
+
// We have to import deploy, otherwise local moduleIds won't be known about
|
|
74
|
+
let deployPath = path.resolve("./deploy.ts");
|
|
75
|
+
await import(deployPath);
|
|
76
|
+
}
|
|
71
77
|
}
|
|
72
78
|
logErrors(main());
|
|
@@ -63,14 +63,15 @@ const getLocalPathRemapping = lazy((): { [gitUrl: string]: string } => {
|
|
|
63
63
|
let packageJSONObj = JSON.parse(packageJSON);
|
|
64
64
|
let repo = packageJSONObj.repository as { type: string, url: string } | undefined;
|
|
65
65
|
if (!repo) throw new Error(`No "repository" property in package.json at ${path}`);
|
|
66
|
-
if (repo.type !== "git") {
|
|
67
|
-
throw new Error(`Repository type ${JSON.stringify(repo.type)} is not supported yet. Only "git"
|
|
66
|
+
if (repo.type !== "git" && repo.type !== "git+https" && repo.type !== "https") {
|
|
67
|
+
throw new Error(`Repository type ${JSON.stringify(repo.type)} is not supported yet. Only "git", "git+https", and "https" are presently supported.`);
|
|
68
68
|
}
|
|
69
69
|
mapping[repo.url] = path;
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
// https://github.com/sliftist/qs-cyoa.git
|
|
71
|
+
// git@github.com:sliftist/qs-cyoa.git
|
|
72
|
+
mapping[repo.url.replace(/^https:\/\/([^\/]+)\//, "git@$1:")] = path;
|
|
73
|
+
// Go from git to https as well, as it might be https
|
|
74
|
+
mapping[repo.url.replace(/^git@([^:]+):(.+)$/, "https://$1/$2")] = path;
|
|
74
75
|
}
|
|
75
76
|
return mapping;
|
|
76
77
|
});
|
|
@@ -131,6 +132,10 @@ let moduleResolver = async (spec: {
|
|
|
131
132
|
gitURL: string;
|
|
132
133
|
gitRef: string;
|
|
133
134
|
}) => {
|
|
135
|
+
if (isLocal()) {
|
|
136
|
+
// Probably a bug. The local path should have matched.
|
|
137
|
+
devDebugbreak();
|
|
138
|
+
}
|
|
134
139
|
let gitURL = spec.gitURL;
|
|
135
140
|
let urlForPath = gitURL;
|
|
136
141
|
|
|
@@ -5,7 +5,7 @@ import { Args } from "socket-function/src/types";
|
|
|
5
5
|
import { appendToPathStr, getPathFromStr, getPathStr, rootPathStr } from "../path";
|
|
6
6
|
import { writeFunctionCall } from "./PathFunctionHelpers";
|
|
7
7
|
import { CallSpec, functionSchema } from "./PathFunctionRunner";
|
|
8
|
-
import { getDomain } from "../config";
|
|
8
|
+
import { getDomain, isLocal } from "../config";
|
|
9
9
|
import { isHotReloading } from "socket-function/hot/HotReloadController";
|
|
10
10
|
import { Schema2, Schema2Fncs, Schema2T, SchemaPath } from "../2-proxy/schema2";
|
|
11
11
|
import { PathValueProxyWatcher, atomic, proxyWatcher, registerSchema } from "../2-proxy/PathValueProxyWatcher";
|
|
@@ -399,9 +399,18 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
|
|
|
399
399
|
// NOTE: We clobber the previous value, as we might just be hotreloading, which
|
|
400
400
|
// could mean the exports value has our previous value.
|
|
401
401
|
module.exports[SCHEMA_EXPORT_KEY] = result;
|
|
402
|
+
developmentModules.set(moduleId, module);
|
|
402
403
|
return result;
|
|
403
404
|
};
|
|
404
405
|
}
|
|
406
|
+
let developmentModules = new Map<string, NodeJS.Module>();
|
|
407
|
+
|
|
408
|
+
export function getDevelopmentModule(moduleId: string): NodeJS.Module | undefined {
|
|
409
|
+
return developmentModules.get(moduleId);
|
|
410
|
+
}
|
|
411
|
+
export function getAllDevelopmentModulesIds(): string[] {
|
|
412
|
+
return Array.from(developmentModules.keys());
|
|
413
|
+
}
|
|
405
414
|
|
|
406
415
|
function isChildPath(spec: SchemaPath, path: string[]): boolean {
|
|
407
416
|
if (spec.length > path.length) return false;
|
|
@@ -35,7 +35,14 @@ export async function getEdgeBootstrapScript(config: {
|
|
|
35
35
|
}): Promise<string> {
|
|
36
36
|
return await measureBlock(async function getEdgeBootstrapScript() {
|
|
37
37
|
let cachedConfig = await getCachedConfig(config.edgeNodeConfigURL);
|
|
38
|
-
|
|
38
|
+
let allowedArgs = ["--local"];
|
|
39
|
+
// TODO: Be smarter about this (getting values and not just flags)
|
|
40
|
+
let argv = process.argv.filter(x => allowedArgs.includes(x));
|
|
41
|
+
return `(${edgeNodeFunction.toString()})(${JSON.stringify({
|
|
42
|
+
...config,
|
|
43
|
+
cachedConfig,
|
|
44
|
+
argv,
|
|
45
|
+
})});`;
|
|
39
46
|
});
|
|
40
47
|
}
|
|
41
48
|
|
|
@@ -64,7 +71,9 @@ declare global {
|
|
|
64
71
|
async function edgeNodeFunction(config: {
|
|
65
72
|
edgeNodeConfigURL: string;
|
|
66
73
|
cachedConfig: EdgeNodesIndex | undefined;
|
|
74
|
+
argv: string[];
|
|
67
75
|
}) {
|
|
76
|
+
process.argv = config.argv;
|
|
68
77
|
// IMPORTANT! Everything in this function is embedded, so this function can't use any external imports
|
|
69
78
|
// (except for type imports, of course).
|
|
70
79
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// a file prevents it from transforming it, so we'll miss a lot of files doing it this late).
|
|
4
4
|
import "../inject";
|
|
5
5
|
|
|
6
|
-
import { isNode, timeInDay, timeInHour, timeInMinute } from "socket-function/src/misc";
|
|
6
|
+
import { isNode, isNodeTrue, timeInDay, timeInHour, timeInMinute } from "socket-function/src/misc";
|
|
7
7
|
|
|
8
8
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
9
9
|
import { isHotReloading, onHotReload, watchFilesAndTriggerHotReloading } from "socket-function/hot/HotReloadController";
|
|
@@ -17,7 +17,7 @@ import { clientWatcher, ClientWatcher } from "../1-path-client/pathValueClientWa
|
|
|
17
17
|
import { SyncWatcher, proxyWatcher, specialObjectWriteValue, isSynced, PathValueProxyWatcher, atomic, doAtomicWrites, noAtomicSchema, undeleteFromLookup, registerSchemaPrefix } from "../2-proxy/PathValueProxyWatcher";
|
|
18
18
|
import { isInProxyDatabase, rawSchema } from "../2-proxy/pathDatabaseProxyBase";
|
|
19
19
|
import { isValueProxy2, getProxyPath } from "../2-proxy/pathValueProxy";
|
|
20
|
-
import { getCurrentCallAllowUndefined, getCurrentCall, CallSpec } from "../3-path-functions/PathFunctionRunner";
|
|
20
|
+
import { getCurrentCallAllowUndefined, getCurrentCall, CallSpec, PathFunctionRunner } from "../3-path-functions/PathFunctionRunner";
|
|
21
21
|
import { listenOnDebugger } from "../diagnostics/listenOnDebugger";
|
|
22
22
|
import { logErrors } from "../errors";
|
|
23
23
|
import { getLastPathPart, getPathIndexAssert, getPathStr2, hack_setPackedPathSuffix } from "../path";
|
|
@@ -47,9 +47,45 @@ import { hookErrors } from "../diagnostics/errorLogs/hookErrors";
|
|
|
47
47
|
import { Schema2, Schema2T, t } from "../2-proxy/schema2";
|
|
48
48
|
import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
|
|
49
49
|
import { isDynamicModule } from "../3-path-functions/pathFunctionLoader";
|
|
50
|
+
import yargs from "yargs";
|
|
50
51
|
|
|
51
52
|
export { t };
|
|
52
53
|
|
|
54
|
+
let yargObj = isNodeTrue() && yargs(process.argv)
|
|
55
|
+
.option("verboseall", { type: "boolean", desc: "Log all everything (except network)" })
|
|
56
|
+
.option("verbose", { type: "boolean", desc: "Log all writes and reads" })
|
|
57
|
+
.option("verbosewrites", { type: "boolean", desc: "Log all writes" })
|
|
58
|
+
.option("verbosereads", { type: "boolean", desc: "Log all reads" })
|
|
59
|
+
.option("verbosecalls", { type: "boolean", desc: "Log all calls" })
|
|
60
|
+
.option("verbosenetwork", { type: "boolean", desc: "Log all network activity" })
|
|
61
|
+
.argv || {}
|
|
62
|
+
;
|
|
63
|
+
setImmediate(() => {
|
|
64
|
+
if (yargObj.verbose) {
|
|
65
|
+
ClientWatcher.DEBUG_WRITES = true;
|
|
66
|
+
ClientWatcher.DEBUG_READS = true;
|
|
67
|
+
}
|
|
68
|
+
if (yargObj.verbosewrites) {
|
|
69
|
+
ClientWatcher.DEBUG_WRITES = true;
|
|
70
|
+
}
|
|
71
|
+
if (yargObj.verbosereads) {
|
|
72
|
+
ClientWatcher.DEBUG_READS = true;
|
|
73
|
+
}
|
|
74
|
+
if (yargObj.verbosecalls) {
|
|
75
|
+
PathFunctionRunner.DEBUG_CALLS = true;
|
|
76
|
+
Querysub.DEBUG_CALLS = true;
|
|
77
|
+
}
|
|
78
|
+
if (yargObj.verboseall) {
|
|
79
|
+
ClientWatcher.DEBUG_WRITES = true;
|
|
80
|
+
ClientWatcher.DEBUG_READS = true;
|
|
81
|
+
PathFunctionRunner.DEBUG_CALLS = true;
|
|
82
|
+
Querysub.DEBUG_CALLS = true;
|
|
83
|
+
}
|
|
84
|
+
if (yargObj.verbosenetwork) {
|
|
85
|
+
SocketFunction.logMessages = true;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
53
89
|
if (!registerGetCompressNetwork) {
|
|
54
90
|
devDebugbreak();
|
|
55
91
|
}
|
|
@@ -302,6 +338,38 @@ export class Querysub {
|
|
|
302
338
|
proxyWatcher.getTriggeredWatcher().onInnerDisposed.push(callback);
|
|
303
339
|
}
|
|
304
340
|
|
|
341
|
+
/** A more powerful version of omCommitFinished, which even waits for call predictions (or tries to).
|
|
342
|
+
* - Also see afterPredictionsSynced, which runs the callback in a write.
|
|
343
|
+
* NOTE: IDEALLY, you would just call a series of synced functions. They will be run in order,
|
|
344
|
+
* with all the synced data, and just work. However... if you are running very expensive
|
|
345
|
+
* asynchronous operations, you might need to use an async workflow.
|
|
346
|
+
* "afterPredictions" helps in async workflows, by calling your callback after all local function
|
|
347
|
+
* predictions finish. This allows you to (somewhat) read back their writes. It isn't perfect,
|
|
348
|
+
* but it is safer then Promise.resolve().then(callback), or any type of setTimeout. Usually
|
|
349
|
+
* the callback will be fast, except for the first time, when code will needed by loaded to
|
|
350
|
+
* run the predictive functions.
|
|
351
|
+
* NOTE: Only waits for calls BEFORE the callback is called. This is useful, but also means
|
|
352
|
+
* you can't call this at the start of your function (as nothing will have been called yet).
|
|
353
|
+
* NOTE: This can also be used to prevent
|
|
354
|
+
*/
|
|
355
|
+
public static afterPredictions(callback: () => Promise<void>) {
|
|
356
|
+
let calls = proxyWatcher.getTriggeredWatcher().pendingCalls.map(x => x.call);
|
|
357
|
+
Querysub.onCommitFinished(async () => {
|
|
358
|
+
await Promise.all(calls.map(x => Querysub.onCallPredict(x)));
|
|
359
|
+
logErrors(callback());
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Solely for use to prevent local writes from occuring before predictions. We MIGHT make this a framework thing,
|
|
364
|
+
* or just not bother with it (as after a prediction is run once the code will be loaded allowing it to always
|
|
365
|
+
* run before the next frame).
|
|
366
|
+
*/
|
|
367
|
+
public static afterPredictionsSynced(callback: () => void) {
|
|
368
|
+
Querysub.afterPredictions(async () => {
|
|
369
|
+
Querysub.serviceWriteDetached(callback);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
305
373
|
public static onCallPredict = (x: CallSpec | undefined) => onCallPredict(x);
|
|
306
374
|
/** NOTE: USUALLY you don't have to wait for predictions, as functions will run in order anyways.
|
|
307
375
|
* BUT, if you run a synced function in the UI, then somewhere unrelated try to read values
|
|
@@ -889,6 +957,11 @@ setImmediate(() => {
|
|
|
889
957
|
hookErrors();
|
|
890
958
|
});
|
|
891
959
|
|
|
960
|
+
setImmediate(async () => {
|
|
961
|
+
// Import, so it registers addStatPeriodic
|
|
962
|
+
await import("../5-diagnostics/nodeMetadata");
|
|
963
|
+
});
|
|
964
|
+
|
|
892
965
|
registerGetCompressNetwork(() => Querysub.COMPRESS_NETWORK);
|
|
893
966
|
registerGetCompressDisk(() => Querysub.COMPRESS_DISK);
|
|
894
967
|
|
|
@@ -897,5 +970,6 @@ registerGetCompressDisk(() => Querysub.COMPRESS_DISK);
|
|
|
897
970
|
import "../diagnostics/watchdog";
|
|
898
971
|
import "../diagnostics/trackResources";
|
|
899
972
|
import "../diagnostics/benchmark";
|
|
900
|
-
import { getEdgeNodeConfigURL, registerEdgeNode } from "../4-deploy/edgeNodes";
|
|
973
|
+
import { getEdgeNodeConfigURL, registerEdgeNode } from "../4-deploy/edgeNodes";
|
|
974
|
+
import { getEdgeBootstrapScript } from "../4-deploy/edgeBootstrap";
|
|
901
975
|
|