querysub 0.153.0 → 0.155.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.
Files changed (70) hide show
  1. package/package.json +6 -6
  2. package/src/-b-authorities/cloudflareHelpers.ts +11 -2
  3. package/src/3-path-functions/PathFunctionRunner.ts +168 -97
  4. package/src/3-path-functions/PathFunctionRunnerMain.ts +8 -2
  5. package/src/3-path-functions/pathFunctionLoader.ts +11 -6
  6. package/src/3-path-functions/syncSchema.ts +10 -1
  7. package/src/4-deploy/deployMain.ts +5 -3
  8. package/src/4-deploy/edgeBootstrap.ts +10 -1
  9. package/src/4-dom/qreact.tsx +1 -0
  10. package/src/4-querysub/Querysub.ts +77 -3
  11. package/src/4-querysub/QuerysubController.ts +22 -2
  12. package/src/4-querysub/permissions.ts +33 -2
  13. package/src/4-querysub/querysubPrediction.ts +52 -18
  14. package/src/archiveapps/archiveGCEntry.tsx +38 -0
  15. package/src/archiveapps/archiveJoinEntry.ts +121 -0
  16. package/src/archiveapps/archiveMergeEntry.tsx +47 -0
  17. package/src/archiveapps/compressTest.tsx +59 -0
  18. package/src/archiveapps/lockTest.ts +127 -0
  19. package/src/config.ts +5 -0
  20. package/src/diagnostics/logs/injectFileLocationToConsole.ts +2 -1
  21. package/src/diagnostics/managementPages.tsx +55 -0
  22. package/src/diagnostics/misc-pages/ArchiveInspect.tsx +325 -0
  23. package/src/diagnostics/misc-pages/ArchiveViewer.tsx +781 -0
  24. package/src/diagnostics/misc-pages/ArchiveViewerTable.tsx +156 -0
  25. package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +573 -0
  26. package/src/diagnostics/misc-pages/ComponentSyncStats.tsx +129 -0
  27. package/src/diagnostics/misc-pages/LocalWatchViewer.tsx +431 -0
  28. package/src/diagnostics/misc-pages/RequireAuditPage.tsx +218 -0
  29. package/src/diagnostics/misc-pages/SnapshotViewer.tsx +206 -0
  30. package/src/diagnostics/misc-pages/TimeRangeView.tsx +648 -0
  31. package/src/diagnostics/misc-pages/archiveViewerFilter.tsx +221 -0
  32. package/src/diagnostics/misc-pages/archiveViewerShared.tsx +76 -0
  33. package/src/email/postmark.tsx +40 -0
  34. package/src/email/sendgrid.tsx +44 -0
  35. package/src/functional/UndoWatch.tsx +133 -0
  36. package/src/functional/diff.ts +858 -0
  37. package/src/functional/promiseCache.ts +67 -0
  38. package/src/functional/random.ts +9 -0
  39. package/src/functional/runCommand.ts +42 -0
  40. package/src/functional/runOnce.ts +7 -0
  41. package/src/functional/stats.ts +61 -0
  42. package/src/functional/throttleRerender.tsx +80 -0
  43. package/src/library-components/AspectSizedComponent.tsx +88 -0
  44. package/src/library-components/Histogram.tsx +338 -0
  45. package/src/library-components/InlinePopup.tsx +67 -0
  46. package/src/library-components/Notifications.tsx +153 -0
  47. package/src/library-components/RenderIfVisible.tsx +80 -0
  48. package/src/library-components/SimpleNotification.tsx +133 -0
  49. package/src/library-components/TabbedUI.tsx +39 -0
  50. package/src/library-components/URLParam.ts +7 -2
  51. package/src/library-components/animateAnyElement.tsx +65 -0
  52. package/src/library-components/errorNotifications.tsx +81 -0
  53. package/src/library-components/loadingIndicator.tsx +93 -0
  54. package/src/library-components/placeholder.ts +18 -0
  55. package/src/misc/format2.ts +48 -0
  56. package/src/misc.ts +33 -0
  57. package/src/misc2.ts +5 -0
  58. package/src/server.ts +2 -1
  59. package/src/storage/diskCache.ts +227 -0
  60. package/src/storage/diskCache2.ts +122 -0
  61. package/src/storage/fileSystemPointer.ts +72 -0
  62. package/src/user-implementation/LoginPage.tsx +78 -0
  63. package/src/user-implementation/RequireAuditPage.tsx +219 -0
  64. package/src/user-implementation/SecurityPage.tsx +212 -0
  65. package/src/user-implementation/UserPage.tsx +320 -0
  66. package/src/user-implementation/addSuperUser.ts +27 -0
  67. package/src/user-implementation/canSeeSource.ts +41 -0
  68. package/src/user-implementation/loginEmail.tsx +159 -0
  69. package/src/user-implementation/setEmailKey.ts +25 -0
  70. package/src/user-implementation/userData.ts +976 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.153.0",
3
+ "version": "0.155.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.84.0",
27
+ "socket-function": "^0.88.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 D:/repos/shard/",
40
- "deploy": "yarn typenode ./src/4-deploy/deployMain.ts --domain querysub.com --local D:/repos/shard/",
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 D:/repos/shard/",
44
- "test2": "yarn typenode ./src/4-dom/qreactTest.tsx --local D:/repos/shard/"
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) throw new Error(`b2:/keys/cloudflare.json is missing. It should contain { "key": "your-key", "email": "your-email" }`);
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
- for (let [filePath, moduleData] of Object.entries(data())) {
262
- watchModuleCalls(filePath);
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 [filePath, moduleData] of Object.entries(data())) {
267
- let callPath = getProxyPath(() => data()[filePath].Calls);
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()[filePath].Results);
276
+ let resultPath = getProxyPath(() => data()[moduleId].Results);
272
277
  triggerPaths.add(resultPath);
273
278
  }
274
279
 
275
- let callPathLength = 0;
276
- if (triggerPaths.size > 0) {
277
- callPathLength = getPathDepth(Array.from(triggerPaths)[0]);
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 callPathEquivalent = trimPathStrToDepth(path, callPathLength);
285
- if (triggerPaths.has(callPathEquivalent)) {
286
- const callId = getPathIndex(path, callPathLength);
287
- if (!callId) continue;
288
- let moduleId = getPathIndex(path, callPathLength - 2);
289
- if (!moduleId) continue;
290
-
291
- callsToCall.delete(callId);
292
-
293
- let moduleData = data()[moduleId];
294
-
295
- let result = atomicObjectRead(moduleData.Results[callId]);
296
- if (result) continue;
297
-
298
- let callData = atomicObjectRead(moduleData.Calls[callId]);
299
- if (!callData) continue;
300
-
301
- if (callData.DomainName !== domainName) continue;
302
- if (callData.ModuleId !== moduleId) continue;
303
- if (callData.CallId !== callId) continue;
304
-
305
- let functionSpec = atomicObjectRead(moduleData.Sources[callData.FunctionId]);
306
- if (!functionSpec) {
307
- if (isSynced(moduleData.Sources[callData.FunctionId])) {
308
- console.warn(yellow(`Cannot call ${callData.DomainName}.${callData.ModuleId}.Sources.${callData.FunctionId} because it is not deployed`));
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
- // If we haven't synced the result, we can't know if it is ready or not
314
- // (we'll run anyways once it is synced). Most other parts are immutable,
315
- // but the result is by definition not, so... it is one of the values we check!
316
- if (!isSynced(moduleData.Results[callId])) continue;
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
- if (PathFunctionRunner.DEBUG_CALLS) {
334
- console.log(`QUEUING ${getDebugName(callData, functionSpec, true)}`);
335
- let resultsPath = getProxyPath(() => moduleData.Results[callId]);
336
- let history = authorityStorage.getValuePlusHistory(resultsPath);
337
- console.log(` History: ${history.length}`);
338
- for (let { valid, time, canGCValue } of history) {
339
- console.log(` ${valid ? "✅" : "❌"} ${debugTime(time)} ${canGCValue ? "value is undefined" : ""}`);
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
- // (We also need to depend on the RIGHT function spec).
531
- if (
532
- syncedSpec && (
533
- syncedSpec.DomainName !== functionSpec.DomainName
534
- || syncedSpec.ModuleId !== functionSpec.ModuleId
535
- || syncedSpec.FilePath !== functionSpec.FilePath
536
- || syncedSpec.FunctionId !== functionSpec.FunctionId
537
- || syncedSpec.exportPathStr !== functionSpec.exportPathStr
538
- || syncedSpec.FilePath !== functionSpec.FilePath
539
- || syncedSpec.gitRef !== functionSpec.gitRef
540
- || syncedSpec.gitURL !== functionSpec.gitURL
541
- )
542
- ) {
543
- isFunctionSpecOutdated = true;
544
- functionSpec = {
545
- DomainName: syncedSpec.DomainName,
546
- ModuleId: syncedSpec.ModuleId,
547
- FilePath: syncedSpec.FilePath,
548
- FunctionId: syncedSpec.FunctionId,
549
- exportPathStr: syncedSpec.exportPathStr,
550
- gitRef: syncedSpec.gitRef,
551
- gitURL: syncedSpec.gitURL,
552
- };
553
- return;
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: syncedSpec, }, () => {
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" is presently supported.`);
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
- // Also, use the git@ version
71
- // Replace "https://*/" with "git@*:"
72
- let gitAtUrl = repo.url.replace(/^https:\/\/([^\/]+)\//, "git@$1:");
73
- mapping[gitAtUrl] = path;
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;
@@ -137,11 +137,13 @@ export async function deployMain() {
137
137
  debugName: "setLiveDeployedHash",
138
138
  inlineNestedWatchers: true,
139
139
  watchFunction: () => {
140
- if (!yargObj.deployonlycode) {
140
+ if (yargObj.deployonlycode) {
141
141
  void setLiveDeployedHash({ hash: gitRef, refreshThresholdTime, });
142
- }
143
- if (!yargObj.deployonlyui) {
142
+ } else if (yargObj.deployonlyui) {
143
+ void replaceFunctions({ domainName, functions: currentFunctions, });
144
+ } else {
144
145
  void replaceFunctions({ domainName, functions: currentFunctions, });
146
+ void setLiveDeployedHash({ hash: gitRef, refreshThresholdTime, });
145
147
  }
146
148
  },
147
149
  });
@@ -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
- return `(${edgeNodeFunction.toString()})(${JSON.stringify({ ...config, cachedConfig })});`;
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
 
@@ -79,6 +79,7 @@ export namespace qreact {
79
79
  export type ComponentChildren = preact.ComponentChildren;
80
80
  export type VDom = preact.ComponentChildren;
81
81
  export type Component = InstanceType<typeof Component>;
82
+ export type ComponentClass = preact.ComponentClass;
82
83
  export namespace JSX {
83
84
  export type IntrinsicElements = {
84
85
  [key in keyof preact.JSX.IntrinsicElements]: (