querysub 0.181.0 → 0.182.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.181.0",
3
+ "version": "0.182.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",
package/spec.txt CHANGED
@@ -51,10 +51,6 @@ More corruption resistance files
51
51
  - Value types will be string, float64, byte, Buffer[]
52
52
  - and... we might as well add support for short, int, float, and uint (uint is a good way to store a guid, via storing 8 uint variables).
53
53
 
54
-
55
-
56
-
57
-
58
54
  Schema/binary PathValues accesses
59
55
 
60
56
  Base code
@@ -683,19 +683,19 @@ class TransactionLocker {
683
683
  {
684
684
  let oldThreshold = readTime - DEAD_CREATE_THRESHOLD;
685
685
  let veryOldFiles = dataState.rawDataFiles.filter(x => x.file.endsWith(".locked")).filter(a => a.createTime < oldThreshold);
686
- let confirmedLookup = new Set(dataState.confirmedDataFiles.map(a => a));
687
- let unconfirmedOldFiles = veryOldFiles.filter(a => !confirmedLookup.has(a));
686
+ let confirmedLookup = new Set(dataState.confirmedDataFiles.map(a => a.file));
687
+ let unconfirmedOldFiles = veryOldFiles.filter(a => !confirmedLookup.has(a.file));
688
688
 
689
689
  if (unconfirmedOldFiles.length > 0) {
690
690
  let doubleCheck = await this.readDataState();
691
- let doubleCheckLookup = new Set(doubleCheck.confirmedDataFiles.map(a => a));
691
+ let doubleCheckLookup = new Set(doubleCheck.confirmedDataFiles.map(a => a.file));
692
692
  let doubleCheckDataFiles = new Set(doubleCheck.rawDataFiles.map(a => a.file));
693
693
  // IMPORTANT! Make sure they still aren't confirmed AND that the rawDataFiles exist. This helps prevent cases
694
694
  // where backblaze returns no files? Which it might be doing, as multiple times this code has
695
695
  // triggered (without this check), and deletes all of our files...
696
- let unconfirmedOldFiles2 = veryOldFiles.filter(a => !doubleCheckLookup.has(a) && doubleCheckDataFiles.has(a.file));
696
+ let unconfirmedOldFiles2 = unconfirmedOldFiles.filter(a => !doubleCheckLookup.has(a.file) && doubleCheckDataFiles.has(a.file));
697
697
  console.warn(red(`Deleted ${unconfirmedOldFiles2.length} very old unconfirmed files`), { files: unconfirmedOldFiles2.map(x => x.file) });
698
- diskLog(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
698
+ logNodeStats(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
699
699
  // At the point the file was very old when we started reading, not part of the active transaction.
700
700
  for (let file of unconfirmedOldFiles2) {
701
701
  await this.deleteDataFile(file.file, `old unconfirmed file (${getOwnNodeId()}, ${process.argv[1]})`);
@@ -185,7 +185,9 @@ export class PathValueArchives {
185
185
  if (time < oldestTime) oldestTime = time;
186
186
  }
187
187
  let slowestFileWriteTime = oldestTime + ARCHIVE_FLUSH_LIMIT;
188
- await archives().set(authorityPath + file, data);
188
+ let fullPath = authorityPath + file;
189
+ console.log(`Write archive file ${fullPath}, with size ${formatNumber(data.byteLength)}B, and count ${formatNumber(values.length)}`);
190
+ await archives().set(fullPath, data);
189
191
 
190
192
  let fileInfo = await archives().getInfo(authorityPath + file);
191
193
  // NOTE: If no fileInfo... then our file was merged? Which... is BAD, as it means we took
@@ -45,6 +45,7 @@ import { devDebugbreak, getDomain, isDynamicallyLoading, isPublic, noSyncing } f
45
45
  import { Schema2, Schema2T, t } from "../2-proxy/schema2";
46
46
  import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
47
47
  import yargs from "yargs";
48
+ import { parseArgsFactory } from "../misc/rawParams";
48
49
 
49
50
  import * as typesafecss from "typesafecss";
50
51
  typesafecss.setMeasureBlock(measureBlock);
@@ -55,7 +56,7 @@ typesafecss.setDelayFnc(async fnc => {
55
56
 
56
57
  export { t };
57
58
 
58
- let yargObj = isNodeTrue() && yargs(process.argv)
59
+ let yargObj = parseArgsFactory()
59
60
  .option("verboseall", { type: "boolean", desc: "Log all everything (except network)" })
60
61
  .option("verbose", { type: "boolean", desc: "Log all writes and reads" })
61
62
  .option("verbosewrites", { type: "boolean", desc: "Log all writes" })
@@ -63,7 +64,8 @@ let yargObj = isNodeTrue() && yargs(process.argv)
63
64
  .option("verbosecalls", { type: "boolean", desc: "Log all calls" })
64
65
  .option("verbosenetwork", { type: "boolean", desc: "Log all network activity" })
65
66
  .option("verboseframework", { type: "boolean", desc: "Log internal SocketFunction framework" })
66
- .argv || {}
67
+ .option("nodelay", { type: "boolean", desc: "Don't delay committing functions, even ones that are marked to be delayed." })
68
+ .argv
67
69
  ;
68
70
  setImmediate(() => {
69
71
  if (yargObj.verbose) {
@@ -92,6 +94,9 @@ setImmediate(() => {
92
94
  if (yargObj.verboseframework) {
93
95
  SocketFunction.silent = false;
94
96
  }
97
+ if (yargObj.nodelay) {
98
+ Querysub.DELAY_COMMIT_DELAY = 0;
99
+ }
95
100
  });
96
101
 
97
102
  if (!registerGetCompressNetwork) {
@@ -27,7 +27,9 @@ async function runGenesisJoinIteration() {
27
27
  while (reread) {
28
28
  reread = false;
29
29
  let result = await locker.atomicSwapFiles({}, async (valueFiles, readFiles) => {
30
+ let originalCount = valueFiles.length;
30
31
  valueFiles = valueFiles.filter(x => matchedDirs.some(y => x.file.startsWith(y)));
32
+ let underPathCount = valueFiles.length;
31
33
  // Merge the newest first, so if we hit a big file, we can just ignore it,
32
34
  // and next time merge the smaller files after it.
33
35
  sort(valueFiles, x => -x.createTime);
@@ -37,6 +39,7 @@ async function runGenesisJoinIteration() {
37
39
  if (obj.sourceType !== "genesis") return false;
38
40
  return x.createTime >= maxAge;
39
41
  });
42
+ let withinTimeRangeCount = valueFiles.length;
40
43
 
41
44
  if (valueFiles.length < 3) return [];
42
45
 
@@ -105,7 +108,7 @@ async function runGenesisJoinIteration() {
105
108
  });
106
109
 
107
110
 
108
- console.log(magenta(`Joining ${formatNumber(usedFiles.length)} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes`));
111
+ console.log(magenta(`Joining ${formatNumber(usedFiles.length)} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes. Original count: ${formatNumber(originalCount)}, under path count: ${formatNumber(underPathCount)}, within time range count: ${formatNumber(withinTimeRangeCount)}`));
109
112
 
110
113
  return [transaction];
111
114
  });
package/src/config.ts CHANGED
@@ -2,10 +2,11 @@ import { isNode, isNodeTrue } from "socket-function/src/misc";
2
2
  import yargs from "yargs";
3
3
  import debugbreak from "debugbreak";
4
4
  import { MaybePromise } from "socket-function/src/types";
5
+ import { parseArgsFactory } from "./misc/rawParams";
5
6
 
6
7
  export const serverPort = 11748;
7
8
 
8
- let yargObj = isNodeTrue() && yargs(process.argv)
9
+ let yargObj = parseArgsFactory()
9
10
  .option("nonetwork", { type: "boolean", desc: `Disables all network requirements. Reduces security, as this means we cannot use real certificates.` })
10
11
  .option("domain", { type: "string", desc: `Sets the domain` })
11
12
  .option("client", { type: "boolean", desc: `Drops permissions, acting as an unauthenticated node` })
@@ -16,7 +17,7 @@ let yargObj = isNodeTrue() && yargs(process.argv)
16
17
  .option("local", { type: "boolean", desc: `If true, uses the local directory instead of the remote git repo. Also hotreloads from disk. Determines the repo to replace through the package.json "repository" property.` })
17
18
  .option("nosyncing", { type: "boolean", desc: `Disabled any syncing code, just using HTTP. Still manages routing via backblaze though.` })
18
19
  .option("recovery", { type: "boolean", desc: `Allows any localhost connections to act like a superuser (and a trusted node), to help recover the database (as you need permission to access the snapshot page).` })
19
- .argv || {}
20
+ .argv
20
21
  ;
21
22
 
22
23
  if (!isNode()) {
@@ -0,0 +1,189 @@
1
+ import { setFlag } from "socket-function/require/compileFlags";
2
+ import { isNode } from "typesafecss";
3
+ import yargs from "yargs";
4
+
5
+ type Arguments<T = {}> = T & {
6
+ /** Non-option arguments */
7
+ _: Array<string | number>;
8
+ /** The script name or node command */
9
+ $0: string;
10
+ /** All remaining options */
11
+ [argName: string]: unknown;
12
+ };
13
+
14
+ interface Options {
15
+ desc?: string;
16
+ required?: boolean;
17
+ type?: "array" | "boolean" | "number" | "string" | undefined;
18
+ }
19
+
20
+ type InferredOptionTypeBase<O extends Options> = (
21
+ O extends { type: "array" } ? string[]
22
+ : O extends { type: "boolean" } ? boolean
23
+ : O extends { type: "number" } ? number
24
+ : O extends { type: "string" } ? string : never
25
+ );
26
+
27
+ type InferredOptionType<O extends Options> = (
28
+ O extends { required: true } ? InferredOptionTypeBase<O> : InferredOptionTypeBase<O> | undefined
29
+ );
30
+
31
+ interface Argv<T = {}> {
32
+ (): { [key in keyof Arguments<T>]: Arguments<T>[key] };
33
+ (args: readonly string[], cwd?: string): Argv<T>;
34
+
35
+ option<K extends string, O extends Options>(
36
+ key: K,
37
+ options: O,
38
+ ): Argv<T & { [key in K]: InferredOptionType<O> }>;
39
+
40
+ argv: T;
41
+ }
42
+
43
+ function getRawParams(): string[] {
44
+ if (isNode()) return process.argv;
45
+ let searchParams = new URLSearchParams(location.search);
46
+ let result: string[] = [];
47
+ for (let [key, value] of searchParams.entries()) {
48
+ if (value === "") {
49
+ result.push(`--${key}`);
50
+ } else {
51
+ result.push(`--${key}=${value}`);
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+
57
+ export function parseArgsFactory(): Argv {
58
+ type OptionCall = {
59
+ key: string;
60
+ options: Options;
61
+ };
62
+ type CurrentConfig = {
63
+ options: OptionCall[];
64
+ };
65
+
66
+ function finish(config: CurrentConfig) {
67
+ let params = getRawParams();
68
+ let result: Record<string, unknown> = {};
69
+
70
+ params = params.map(param => param.replace(/^-+/, ""));
71
+
72
+ if (params.includes("help")) {
73
+ console.log("Available options:");
74
+ for (let option of config.options) {
75
+ let type = option.options.type || "string";
76
+ let required = option.options.required ? " (required)" : "";
77
+ let desc = option.options.desc ? ` - ${option.options.desc}` : "";
78
+ console.log(` --${option.key} (${type})${required}${desc}`);
79
+ }
80
+ process.exit(0);
81
+ }
82
+
83
+ // Second pass: join split parameters
84
+ let processedParams: string[] = [];
85
+ for (let i = 0; i < params.length; i++) {
86
+ let key = params[i];
87
+
88
+ // Only treat as option if it's in the options list
89
+ let optionConfig = config.options.find(opt => opt.key === key);
90
+ if (optionConfig && optionConfig.options.type !== "boolean") {
91
+ let nextParam = params[i + 1]; // Use original for value
92
+ // Join them: x 5 becomes x=5
93
+ processedParams.push(`${key}=${nextParam}`);
94
+ i++; // Skip the next parameter
95
+ continue;
96
+ }
97
+
98
+ processedParams.push(key);
99
+ }
100
+
101
+ // Second pass: process the parameters
102
+ for (let param of processedParams) {
103
+ // Check if it's a key=value format
104
+ if (param.includes("=")) {
105
+ let [optionKey, ...valueParts] = param.split("=");
106
+ let value = valueParts.join("=");
107
+ let optionConfig = config.options.find(opt => opt.key === optionKey);
108
+
109
+ if (optionConfig) {
110
+ let { type } = optionConfig.options;
111
+ if (type === "boolean") {
112
+ result[optionKey] = value === "true";
113
+ } else if (type === "number") {
114
+ result[optionKey] = Number(value);
115
+ } else if (type === "array") {
116
+ if (!result[optionKey]) result[optionKey] = [];
117
+ (result[optionKey] as unknown[]).push(value);
118
+ } else {
119
+ result[optionKey] = value;
120
+ }
121
+ } else {
122
+ result[optionKey] = value;
123
+ }
124
+ } else {
125
+ // Regular option (boolean)
126
+ let optionConfig = config.options.find(opt => opt.key === param);
127
+
128
+ if (optionConfig) {
129
+ let { type } = optionConfig.options;
130
+
131
+ if (type === "boolean") {
132
+ result[param] = true;
133
+ } else {
134
+ // Other types should have been joined in first pass
135
+ // Set default if somehow we get here
136
+ if (type === "number") {
137
+ result[param] = 0;
138
+ } else if (type === "array") {
139
+ result[param] = [];
140
+ } else {
141
+ result[param] = "";
142
+ }
143
+ }
144
+ } else {
145
+ // Unknown option, treat as boolean
146
+ result[param] = true;
147
+ }
148
+ }
149
+ }
150
+
151
+ // Set default values for required options that weren't provided
152
+ for (let option of config.options) {
153
+ if (option.options.required && result[option.key] === undefined) {
154
+ if (option.options.type === "boolean") {
155
+ result[option.key] = false;
156
+ } else if (option.options.type === "array") {
157
+ result[option.key] = [];
158
+ } else if (option.options.type === "number") {
159
+ result[option.key] = 0;
160
+ } else {
161
+ result[option.key] = "";
162
+ }
163
+ }
164
+ }
165
+
166
+ return result;
167
+ }
168
+
169
+ function createCallback(config: CurrentConfig): Argv {
170
+ const callback = {
171
+ option: (key: string, options: Options) => {
172
+ config.options.push({ key, options });
173
+ return createCallback(config) as any;
174
+ },
175
+ get argv() {
176
+ return finish(config);
177
+ }
178
+ };
179
+
180
+ // Make the callback callable
181
+ const fn = (() => callback.argv) as any;
182
+ fn.option = callback.option;
183
+ fn.argv = callback.argv;
184
+
185
+ return fn;
186
+ }
187
+
188
+ return createCallback({ options: [] });
189
+ }