querysub 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.2.0",
3
+ "version": "0.3.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.31.0",
27
+ "socket-function": "^0.32.0",
28
28
  "terser": "^5.31.0",
29
29
  "typesafecss": "^0.6.3",
30
30
  "yaml": "^2.5.0",
@@ -4,6 +4,8 @@ import { getAllNodeIds, getOwnThreadId } from "../../-f-node-discovery/NodeDisco
4
4
  import { pathValueArchives } from "../pathValueArchives";
5
5
  import { ignoreErrors, logErrors, timeoutToUndefinedSilent } from "../../errors";
6
6
  import { green, magenta } from "socket-function/src/formatting/logColors";
7
+ import { devDebugbreak } from "../../config";
8
+ import { sort } from "socket-function/src/misc";
7
9
 
8
10
  const snapshots = getArchives("snapshots");
9
11
 
@@ -94,6 +96,8 @@ export async function getSnapshotList(): Promise<ArchiveSnapshotOverview[]> {
94
96
 
95
97
  overview.push(...snapshotFiles.map(file => fileNameToOverview(file)));
96
98
 
99
+ sort(overview, x => -x.time);
100
+
97
101
  return overview;
98
102
  }
99
103
 
@@ -8,7 +8,7 @@ import { getModuleRelativePath } from "./syncSchema";
8
8
  import * as child_process from "child_process";
9
9
  import { proxyWatcher } from "../2-proxy/PathValueProxyWatcher";
10
10
  import { green, red } from "socket-function/src/formatting/logColors";
11
- import { isDefined } from "../misc";
11
+ import { isDefined, isEmpty } from "../misc";
12
12
  import fs from "fs";
13
13
  import { SocketFunction } from "socket-function/SocketFunction";
14
14
  import { getThreadKeyCert } from "../-a-auth/certs";
@@ -23,6 +23,7 @@ import debugbreak from "debugbreak";
23
23
  import yargs from "yargs";
24
24
  import { isPublic } from "../config";
25
25
  import { deployBlock } from "./deployBlock";
26
+ import { LOCAL_DOMAIN } from "../0-path-value-core/PathController";
26
27
  let yargObj = yargs(process.argv)
27
28
  .option("domain", { type: "string", required: true, desc: "Domain to deploy to" })
28
29
  .option("nogit", { type: "boolean", default: false, desc: `Disable git operations. Useful for debugging, when using --local` })
@@ -87,31 +88,33 @@ export async function deployMain() {
87
88
 
88
89
  if (!isAllowedToDeploy) continue;
89
90
  let schema = getSchemaObject(module);
91
+ if (!schema) continue;
92
+ // If it's local, we don't need to deploy it.
93
+ // (And shouldn't?, as we don't want to run local functions by request?)
94
+ if (schema.domainName === LOCAL_DOMAIN) continue;
90
95
 
91
- if (schema) {
92
- let filePath = getModuleRelativePath(module);
93
-
94
- let moduleId = getPathStr2(domainName, filePath);
95
- let otherUsed = usedModules.get(moduleId);
96
- if (otherUsed) {
97
- throw new Error(`Overlapping module ids for ${domainName}.${filePath}, at ${otherUsed} and ${filePath}`);
98
- }
99
- usedModules.set(moduleId, filePath);
100
-
101
- const functionIds = Object.keys(schema.rawFunctions);
102
- functionIds.push(PERMISSIONS_FUNCTION_ID);
103
- for (let functionId of functionIds) {
104
- const exportPathStr = getExportPath(functionId);
105
- currentFunctions.push({
106
- DomainName: domainName,
107
- ModuleId: schema.moduleId,
108
- FilePath: filePath,
109
- FunctionId: functionId,
110
- exportPathStr,
111
- gitURL,
112
- gitRef,
113
- });
114
- }
96
+ let filePath = getModuleRelativePath(module);
97
+
98
+ let moduleId = getPathStr2(domainName, filePath);
99
+ let otherUsed = usedModules.get(moduleId);
100
+ if (otherUsed) {
101
+ throw new Error(`Overlapping module ids for ${domainName}.${filePath}, at ${otherUsed} and ${filePath}`);
102
+ }
103
+ usedModules.set(moduleId, filePath);
104
+
105
+ const functionIds = Object.keys(schema.rawFunctions);
106
+ functionIds.push(PERMISSIONS_FUNCTION_ID);
107
+ for (let functionId of functionIds) {
108
+ const exportPathStr = getExportPath(functionId);
109
+ currentFunctions.push({
110
+ DomainName: domainName,
111
+ ModuleId: schema.moduleId,
112
+ FilePath: filePath,
113
+ FunctionId: functionId,
114
+ exportPathStr,
115
+ gitURL,
116
+ gitRef,
117
+ });
115
118
  }
116
119
  }
117
120
 
@@ -183,14 +183,15 @@ async function getModuleFromSpecBase(
183
183
  // NOTE: The true tells require to not warn about the async loading
184
184
  await (require as any)(path, true);
185
185
  } catch (e: any) {
186
- throw new Error(`Error when loading function for ${JSON.stringify(path)}\n${e.stack}`);
186
+
187
+ throw new Error(`Error when loading function for ${JSON.stringify(path)}:${spec.FunctionId}\n${e.stack}`);
187
188
  }
188
189
  let moduleId = require.resolve(path) || path;
189
190
  let module = require.cache[moduleId];
190
191
  if (!module) {
191
192
  debugbreak(2);
192
193
  debugger;
193
- throw new Error(`Module not found: ${moduleId}`);
194
+ throw new Error(`Module not found: ${moduleId} (for ${spec.FunctionId})`);
194
195
  }
195
196
 
196
197
  //todonext
@@ -17,6 +17,7 @@ import { getPathFromProxy, getProxyPath } from "../2-proxy/pathValueProxy";
17
17
  import { CALL_PERMISSIONS_KEY } from "../4-querysub/permissionsShared";
18
18
  import { LOCAL_DOMAIN } from "../0-path-value-core/PathController";
19
19
  import path from "path";
20
+ import { isEmpty } from "../misc";
20
21
 
21
22
  // This is the the function id which should be used when creating the FunctionSpec (in order to load the module),
22
23
  // to access the permissions in the schema.
@@ -45,6 +46,7 @@ export type FunctionMetadata<F = unknown> = {
45
46
 
46
47
  export interface SchemaObject<Schema = any, Functions = any> {
47
48
  data: () => Schema;
49
+ domainName: string;
48
50
  moduleId: string;
49
51
  functions: {
50
52
  [key in keyof Functions]: (
@@ -350,9 +352,7 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
350
352
 
351
353
  let permissionsNonWildcards = new Map<string, PermissionsCallback>();
352
354
  let permissionsWildcards: { pathParts: string[]; callback: PermissionsCallback }[] = [];
353
- if (permissions) {
354
- addPermissions(rootPathStr, permissions);
355
- }
355
+ addPermissions(rootPathStr, permissions);
356
356
  function addPermissions(path: string, permissions: SchemaToPermissions<Schema> | undefined) {
357
357
  if (!permissions) return;
358
358
  for (let key of getStringKeys(permissions)) {
@@ -378,6 +378,7 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
378
378
 
379
379
  let result: SchemaObject = {
380
380
  data,
381
+ domainName,
381
382
  moduleId,
382
383
  functions: wrappedFunctions as any,
383
384
  functionMetadata: functionMetadata as any,
@@ -329,10 +329,10 @@ function predictCall(config: {
329
329
  };
330
330
  try {
331
331
  dryRunResult = await getCallWrites({ call, debugName, overrides: config.overrides });
332
- } catch (e) {
332
+ } catch (e: any) {
333
333
 
334
334
  if (!pathValueSerializer.getPathValue(authorityStorage.getValueAtTime(pathResultWrite, undefined))) {
335
- console.log(`Skipping prediction for ${debugName} due to error running predictive call. Likely just an out of order error.`, e);
335
+ console.log(`Skipping prediction for ${debugName} due to error running predictive call. Likely just an out of order error.`, e.stack);
336
336
  } else {
337
337
  // NOTE: This case happens a lot, because of how we handle locks. We don't receive locked values, and so
338
338
  // we assume all values have no locks, and only keep the latest. This is usually fine, but... if we lose
@@ -482,9 +482,9 @@ export async function getCallWrites(config: {
482
482
  // throw new Error(`Domain not found ${call.DomainName}`);
483
483
  // }
484
484
  let domainObject = functionSchema()[call.DomainName];
485
- if (!(call.ModuleId in domainObject.PathFunctionRunner)) {
486
- throw new Error(`Module not found ${call.DomainName}.${call.ModuleId}`);
487
- }
485
+ // if (!(call.ModuleId in domainObject.PathFunctionRunner)) {
486
+ // throw new Error(`Module not found ${call.DomainName}.${call.ModuleId}`);
487
+ // }
488
488
  let moduleObject = domainObject.PathFunctionRunner[call.ModuleId];
489
489
  if (!(call.FunctionId in moduleObject.Sources)) {
490
490
  throw new Error(`Function not found ${call.DomainName}.${call.ModuleId}.${call.FunctionId}`);
@@ -516,9 +516,9 @@ export async function getCallWrites(config: {
516
516
  overrides: config.overrides,
517
517
  nestedCalls: "inline",
518
518
  watchFunction() {
519
- overrideCurrentCall(call, () => {
519
+ return overrideCurrentCall(call, () => {
520
520
  let args = parseArgs(call);
521
- baseFunction(...args);
521
+ return baseFunction(...args);
522
522
  });
523
523
  },
524
524
  });
@@ -20,8 +20,9 @@ import { CallSpec, getCurrentCall, getCurrentCallAllowUndefined, overrideCurrent
20
20
  import { createLazyComponent } from "../library-components/LazyComponent";
21
21
  import { LogNotify } from "./errorLogs/LogNotify";
22
22
  import { isTrusted } from "../-d-trust/NetworkTrust2";
23
- import { getDomain } from "../config";
23
+ import { devDebugbreak, getDomain } from "../config";
24
24
  import { getCallWrites } from "../4-querysub/querysubPrediction";
25
+ import { SchemaObject } from "../3-path-functions/syncSchema";
25
26
 
26
27
 
27
28
  export const managementPageURL = new URLParam("managementpage", "");
@@ -33,25 +34,17 @@ let pages: {
33
34
  title: string;
34
35
  }[] = [];
35
36
 
36
- let internalIsManagementUser: () => boolean = () => {
37
- throw new Error("registerManagementPages2 must be called during deploy for management functions to be exposed");
38
- };
39
-
40
- // NOTE: We don't store the UI in the database (here, or anywhere else, at least
41
- // not yet), but we do want to store the permission function call, as we may
42
- // need to update it immediately, across all servers.
43
- const schema = Querysub.createSchema()({
44
- module,
45
- moduleId: "managementPages",
46
- functions: {
47
- schemaIsManagementUser,
48
- },
49
- });
50
- function schemaIsManagementUser() {
51
- return internalIsManagementUser();
37
+ let __schema: SchemaObject<unknown, {
38
+ schemaIsManagementUser: () => boolean;
39
+ }> | undefined = undefined;
40
+ function getSchema() {
41
+ if (!__schema) throw new Error(`registerManagementPages2 must be called for management functions to be accessed`);
42
+ return __schema;
52
43
  }
53
44
 
45
+ let registeredModule: NodeJS.Module | undefined = undefined;
54
46
  export async function registerManagementPages2(config: {
47
+ module: NodeJS.Module;
55
48
  isManagementUser: () => boolean;
56
49
  pages: {
57
50
  componentName: string;
@@ -61,7 +54,11 @@ export async function registerManagementPages2(config: {
61
54
  title?: string;
62
55
  }[];
63
56
  }) {
64
- baseIsManagementUser = config.isManagementUser;
57
+ if (registeredModule) {
58
+ if (registeredModule === config.module) return;
59
+ throw new Error(`registerManagementPages2 called with a different module than previously registered. This will break isManagementUser, and so is not allowed.`);
60
+ }
61
+ registeredModule = config.module;
65
62
  let inputPages = config.pages.slice();
66
63
 
67
64
  inputPages.push({
@@ -94,6 +91,17 @@ export async function registerManagementPages2(config: {
94
91
  getModule: () => import("./TimeDebug"),
95
92
  });
96
93
 
94
+ // NOTE: We don't store the UI in the database (here, or anywhere else, at least
95
+ // not yet), but we do want to store the permission function call, as we may
96
+ // need to update it immediately, across all servers.
97
+ __schema = Querysub.createSchema()({
98
+ module: config.module,
99
+ moduleId: "managementPages",
100
+ functions: {
101
+ schemaIsManagementUser: config.isManagementUser,
102
+ },
103
+ });
104
+
97
105
  if (isServer()) {
98
106
  for (let page of inputPages) {
99
107
  // NOTE: If we split this into a module for component/controller, we need to make sure we
@@ -124,11 +132,6 @@ export async function registerManagementPages2(config: {
124
132
  }
125
133
  }
126
134
 
127
- let baseIsManagementUser: () => boolean = () => {
128
- // If we are clientside, all calls come from the server, and so are allowed.
129
- if (isClient()) return true;
130
- throw new Error("baseIsManagementUser not set");
131
- };
132
135
 
133
136
  export async function isManagementUser() {
134
137
  let caller = SocketFunction.getCaller();
@@ -138,6 +141,9 @@ export async function isManagementUser() {
138
141
  // when clients are given callbacks by servers, for management apis which
139
142
  // have every endpoint protected by isManagementUser.
140
143
  if (await isTrusted(callerMachineId)) return true;
144
+ let schema = getSchema();
145
+ let functionId = "schemaIsManagementUser" as const;
146
+ if (!schema.functions[functionId]) throw new Error(`Function ${functionId} not found in schema`);
141
147
 
142
148
  let testCall: CallSpec = {
143
149
  callerIP: IdentityController_getSecureIP(caller),
@@ -145,8 +151,8 @@ export async function isManagementUser() {
145
151
  argsEncoded: encodeArgs([]),
146
152
  CallId: nextId(),
147
153
  DomainName: getDomain(),
148
- ModuleId: "managementPages",
149
- FunctionId: "schemaIsManagementUser",
154
+ ModuleId: schema.moduleId,
155
+ FunctionId: functionId,
150
156
  runAtTime: getNextTime(),
151
157
  };
152
158