@rogatio/cli 6.16.4 → 6.16.5

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 (2) hide show
  1. package/dist/node/index.js +210 -82
  2. package/package.json +1 -1
@@ -2200,19 +2200,21 @@ function compileProject(value) {
2200
2200
  };
2201
2201
  }
2202
2202
  }
2203
- function sourceMatches(source, url) {
2204
- const regex = compileUrlRegex(source.value);
2205
- if (regex === null) return false;
2206
- if (source.key === "url") {
2207
- return regex.test(url);
2208
- }
2203
+ function sourceMatchText(source, url) {
2204
+ if (source.key === "url") return url;
2209
2205
  try {
2210
- const hostname = new URL(url).hostname;
2211
- return regex.test(hostname);
2206
+ return new URL(url).hostname;
2212
2207
  } catch {
2213
- return false;
2208
+ return null;
2214
2209
  }
2215
2210
  }
2211
+ function sourceMatches(source, url) {
2212
+ const regex = compileUrlRegex(source.value);
2213
+ if (regex === null) return false;
2214
+ const text = sourceMatchText(source, url);
2215
+ if (text === null) return false;
2216
+ return regex.test(text);
2217
+ }
2216
2218
  var PAC_STEER_MARKER = "steer:";
2217
2219
  function decodePacSteer(entry) {
2218
2220
  if (!entry.startsWith(PAC_STEER_MARKER)) return null;
@@ -24419,29 +24421,6 @@ async function resolveConfinedRoot(value) {
24419
24421
  await directory?.close().catch(() => void 0);
24420
24422
  }
24421
24423
  }
24422
- var MOCK_LISTENER_PREFIX = "/.rogatio/mock/";
24423
- function isSingleSegment(value) {
24424
- return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
24425
- }
24426
- function parseMockRedirect(rawUrl) {
24427
- const queryAt = rawUrl.indexOf("?");
24428
- const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
24429
- if (!path2.startsWith(MOCK_LISTENER_PREFIX)) return null;
24430
- const rest = path2.slice(MOCK_LISTENER_PREFIX.length);
24431
- const slash = rest.indexOf("/");
24432
- if (slash <= 0) return null;
24433
- if (rest.indexOf("/", slash + 1) !== -1) return null;
24434
- let token;
24435
- let digest;
24436
- try {
24437
- token = decodeURIComponent(rest.slice(0, slash));
24438
- digest = decodeURIComponent(rest.slice(slash + 1));
24439
- } catch {
24440
- return null;
24441
- }
24442
- if (!isSingleSegment(token) || !isSingleSegment(digest)) return null;
24443
- return { token, digest };
24444
- }
24445
24424
  var REGEX_WORKER_SOURCE = `
24446
24425
  const workerThreads =
24447
24426
  typeof process.getBuiltinModule === "function"
@@ -24450,11 +24429,35 @@ const workerThreads =
24450
24429
  const { parentPort } = workerThreads;
24451
24430
  parentPort.postMessage({ type: "ready" });
24452
24431
  parentPort.on("message", (msg) => {
24453
- if (msg.type !== "replace") return;
24432
+ if (msg.type !== "replace" && msg.type !== "test" && msg.type !== "exec") return;
24454
24433
  parentPort.postMessage({ id: msg.id, type: "ack" });
24455
24434
  try {
24456
- const out = msg.text.replace(new RegExp(msg.pattern, "gu"), msg.replacement);
24457
- parentPort.postMessage({ id: msg.id, type: "result", ok: true, out });
24435
+ if (msg.type === "replace") {
24436
+ const out = msg.text.replace(new RegExp(msg.pattern, "gu"), msg.replacement);
24437
+ parentPort.postMessage({ id: msg.id, type: "result", ok: true, out });
24438
+ return;
24439
+ }
24440
+ const regex = new RegExp(msg.pattern);
24441
+ if (msg.type === "test") {
24442
+ parentPort.postMessage({
24443
+ id: msg.id,
24444
+ type: "result",
24445
+ ok: true,
24446
+ matched: regex.test(msg.text),
24447
+ });
24448
+ return;
24449
+ }
24450
+ const match = regex.exec(msg.text);
24451
+ const captures =
24452
+ match === null
24453
+ ? null
24454
+ : match.slice(1).map((part) => (part === undefined ? null : part));
24455
+ parentPort.postMessage({
24456
+ id: msg.id,
24457
+ type: "result",
24458
+ ok: true,
24459
+ captures,
24460
+ });
24458
24461
  } catch (err) {
24459
24462
  parentPort.postMessage({
24460
24463
  id: msg.id,
@@ -24470,6 +24473,7 @@ var regexWorker = null;
24470
24473
  var regexWorkerBoot = null;
24471
24474
  var nextRegexJobId = 1;
24472
24475
  var regexWorkerBootDelayForTestsMs = 0;
24476
+ var failNextRegexWorkerBootForTests = false;
24473
24477
  function discardRegexWorker(worker) {
24474
24478
  if (regexWorker === worker) {
24475
24479
  regexWorker = null;
@@ -24479,6 +24483,10 @@ function discardRegexWorker(worker) {
24479
24483
  });
24480
24484
  }
24481
24485
  function bootRegexWorker() {
24486
+ if (failNextRegexWorkerBootForTests) {
24487
+ failNextRegexWorkerBootForTests = false;
24488
+ return Promise.reject(new Error("regex-worker-boot-failed"));
24489
+ }
24482
24490
  if (regexWorker !== null) return Promise.resolve(regexWorker);
24483
24491
  if (regexWorkerBoot !== null) return regexWorkerBoot;
24484
24492
  const boot = new Promise((resolve12, reject) => {
@@ -24546,7 +24554,7 @@ function bootRegexWorker() {
24546
24554
  });
24547
24555
  return boot;
24548
24556
  }
24549
- function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24557
+ function runOnRegexWorker(worker, id2, type, text, pattern, replacement, deadlineMs) {
24550
24558
  return new Promise((resolve12, reject) => {
24551
24559
  let timer = null;
24552
24560
  const cleanup = () => {
@@ -24567,8 +24575,15 @@ function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24567
24575
  }
24568
24576
  if (msg.type === "result") {
24569
24577
  cleanup();
24570
- if (msg.ok && typeof msg.out === "string") resolve12(msg.out);
24571
- else reject(new Error(msg.error || "regex-invalid"));
24578
+ if (!msg.ok) {
24579
+ reject(new Error(msg.error || "regex-invalid"));
24580
+ return;
24581
+ }
24582
+ resolve12({
24583
+ ...typeof msg.out === "string" ? { out: msg.out } : {},
24584
+ ...typeof msg.matched === "boolean" ? { matched: msg.matched } : {},
24585
+ ...msg.captures !== void 0 ? { captures: msg.captures } : {}
24586
+ });
24572
24587
  }
24573
24588
  };
24574
24589
  const onError = (error) => {
@@ -24582,23 +24597,109 @@ function runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs) {
24582
24597
  worker.on("message", onMessage);
24583
24598
  worker.once("error", onError);
24584
24599
  worker.once("exit", onExit);
24585
- worker.postMessage({ id: id2, type: "replace", text, pattern, replacement });
24600
+ worker.postMessage({
24601
+ id: id2,
24602
+ type,
24603
+ text,
24604
+ pattern,
24605
+ ...replacement !== void 0 ? { replacement } : {}
24606
+ });
24586
24607
  });
24587
24608
  }
24588
- function runRegexReplace(text, pattern, replacement, deadlineMs) {
24609
+ function nextJobId() {
24589
24610
  const id2 = nextRegexJobId;
24590
24611
  nextRegexJobId += 1;
24612
+ return id2;
24613
+ }
24614
+ function runRegexReplace(text, pattern, replacement, deadlineMs) {
24615
+ const id2 = nextJobId();
24591
24616
  return bootRegexWorker().then(
24592
- (worker) => runOnRegexWorker(worker, id2, text, pattern, replacement, deadlineMs),
24593
- () => {
24594
- try {
24595
- return text.replace(new RegExp(pattern, "gu"), replacement);
24596
- } catch {
24597
- throw new Error("regex-invalid");
24598
- }
24599
- }
24617
+ (worker) => runOnRegexWorker(
24618
+ worker,
24619
+ id2,
24620
+ "replace",
24621
+ text,
24622
+ pattern,
24623
+ replacement,
24624
+ deadlineMs
24625
+ ).then((result) => {
24626
+ if (typeof result.out !== "string") throw new Error("regex-invalid");
24627
+ return result.out;
24628
+ })
24629
+ );
24630
+ }
24631
+ function capturesFromWorker(captures) {
24632
+ if (captures === null || captures === void 0) return null;
24633
+ return captures.map((part) => part === null ? void 0 : part);
24634
+ }
24635
+ function runRegexTest(text, pattern, deadlineMs) {
24636
+ const id2 = nextJobId();
24637
+ return bootRegexWorker().then(
24638
+ (worker) => runOnRegexWorker(
24639
+ worker,
24640
+ id2,
24641
+ "test",
24642
+ text,
24643
+ pattern,
24644
+ void 0,
24645
+ deadlineMs
24646
+ ).then(
24647
+ (result) => result.matched === true,
24648
+ () => false
24649
+ ),
24650
+ () => false
24651
+ );
24652
+ }
24653
+ function runRegexExec(text, pattern, deadlineMs) {
24654
+ const id2 = nextJobId();
24655
+ return bootRegexWorker().then(
24656
+ (worker) => runOnRegexWorker(
24657
+ worker,
24658
+ id2,
24659
+ "exec",
24660
+ text,
24661
+ pattern,
24662
+ void 0,
24663
+ deadlineMs
24664
+ ).then(
24665
+ (result) => capturesFromWorker(result.captures),
24666
+ () => null
24667
+ ),
24668
+ () => null
24600
24669
  );
24601
24670
  }
24671
+ async function sourceMatchesWithinDeadline(source, url) {
24672
+ if (compileUrlRegex(source.value) === null) return false;
24673
+ const text = sourceMatchText(source, url);
24674
+ if (text === null) return false;
24675
+ return runRegexTest(text, source.value, RUNTIME_LIMITS.maxRegexDeadlineMs);
24676
+ }
24677
+ function matchUrlCapturesWithinDeadline(urlRegex, url) {
24678
+ return runRegexExec(url, urlRegex, RUNTIME_LIMITS.maxRegexDeadlineMs);
24679
+ }
24680
+ var MOCK_LISTENER_PREFIX = "/.rogatio/mock/";
24681
+ function isSingleSegment(value) {
24682
+ return value.length > 0 && value.length <= 2048 && value !== "." && value !== ".." && !value.includes("/") && !value.includes("\\") && !value.includes("?") && !hasControl(value);
24683
+ }
24684
+ function parseMockRedirect(rawUrl) {
24685
+ const queryAt = rawUrl.indexOf("?");
24686
+ const path2 = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
24687
+ if (!path2.startsWith(MOCK_LISTENER_PREFIX)) return null;
24688
+ const rest = path2.slice(MOCK_LISTENER_PREFIX.length);
24689
+ const slash = rest.indexOf("/");
24690
+ if (slash <= 0) return null;
24691
+ if (rest.indexOf("/", slash + 1) !== -1) return null;
24692
+ let token;
24693
+ let digest;
24694
+ try {
24695
+ token = decodeURIComponent(rest.slice(0, slash));
24696
+ digest = decodeURIComponent(rest.slice(slash + 1));
24697
+ } catch {
24698
+ return null;
24699
+ }
24700
+ if (!isSingleSegment(token) || !isSingleSegment(digest)) return null;
24701
+ return { token, digest };
24702
+ }
24602
24703
  function supportedContentType(value) {
24603
24704
  if (value === void 0) return false;
24604
24705
  const mediaType = value.split(";", 1)[0]?.trim().toLowerCase() ?? "";
@@ -24630,7 +24731,7 @@ async function rewriteRequestBody(input, action, captureContext) {
24630
24731
  }
24631
24732
  outputText = action.body;
24632
24733
  if (captureContext !== void 0) {
24633
- const captures = matchUrlCaptures(
24734
+ const captures = await matchUrlCapturesWithinDeadline(
24634
24735
  captureContext.urlRegex,
24635
24736
  captureContext.url
24636
24737
  );
@@ -24870,7 +24971,7 @@ async function rewriteResponseBody(input, replacements, replaceAction, captureCo
24870
24971
  if (captureContext === void 0) {
24871
24972
  text = replaceAction.body;
24872
24973
  } else {
24873
- const captures = matchUrlCaptures(
24974
+ const captures = await matchUrlCapturesWithinDeadline(
24874
24975
  captureContext.urlRegex,
24875
24976
  captureContext.url
24876
24977
  );
@@ -24902,8 +25003,11 @@ async function rewriteResponseBody(input, replacements, replaceAction, captureCo
24902
25003
  }
24903
25004
  };
24904
25005
  }
24905
- function replaceResponseBody(body, contentType, captureContext) {
24906
- const captures = captureContext === void 0 ? [] : matchUrlCaptures(captureContext.urlRegex, captureContext.url);
25006
+ async function replaceResponseBody(body, contentType, captureContext) {
25007
+ const captures = captureContext === void 0 ? [] : await matchUrlCapturesWithinDeadline(
25008
+ captureContext.urlRegex,
25009
+ captureContext.url
25010
+ );
24907
25011
  if (captureContext !== void 0 && captures === null) {
24908
25012
  return failure("runtime.url-capture-mismatch");
24909
25013
  }
@@ -24925,7 +25029,7 @@ async function fetchAndRewriteAuthorizedResponse(operation, action, options = {}
24925
25029
  const response = await fetchAuthorized(operation, options);
24926
25030
  if (!response.ok) return response;
24927
25031
  const contentType = responseHeader(response.value.headers, "content-type");
24928
- const transformed = isReplaceAction(action) ? replaceResponseBody(
25032
+ const transformed = isReplaceAction(action) ? await replaceResponseBody(
24929
25033
  action.body,
24930
25034
  contentType,
24931
25035
  operation.sourceValue === void 0 ? void 0 : { url: operation.target, urlRegex: operation.sourceValue }
@@ -24995,7 +25099,7 @@ function projectHasRule(project, groupId, ruleId) {
24995
25099
  function isBodyOperation(operation) {
24996
25100
  return operation.kind === "request-body" || operation.kind === "response-body";
24997
25101
  }
24998
- function revalidateAuthority(project, operations, request) {
25102
+ async function revalidateAuthority(project, operations, request) {
24999
25103
  const projectSnap = snapshotOwnData3(project);
25000
25104
  if (!projectSnap.valid) return { allowed: false, reason: "project-invalid" };
25001
25105
  const opsSnap = snapshotOwnData3(operations);
@@ -25016,7 +25120,7 @@ function revalidateAuthority(project, operations, request) {
25016
25120
  return { allowed: false, reason: "project-inconsistent" };
25017
25121
  }
25018
25122
  const matcher = operation.matcher;
25019
- if (!sourceMatches(matcher.source, request.url)) {
25123
+ if (!await sourceMatchesWithinDeadline(matcher.source, request.url)) {
25020
25124
  return { allowed: false, reason: "url-mismatch" };
25021
25125
  }
25022
25126
  const requestOrigin = originOf(request.url);
@@ -25056,18 +25160,19 @@ function revalidateAuthority(project, operations, request) {
25056
25160
  function isBodyOp(op) {
25057
25161
  return op.kind === "request-body" || op.kind === "response-body";
25058
25162
  }
25059
- function matcherMatches(op, url, method) {
25060
- if (!sourceMatches(op.matcher.source, url)) return false;
25163
+ async function matcherMatches(op, url, method) {
25164
+ if (!await sourceMatchesWithinDeadline(op.matcher.source, url))
25165
+ return false;
25061
25166
  if (op.matcher.method !== void 0 && op.matcher.method !== method.toUpperCase()) {
25062
25167
  return false;
25063
25168
  }
25064
25169
  return true;
25065
25170
  }
25066
- function pickHighest(operations, kind, url, method) {
25171
+ async function pickHighest(operations, kind, url, method) {
25067
25172
  let best = null;
25068
25173
  for (const op of operations) {
25069
25174
  if (!isBodyOp(op) || op.kind !== kind) continue;
25070
- if (!matcherMatches(op, url, method)) continue;
25175
+ if (!await matcherMatches(op, url, method)) continue;
25071
25176
  if (best === null || op.matcher.priority > best.matcher.priority || op.matcher.priority === best.matcher.priority && op.ruleId.localeCompare(best.ruleId) < 0) {
25072
25177
  best = op;
25073
25178
  }
@@ -25436,17 +25541,21 @@ function initiatorFromHeaders(headers, targetUrl) {
25436
25541
  return void 0;
25437
25542
  }
25438
25543
  }
25439
- function authorizeOp(policy, op, url, method, headers) {
25544
+ async function authorizeOp(policy, op, url, method, headers) {
25440
25545
  const resourceType = op.matcher.resourceTypes[0];
25441
25546
  const initiator = initiatorFromHeaders(headers, url);
25442
- const decision = revalidateAuthority(policy.project, policy.operations, {
25443
- groupId: op.groupId,
25444
- ruleId: op.ruleId,
25445
- url,
25446
- method,
25447
- ...initiator !== void 0 ? { initiator } : {},
25448
- ...resourceType !== void 0 ? { resourceType } : {}
25449
- });
25547
+ const decision = await revalidateAuthority(
25548
+ policy.project,
25549
+ policy.operations,
25550
+ {
25551
+ groupId: op.groupId,
25552
+ ruleId: op.ruleId,
25553
+ url,
25554
+ method,
25555
+ ...initiator !== void 0 ? { initiator } : {},
25556
+ ...resourceType !== void 0 ? { resourceType } : {}
25557
+ }
25558
+ );
25450
25559
  return decision.allowed;
25451
25560
  }
25452
25561
  async function startInterceptProxy(options = {}) {
@@ -25494,13 +25603,13 @@ async function startInterceptProxy(options = {}) {
25494
25603
  }
25495
25604
  const method = (req.method ?? "GET").toUpperCase();
25496
25605
  const active = policy;
25497
- const requestOp = active === null ? null : pickHighest(
25606
+ const requestOp = active === null ? null : await pickHighest(
25498
25607
  active.operations,
25499
25608
  "request-body",
25500
25609
  targetUrl,
25501
25610
  method
25502
25611
  );
25503
- const responseOp = active === null ? null : pickHighest(
25612
+ const responseOp = active === null ? null : await pickHighest(
25504
25613
  active.operations,
25505
25614
  "response-body",
25506
25615
  targetUrl,
@@ -25520,7 +25629,13 @@ async function startInterceptProxy(options = {}) {
25520
25629
  }
25521
25630
  let requestBody = bodyResult.body;
25522
25631
  if (needsRequestBody && requestOp !== null && active !== null) {
25523
- if (!authorizeOp(active, requestOp, targetUrl, method, req.headers)) {
25632
+ if (!await authorizeOp(
25633
+ active,
25634
+ requestOp,
25635
+ targetUrl,
25636
+ method,
25637
+ req.headers
25638
+ )) {
25524
25639
  } else {
25525
25640
  const rewritten = await rewriteRequestBody(
25526
25641
  {
@@ -25569,7 +25684,13 @@ async function startInterceptProxy(options = {}) {
25569
25684
  let responseBody = upstream.body;
25570
25685
  if (responseOp !== null && active !== null) {
25571
25686
  if (responseBody.byteLength > RUNTIME_LIMITS.maxResponseBodyBytes) {
25572
- } else if (!authorizeOp(active, responseOp, targetUrl, method, req.headers)) {
25687
+ } else if (!await authorizeOp(
25688
+ active,
25689
+ responseOp,
25690
+ targetUrl,
25691
+ method,
25692
+ req.headers
25693
+ )) {
25573
25694
  } else {
25574
25695
  const action = responseOp.responseBody;
25575
25696
  const isReplace = "mode" in action && action.mode === "replace";
@@ -25693,7 +25814,10 @@ async function startInterceptProxy(options = {}) {
25693
25814
  const operation = active.operations.find(
25694
25815
  (op) => op.kind === "response-body" && op.ruleId === parsed.ruleId
25695
25816
  );
25696
- if (operation === void 0 || !sourceMatches(operation.matcher.source, parsed.originalUrl)) {
25817
+ if (operation === void 0 || !await sourceMatchesWithinDeadline(
25818
+ operation.matcher.source,
25819
+ parsed.originalUrl
25820
+ )) {
25697
25821
  res.writeHead(404);
25698
25822
  res.end();
25699
25823
  return;
@@ -25709,15 +25833,19 @@ async function startInterceptProxy(options = {}) {
25709
25833
  res.end();
25710
25834
  return;
25711
25835
  }
25712
- const allowed = revalidateAuthority(active.project, active.operations, {
25713
- groupId: operation.groupId,
25714
- ruleId: operation.ruleId,
25715
- url: parsed.originalUrl,
25716
- target,
25717
- method: "GET",
25718
- initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin,
25719
- resourceType: operation.matcher.resourceTypes[0]
25720
- });
25836
+ const allowed = await revalidateAuthority(
25837
+ active.project,
25838
+ active.operations,
25839
+ {
25840
+ groupId: operation.groupId,
25841
+ ruleId: operation.ruleId,
25842
+ url: parsed.originalUrl,
25843
+ target,
25844
+ method: "GET",
25845
+ initiator: initiatorFromHeaders(req.headers, parsed.originalUrl) ?? new URL(target).origin,
25846
+ resourceType: operation.matcher.resourceTypes[0]
25847
+ }
25848
+ );
25721
25849
  if (!allowed.allowed) {
25722
25850
  res.writeHead(403);
25723
25851
  res.end();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.16.4",
3
+ "version": "6.16.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",