@openclaw/lobster 2026.9.1-beta.1 → 2026.9.1
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/dist/index.js +130 -112
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/.github/workflows/ci.yml +1 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/.github/workflows/lock-threads.yml +1 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/SECURITY.md +6 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/benchmark/benchmark.mjs +71 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/index.js +223 -43
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/lib/schemes.js +384 -7
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/lib/utils.js +310 -89
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/package.json +1 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/component-safe-serialization.test.js +163 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/equal.test.js +43 -3
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/fixtures/uri-js-parse.json +24 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/fixtures/uri-js-serialize.json +23 -1
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/ipv6-validation.test.js +154 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/parse.test.js +145 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/scheme-validation.test.js +122 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/security-normalization.test.js +101 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/security-utf8-encoding.test.js +0 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/security.test.js +75 -3
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/serialize.test.js +144 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/uri-js-compatibility.test.js +0 -3
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/uri-js.test.js +3 -3
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/types/index.d.ts +4 -0
- package/node_modules/@clawdbot/lobster/node_modules/fast-uri/types/index.tst.ts +16 -0
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -35,10 +35,10 @@ function createLimitedSink(maxBytes, label) {
|
|
|
35
35
|
callback();
|
|
36
36
|
} });
|
|
37
37
|
}
|
|
38
|
-
function normalizeEnvelope(envelope) {
|
|
38
|
+
function normalizeEnvelope(envelope, maxStdoutBytes) {
|
|
39
39
|
if (!envelope.ok) throw new Error(envelope.error?.message ?? "lobster runtime failed");
|
|
40
40
|
if (envelope.status === "needs_input") throw new Error("Lobster input requests are not supported by the OpenClaw Lobster tool yet");
|
|
41
|
-
|
|
41
|
+
const normalized = {
|
|
42
42
|
ok: true,
|
|
43
43
|
status: envelope.status ?? "ok",
|
|
44
44
|
output: Array.isArray(envelope.output) ? envelope.output : [],
|
|
@@ -50,6 +50,8 @@ function normalizeEnvelope(envelope) {
|
|
|
50
50
|
...envelope.requiresApproval.approvalId ? { approvalId: envelope.requiresApproval.approvalId } : {}
|
|
51
51
|
} : null
|
|
52
52
|
};
|
|
53
|
+
if (Buffer.byteLength(JSON.stringify(normalized, null, 2), "utf8") > maxStdoutBytes) throw new Error("lobster runtime result exceeded maxStdoutBytes");
|
|
54
|
+
return normalized;
|
|
53
55
|
}
|
|
54
56
|
function isMissingPathError(error) {
|
|
55
57
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
@@ -112,6 +114,7 @@ function createEmbeddedLobsterRunner(options) {
|
|
|
112
114
|
const runtime = await runtimePromise;
|
|
113
115
|
return await withTimeout(params.timeoutMs, async (signal) => {
|
|
114
116
|
const ctx = createEmbeddedToolContext(params, signal);
|
|
117
|
+
let envelope;
|
|
115
118
|
if (params.action === "run") {
|
|
116
119
|
const pipeline = params.pipeline?.trim() ?? "";
|
|
117
120
|
if (!pipeline) throw new Error("pipeline required");
|
|
@@ -124,27 +127,28 @@ function createEmbeddedLobsterRunner(options) {
|
|
|
124
127
|
} catch {
|
|
125
128
|
throw new Error("run --args-json must be valid JSON");
|
|
126
129
|
}
|
|
127
|
-
|
|
130
|
+
envelope = await runtime.runToolRequest({
|
|
128
131
|
filePath,
|
|
129
132
|
args,
|
|
130
133
|
ctx
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
return normalizeEnvelope(await runtime.runToolRequest({
|
|
134
|
+
});
|
|
135
|
+
} else envelope = await runtime.runToolRequest({
|
|
134
136
|
pipeline,
|
|
135
137
|
ctx
|
|
136
|
-
})
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
const token = params.token?.trim() ?? "";
|
|
141
|
+
const approvalId = params.approvalId?.trim() ?? "";
|
|
142
|
+
if (!token && !approvalId) throw new Error("token or approvalId required");
|
|
143
|
+
if (typeof params.approve !== "boolean") throw new Error("approve required");
|
|
144
|
+
envelope = await runtime.resumeToolRequest({
|
|
145
|
+
...token ? { token } : {},
|
|
146
|
+
...approvalId ? { approvalId } : {},
|
|
147
|
+
approved: params.approve,
|
|
148
|
+
ctx
|
|
149
|
+
});
|
|
137
150
|
}
|
|
138
|
-
|
|
139
|
-
const approvalId = params.approvalId?.trim() ?? "";
|
|
140
|
-
if (!token && !approvalId) throw new Error("token or approvalId required");
|
|
141
|
-
if (typeof params.approve !== "boolean") throw new Error("approve required");
|
|
142
|
-
return normalizeEnvelope(await runtime.resumeToolRequest({
|
|
143
|
-
...token ? { token } : {},
|
|
144
|
-
...approvalId ? { approvalId } : {},
|
|
145
|
-
approved: params.approve,
|
|
146
|
-
ctx
|
|
147
|
-
}));
|
|
151
|
+
return normalizeEnvelope(envelope, Math.max(1024, params.maxStdoutBytes));
|
|
148
152
|
});
|
|
149
153
|
} };
|
|
150
154
|
}
|
|
@@ -152,29 +156,25 @@ function createEmbeddedLobsterRunner(options) {
|
|
|
152
156
|
//#region extensions/lobster/src/lobster-taskflow.ts
|
|
153
157
|
function toJsonLike(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
154
158
|
if (value === null) return null;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
jsonObject[key] = toJsonLike(entry, seen);
|
|
172
|
-
}
|
|
173
|
-
seen.delete(value);
|
|
174
|
-
return jsonObject;
|
|
175
|
-
}
|
|
159
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
|
|
160
|
+
if (typeof value === "bigint") return value.toString();
|
|
161
|
+
if (typeof value === "boolean" || typeof value === "string") return value;
|
|
162
|
+
if (typeof value !== "object") return null;
|
|
163
|
+
if (value instanceof Date) return value.toISOString();
|
|
164
|
+
if (seen.has(value)) return "[Circular]";
|
|
165
|
+
seen.add(value);
|
|
166
|
+
if (Array.isArray(value)) {
|
|
167
|
+
const jsonArray = value.map((item) => toJsonLike(item, seen));
|
|
168
|
+
seen.delete(value);
|
|
169
|
+
return jsonArray;
|
|
170
|
+
}
|
|
171
|
+
const jsonObject = {};
|
|
172
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
173
|
+
if (entry === void 0 || typeof entry === "function" || typeof entry === "symbol") continue;
|
|
174
|
+
jsonObject[key] = toJsonLike(entry, seen);
|
|
176
175
|
}
|
|
177
|
-
|
|
176
|
+
seen.delete(value);
|
|
177
|
+
return jsonObject;
|
|
178
178
|
}
|
|
179
179
|
function buildApprovalWaitState(envelope) {
|
|
180
180
|
const approval = envelope.requiresApproval;
|
|
@@ -186,40 +186,51 @@ function buildApprovalWaitState(envelope) {
|
|
|
186
186
|
...approval?.approvalId ? { approvalId: approval.approvalId } : {}
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
|
-
function
|
|
190
|
-
const { taskFlow, flow, envelope, waitingStep } = params;
|
|
191
|
-
const flowMutation = {
|
|
192
|
-
flowId: flow.flowId,
|
|
193
|
-
expectedRevision: flow.revision
|
|
194
|
-
};
|
|
195
|
-
if (!envelope.ok) return taskFlow.fail(flowMutation);
|
|
196
|
-
if (envelope.status === "needs_approval") return taskFlow.setWaiting({
|
|
197
|
-
...flowMutation,
|
|
198
|
-
currentStep: waitingStep,
|
|
199
|
-
waitJson: buildApprovalWaitState(envelope)
|
|
200
|
-
});
|
|
201
|
-
return taskFlow.finish(flowMutation);
|
|
202
|
-
}
|
|
203
|
-
async function executeManagedLobsterFlow(params, flow, failureFlowId = flow.flowId) {
|
|
189
|
+
async function executeManagedLobsterFlow(params, flow) {
|
|
204
190
|
try {
|
|
205
191
|
const envelope = await params.runner.run(params.runnerParams);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
192
|
+
if (envelope.ok && envelope.status === "cancelled") try {
|
|
193
|
+
const mutation = await params.taskFlow.cancel({
|
|
194
|
+
flowId: flow.flowId,
|
|
195
|
+
cfg: params.config
|
|
196
|
+
});
|
|
197
|
+
return mutation.cancelled ? {
|
|
198
|
+
ok: true,
|
|
199
|
+
envelope,
|
|
200
|
+
flow,
|
|
201
|
+
mutation
|
|
202
|
+
} : {
|
|
203
|
+
ok: false,
|
|
204
|
+
flow,
|
|
205
|
+
mutation,
|
|
206
|
+
error: /* @__PURE__ */ new Error(`TaskFlow cancellation failed: ${mutation.reason ?? "unknown"}`)
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
flow,
|
|
212
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const flowMutation = {
|
|
216
|
+
flowId: flow.flowId,
|
|
217
|
+
expectedRevision: flow.revision
|
|
218
|
+
};
|
|
212
219
|
if (!envelope.ok) return {
|
|
213
220
|
ok: false,
|
|
214
221
|
flow,
|
|
215
|
-
mutation,
|
|
222
|
+
mutation: params.taskFlow.fail(flowMutation),
|
|
216
223
|
error: new Error(envelope.error.message)
|
|
217
224
|
};
|
|
218
225
|
return {
|
|
219
226
|
ok: true,
|
|
220
227
|
envelope,
|
|
221
228
|
flow,
|
|
222
|
-
mutation
|
|
229
|
+
mutation: envelope.status === "needs_approval" ? params.taskFlow.setWaiting({
|
|
230
|
+
...flowMutation,
|
|
231
|
+
currentStep: params.waitingStep ?? "await_lobster_approval",
|
|
232
|
+
waitJson: buildApprovalWaitState(envelope)
|
|
233
|
+
}) : params.taskFlow.finish(flowMutation)
|
|
223
234
|
};
|
|
224
235
|
} catch (error) {
|
|
225
236
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -228,7 +239,7 @@ async function executeManagedLobsterFlow(params, flow, failureFlowId = flow.flow
|
|
|
228
239
|
ok: false,
|
|
229
240
|
flow,
|
|
230
241
|
mutation: params.taskFlow.fail({
|
|
231
|
-
flowId:
|
|
242
|
+
flowId: flow.flowId,
|
|
232
243
|
expectedRevision: flow.revision
|
|
233
244
|
}),
|
|
234
245
|
error: err
|
|
@@ -268,7 +279,7 @@ async function resumeManagedLobsterFlow(params) {
|
|
|
268
279
|
mutation: resumed,
|
|
269
280
|
error: /* @__PURE__ */ new Error(`TaskFlow resume failed: ${resumed.code}`)
|
|
270
281
|
};
|
|
271
|
-
return await executeManagedLobsterFlow(params, resumed.flow
|
|
282
|
+
return await executeManagedLobsterFlow(params, resumed.flow);
|
|
272
283
|
}
|
|
273
284
|
//#endregion
|
|
274
285
|
//#region extensions/lobster/src/lobster-tool.ts
|
|
@@ -300,28 +311,29 @@ function parseOptionalFlowStateJson(value) {
|
|
|
300
311
|
function isEmptyJsonObject(value) {
|
|
301
312
|
return value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
302
313
|
}
|
|
303
|
-
function
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
314
|
+
function parseManagedFlowParams(action, params, runnerParams) {
|
|
315
|
+
if (action === "run") {
|
|
316
|
+
const controllerId = readOptionalTrimmedString(params.flowControllerId, "flowControllerId");
|
|
317
|
+
const goal = readOptionalTrimmedString(params.flowGoal, "flowGoal");
|
|
318
|
+
const currentStep = readOptionalTrimmedString(params.flowCurrentStep, "flowCurrentStep");
|
|
319
|
+
const waitingStep = readOptionalTrimmedString(params.flowWaitingStep, "flowWaitingStep");
|
|
320
|
+
const stateJson = parseOptionalFlowStateJson(params.flowStateJson);
|
|
321
|
+
const resumeFlowId = readOptionalTrimmedString(params.flowId, "flowId");
|
|
322
|
+
const resumeRevision = readOptionalNumber(params.flowExpectedRevision, "flowExpectedRevision");
|
|
323
|
+
const stateJsonSignalsRunMode = stateJson !== void 0 && !isEmptyJsonObject(stateJson);
|
|
324
|
+
if (resumeFlowId !== void 0 || resumeRevision !== void 0 && resumeRevision !== 0) throw new Error("run action does not accept flowId or flowExpectedRevision");
|
|
325
|
+
if (controllerId === void 0 && goal === void 0 && currentStep === void 0 && waitingStep === void 0 && !stateJsonSignalsRunMode) return null;
|
|
326
|
+
if (!controllerId) throw new Error("flowControllerId required when using managed TaskFlow run mode");
|
|
327
|
+
if (!goal) throw new Error("flowGoal required when using managed TaskFlow run mode");
|
|
328
|
+
return {
|
|
329
|
+
action,
|
|
330
|
+
controllerId,
|
|
331
|
+
goal,
|
|
332
|
+
...currentStep ? { currentStep } : {},
|
|
333
|
+
...waitingStep ? { waitingStep } : {},
|
|
334
|
+
...stateJson !== void 0 ? { stateJson } : {}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
325
337
|
const flowId = readOptionalTrimmedString(params.flowId, "flowId");
|
|
326
338
|
const expectedRevision = readOptionalNumber(params.flowExpectedRevision, "flowExpectedRevision");
|
|
327
339
|
const currentStep = readOptionalTrimmedString(params.flowCurrentStep, "flowCurrentStep");
|
|
@@ -337,13 +349,21 @@ function parseResumeFlowParams(params) {
|
|
|
337
349
|
if (!(flowId !== void 0 || expectedRevision !== void 0 && expectedRevision !== 0 || currentStep !== void 0 || waitingStep !== void 0)) return null;
|
|
338
350
|
if (!flowId) throw new Error("flowId required when using managed TaskFlow resume mode");
|
|
339
351
|
if (expectedRevision === void 0) throw new Error("flowExpectedRevision required when using managed TaskFlow resume mode");
|
|
340
|
-
|
|
352
|
+
const credentialParams = token ? { token: runnerParams.token ?? token } : approvalId ? { approvalId: runnerParams.approvalId ?? approvalId } : null;
|
|
353
|
+
if (!credentialParams) throw new Error("token or approvalId required when using managed TaskFlow resume mode");
|
|
341
354
|
if (approve === void 0) throw new Error("approve required when using managed TaskFlow resume mode");
|
|
342
355
|
return {
|
|
356
|
+
action,
|
|
343
357
|
flowId,
|
|
344
358
|
expectedRevision,
|
|
345
359
|
...currentStep ? { currentStep } : {},
|
|
346
|
-
...waitingStep ? { waitingStep } : {}
|
|
360
|
+
...waitingStep ? { waitingStep } : {},
|
|
361
|
+
runnerParams: {
|
|
362
|
+
...runnerParams,
|
|
363
|
+
...credentialParams,
|
|
364
|
+
action,
|
|
365
|
+
approve
|
|
366
|
+
}
|
|
347
367
|
};
|
|
348
368
|
}
|
|
349
369
|
function resolveManagedFlowToolResult(result) {
|
|
@@ -402,30 +422,28 @@ function createLobsterTool(api, options) {
|
|
|
402
422
|
maxStdoutBytes
|
|
403
423
|
};
|
|
404
424
|
const taskFlow = options?.taskFlow;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}));
|
|
428
|
-
}
|
|
425
|
+
const flowParams = parseManagedFlowParams(action, params, runnerParams);
|
|
426
|
+
if (flowParams?.action === "run") return resolveManagedFlowToolResult(await runManagedLobsterFlow({
|
|
427
|
+
taskFlow: requireTaskFlowRuntime(taskFlow, "run"),
|
|
428
|
+
config: api.config,
|
|
429
|
+
runner,
|
|
430
|
+
runnerParams,
|
|
431
|
+
controllerId: flowParams.controllerId,
|
|
432
|
+
goal: flowParams.goal,
|
|
433
|
+
...flowParams.stateJson !== void 0 ? { stateJson: flowParams.stateJson } : {},
|
|
434
|
+
...flowParams.currentStep ? { currentStep: flowParams.currentStep } : {},
|
|
435
|
+
...flowParams.waitingStep ? { waitingStep: flowParams.waitingStep } : {}
|
|
436
|
+
}));
|
|
437
|
+
if (flowParams?.action === "resume") return resolveManagedFlowToolResult(await resumeManagedLobsterFlow({
|
|
438
|
+
taskFlow: requireTaskFlowRuntime(taskFlow, "resume"),
|
|
439
|
+
config: api.config,
|
|
440
|
+
runner,
|
|
441
|
+
runnerParams: flowParams.runnerParams,
|
|
442
|
+
flowId: flowParams.flowId,
|
|
443
|
+
expectedRevision: flowParams.expectedRevision,
|
|
444
|
+
...flowParams.currentStep ? { currentStep: flowParams.currentStep } : {},
|
|
445
|
+
...flowParams.waitingStep ? { waitingStep: flowParams.waitingStep } : {}
|
|
446
|
+
}));
|
|
429
447
|
const envelope = await runner.run(runnerParams);
|
|
430
448
|
if (!envelope.ok) throw new Error(envelope.error.message);
|
|
431
449
|
return jsonResult(envelope);
|
|
@@ -147,7 +147,7 @@ jobs:
|
|
|
147
147
|
permissions:
|
|
148
148
|
contents: write
|
|
149
149
|
pull-requests: write
|
|
150
|
-
uses: fastify/workflows/.github/workflows/plugins-ci.yml@
|
|
150
|
+
uses: fastify/workflows/.github/workflows/plugins-ci.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
|
|
151
151
|
with:
|
|
152
152
|
license-check: true
|
|
153
153
|
lint: true
|
package/node_modules/@clawdbot/lobster/node_modules/fast-uri/.github/workflows/lock-threads.yml
CHANGED
|
@@ -16,4 +16,4 @@ jobs:
|
|
|
16
16
|
permissions:
|
|
17
17
|
issues: write
|
|
18
18
|
pull-requests: write
|
|
19
|
-
uses: fastify/workflows/.github/workflows/lock-threads.yml@
|
|
19
|
+
uses: fastify/workflows/.github/workflows/lock-threads.yml@ef591e2186785d5ab36b9fe6a79c7ce2f1d94e57 # v7.0.0
|
|
@@ -11,10 +11,15 @@ official Fastify project package. It supplements the Fastify project's
|
|
|
11
11
|
[WHATWG URL Standard](https://url.spec.whatwg.org/).
|
|
12
12
|
|
|
13
13
|
Differences between `fast-uri` and WHATWG URL implementations are expected.
|
|
14
|
-
Reports based on comparing or mixing parsers that follow these different
|
|
14
|
+
Reports based solely on comparing or mixing parsers that follow these different
|
|
15
15
|
standards are out of scope. Applications must use the same parsing and
|
|
16
16
|
normalization rules for both security decisions and subsequent URI use.
|
|
17
17
|
|
|
18
|
+
Reports are assessed based on reproducibility, documented supported usage, and
|
|
19
|
+
concrete security impact—not merely on whether they mention WHATWG URL
|
|
20
|
+
behavior. Reports demonstrating a parsing or normalization flaw in a supported
|
|
21
|
+
scheme remain eligible for evaluation.
|
|
22
|
+
|
|
18
23
|
## Threat model
|
|
19
24
|
|
|
20
25
|
`fast-uri`'s threat model extends the
|
|
@@ -10,6 +10,21 @@ const ipv6 = '//[2001:db8::7]'
|
|
|
10
10
|
const urn = 'urn:foo:a123,456'
|
|
11
11
|
const urnuuid = 'urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6'
|
|
12
12
|
|
|
13
|
+
const mailtoSimple = 'mailto:chris@example.com'
|
|
14
|
+
const mailtoWithSubject = 'mailto:infobot@example.com?subject=current-issue'
|
|
15
|
+
const mailtoWithBody = 'mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index'
|
|
16
|
+
const mailtoWithHeaders = 'mailto:list@example.org?In-Reply-To=%3C3469A91.D10AF4C@example.com%3E'
|
|
17
|
+
const mailtoMultiple = 'mailto:joe@example.com,alice@example.org?subject=Test&body=NATTO'
|
|
18
|
+
const mailtoEncoded = 'mailto:%22oh%5C%5Cno%22@example.org'
|
|
19
|
+
|
|
20
|
+
const mailtoComponent = {
|
|
21
|
+
scheme: 'mailto',
|
|
22
|
+
to: ['chris@example.com'],
|
|
23
|
+
subject: 'current-issue',
|
|
24
|
+
body: 'send current-issue\r\nsend index',
|
|
25
|
+
headers: { 'In-Reply-To': '<3469A91.D10AF4C@example.com>' }
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
const urnuuidComponent = {
|
|
14
29
|
scheme: 'urn',
|
|
15
30
|
nid: 'uuid',
|
|
@@ -146,6 +161,62 @@ benchUriJs.add('urijs: equal', function () {
|
|
|
146
161
|
uriJsEqual('example://a/b/c/%7Bfoo%7D', 'eXAMPLE://a/./b/../b/%63/%7bfoo%7d')
|
|
147
162
|
})
|
|
148
163
|
|
|
164
|
+
benchFastUri.add('fast-uri: parse mailto simple', function () {
|
|
165
|
+
fastUriParse(mailtoSimple)
|
|
166
|
+
})
|
|
167
|
+
benchUriJs.add('urijs: parse mailto simple', function () {
|
|
168
|
+
uriJsParse(mailtoSimple)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
benchFastUri.add('fast-uri: parse mailto subject', function () {
|
|
172
|
+
fastUriParse(mailtoWithSubject)
|
|
173
|
+
})
|
|
174
|
+
benchUriJs.add('urijs: parse mailto subject', function () {
|
|
175
|
+
uriJsParse(mailtoWithSubject)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
benchFastUri.add('fast-uri: parse mailto body CRLF', function () {
|
|
179
|
+
fastUriParse(mailtoWithBody)
|
|
180
|
+
})
|
|
181
|
+
benchUriJs.add('urijs: parse mailto body CRLF', function () {
|
|
182
|
+
uriJsParse(mailtoWithBody)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
benchFastUri.add('fast-uri: parse mailto headers', function () {
|
|
186
|
+
fastUriParse(mailtoWithHeaders)
|
|
187
|
+
})
|
|
188
|
+
benchUriJs.add('urijs: parse mailto headers', function () {
|
|
189
|
+
uriJsParse(mailtoWithHeaders)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
benchFastUri.add('fast-uri: parse mailto multi recipient', function () {
|
|
193
|
+
fastUriParse(mailtoMultiple)
|
|
194
|
+
})
|
|
195
|
+
benchUriJs.add('urijs: parse mailto multi recipient', function () {
|
|
196
|
+
uriJsParse(mailtoMultiple)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
benchFastUri.add('fast-uri: parse mailto encoded local', function () {
|
|
200
|
+
fastUriParse(mailtoEncoded)
|
|
201
|
+
})
|
|
202
|
+
benchUriJs.add('urijs: parse mailto encoded local', function () {
|
|
203
|
+
uriJsParse(mailtoEncoded)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
benchFastUri.add('fast-uri: serialize mailto', function () {
|
|
207
|
+
fastUriSerialize(mailtoComponent)
|
|
208
|
+
})
|
|
209
|
+
benchUriJs.add('urijs: serialize mailto', function () {
|
|
210
|
+
uriJsSerialize(mailtoComponent)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
benchFastUri.add('fast-uri: serialize+parse mailto round-trip', function () {
|
|
214
|
+
fastUriSerialize(fastUriParse(mailtoWithBody))
|
|
215
|
+
})
|
|
216
|
+
benchUriJs.add('urijs: serialize+parse mailto round-trip', function () {
|
|
217
|
+
uriJsSerialize(uriJsParse(mailtoWithBody))
|
|
218
|
+
})
|
|
219
|
+
|
|
149
220
|
await benchFastUri.run()
|
|
150
221
|
console.log(benchFastUri.name)
|
|
151
222
|
console.table(benchFastUri.table())
|