@rogatio/cli 6.14.2 → 6.14.3

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 +205 -248
  2. package/package.json +1 -1
@@ -27188,25 +27188,6 @@ import { readFile as readFile4 } from "node:fs/promises";
27188
27188
  import { dirname as dirname6, isAbsolute as isAbsolute5, resolve as resolve2 } from "node:path";
27189
27189
 
27190
27190
  // packages/dry-run/dist/node/index.js
27191
- function parseTestUrl(input) {
27192
- if (typeof input !== "string" || input.length === 0) {
27193
- return { ok: false };
27194
- }
27195
- let url;
27196
- try {
27197
- url = new URL(input);
27198
- } catch {
27199
- return { ok: false };
27200
- }
27201
- if (url.protocol !== "http:" && url.protocol !== "https:") {
27202
- return { ok: false };
27203
- }
27204
- if (url.origin === "null") {
27205
- return { ok: false };
27206
- }
27207
- return { ok: true, value: { origin: url.origin, href: url.href } };
27208
- }
27209
- var DEFAULT_MAX_CASES = 256;
27210
27191
  function invalidCase(index, message = "Test case is invalid") {
27211
27192
  return {
27212
27193
  code: "dryrun.invalid-case",
@@ -27220,6 +27201,69 @@ function invalidUrl(index, message = "Test case URL is invalid") {
27220
27201
  function invalidOptions() {
27221
27202
  return invalidCase(void 0, "Dry-run options are invalid");
27222
27203
  }
27204
+ function batchLimit(maxCases) {
27205
+ return {
27206
+ code: "dryrun.batch-limit",
27207
+ message: `Test batch exceeds maxCases (${maxCases})`
27208
+ };
27209
+ }
27210
+ function buildDimension(state, detail) {
27211
+ return {
27212
+ state,
27213
+ matched: state === "not-applicable" ? null : state === "matched",
27214
+ detail
27215
+ };
27216
+ }
27217
+ function sourceDetail(source, matched) {
27218
+ const subject = source.key === "host" ? "hostname" : "url";
27219
+ return matched ? `${subject} matched /${source.value}/ (${source.key})` : `${subject} did not match /${source.value}/ (${source.key})`;
27220
+ }
27221
+ function sourceDimension(matcher, url) {
27222
+ const sourceState = sourceMatches(matcher.source, url) ? "matched" : "unmatched";
27223
+ return buildDimension(
27224
+ sourceState,
27225
+ sourceDetail(matcher.source, sourceState === "matched")
27226
+ );
27227
+ }
27228
+ function methodDimension(matcher, testCase) {
27229
+ const methodState = testCase.method === void 0 ? "not-applicable" : matcher.method === void 0 || matcher.method === testCase.method ? "matched" : "unmatched";
27230
+ return buildDimension(
27231
+ methodState,
27232
+ methodState === "not-applicable" ? "method not specified" : methodState === "matched" ? matcher.method === void 0 ? "rule has no method constraint" : `method ${testCase.method} matches` : `rule method ${matcher.method} != ${testCase.method}`
27233
+ );
27234
+ }
27235
+ function resourceTypeDimension(matcher, testCase) {
27236
+ const resourceState = testCase.resourceType === void 0 ? "not-applicable" : matcher.resourceTypes.length === 0 || matcher.resourceTypes.includes(testCase.resourceType) ? "matched" : "unmatched";
27237
+ return buildDimension(
27238
+ resourceState,
27239
+ resourceState === "not-applicable" ? "resource type not specified" : resourceState === "matched" ? matcher.resourceTypes.length === 0 ? "rule has no resource type constraint" : `resource type ${testCase.resourceType} matches` : `rule resource types [${matcher.resourceTypes.join(", ")}] exclude ${testCase.resourceType}`
27240
+ );
27241
+ }
27242
+ function safePreview(fn, operation, url, testCase) {
27243
+ try {
27244
+ return fn(operation, url, testCase);
27245
+ } catch {
27246
+ return null;
27247
+ }
27248
+ }
27249
+ function evaluateRule(operation, testCase, previewAction) {
27250
+ const matcher = operation.matcher;
27251
+ const source = sourceDimension(matcher, testCase.url);
27252
+ const method = methodDimension(matcher, testCase);
27253
+ const resourceType = resourceTypeDimension(matcher, testCase);
27254
+ const matched = source.state === "matched" && method.state !== "unmatched" && resourceType.state !== "unmatched";
27255
+ const actionPreview = previewAction ? safePreview(previewAction, operation, testCase.url, testCase) : null;
27256
+ return {
27257
+ groupId: operation.groupId,
27258
+ ruleId: operation.ruleId,
27259
+ matched,
27260
+ source,
27261
+ method,
27262
+ resourceType,
27263
+ actionPreview
27264
+ };
27265
+ }
27266
+ var DEFAULT_MAX_CASES = 256;
27223
27267
  function ownPropertyNames(value) {
27224
27268
  try {
27225
27269
  const names = Object.getOwnPropertyNames(value);
@@ -27291,32 +27335,6 @@ function validateCase(raw, index) {
27291
27335
  }
27292
27336
  };
27293
27337
  }
27294
- function safePreview(fn, operation, url, testCase) {
27295
- try {
27296
- return fn(operation, url, testCase);
27297
- } catch {
27298
- return null;
27299
- }
27300
- }
27301
- function buildDimension(state, detail) {
27302
- return {
27303
- state,
27304
- matched: state === "not-applicable" ? null : state === "matched",
27305
- detail
27306
- };
27307
- }
27308
- function sourceDetail(source, matched) {
27309
- const subject = source.key === "host" ? "hostname" : "url";
27310
- return matched ? `${subject} matched /${source.value}/ (${source.key})` : `${subject} did not match /${source.value}/ (${source.key})`;
27311
- }
27312
- function emptySummary() {
27313
- return {
27314
- caseCount: 0,
27315
- urlCount: 0,
27316
- matchedUrlCount: 0,
27317
- matchedRuleTotal: 0
27318
- };
27319
- }
27320
27338
  function normalizeOptions(raw) {
27321
27339
  if (raw === void 0) {
27322
27340
  return { ok: true, maxCases: DEFAULT_MAX_CASES };
@@ -27354,47 +27372,29 @@ function normalizeOptions(raw) {
27354
27372
  }
27355
27373
  return { ok: true, maxCases, previewAction };
27356
27374
  }
27357
- function dryRunProject(operations, cases, options) {
27358
- const normalizedOptions = normalizeOptions(options);
27359
- if (!normalizedOptions.ok) {
27360
- return { results: [], errors: [invalidOptions()], summary: emptySummary() };
27361
- }
27362
- const errors = [];
27375
+ function readCaseBatch(cases, maxCases) {
27363
27376
  if (!Array.isArray(cases)) {
27364
27377
  return {
27365
- results: [],
27366
- errors: [invalidCase(void 0, "Test cases must be an array")],
27367
- summary: emptySummary()
27378
+ ok: false,
27379
+ error: invalidCase(void 0, "Test cases must be an array")
27368
27380
  };
27369
27381
  }
27370
27382
  let caseCount;
27371
27383
  try {
27372
27384
  caseCount = cases.length;
27373
- if (caseCount > normalizedOptions.maxCases) {
27374
- return {
27375
- results: [],
27376
- errors: [
27377
- {
27378
- code: "dryrun.batch-limit",
27379
- message: `Test batch exceeds maxCases (${normalizedOptions.maxCases})`
27380
- }
27381
- ],
27382
- summary: emptySummary()
27383
- };
27385
+ if (caseCount > maxCases) {
27386
+ return { ok: false, error: batchLimit(maxCases) };
27384
27387
  }
27385
27388
  for (let index = 0; index < caseCount; index += 1) {
27386
27389
  if (!Object.hasOwn(cases, index)) {
27387
- return {
27388
- results: [],
27389
- errors: [invalidCase(index)],
27390
- summary: emptySummary()
27391
- };
27390
+ return { ok: false, error: invalidCase(index) };
27392
27391
  }
27393
27392
  }
27394
27393
  } catch {
27395
- return { results: [], errors: [invalidCase()], summary: emptySummary() };
27394
+ return { ok: false, error: invalidCase() };
27396
27395
  }
27397
- const validCases = [];
27396
+ const errors = [];
27397
+ const valid = [];
27398
27398
  for (let index = 0; index < caseCount; index += 1) {
27399
27399
  let raw;
27400
27400
  try {
@@ -27405,15 +27405,57 @@ function dryRunProject(operations, cases, options) {
27405
27405
  }
27406
27406
  const result = validateCase(raw, index);
27407
27407
  if (result.ok) {
27408
- validCases.push({ value: result.value, index });
27408
+ valid.push({ value: result.value, index });
27409
27409
  } else {
27410
27410
  errors.push(result.error);
27411
27411
  }
27412
27412
  }
27413
+ return { ok: true, valid, errors };
27414
+ }
27415
+ function parseTestUrl(input) {
27416
+ if (typeof input !== "string" || input.length === 0) {
27417
+ return { ok: false };
27418
+ }
27419
+ let url;
27420
+ try {
27421
+ url = new URL(input);
27422
+ } catch {
27423
+ return { ok: false };
27424
+ }
27425
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
27426
+ return { ok: false };
27427
+ }
27428
+ if (url.origin === "null") {
27429
+ return { ok: false };
27430
+ }
27431
+ return { ok: true, value: { origin: url.origin, href: url.href } };
27432
+ }
27433
+ function emptySummary() {
27434
+ return {
27435
+ caseCount: 0,
27436
+ urlCount: 0,
27437
+ matchedUrlCount: 0,
27438
+ matchedRuleTotal: 0
27439
+ };
27440
+ }
27441
+ function dryRunProject(operations, cases, options) {
27442
+ const normalizedOptions = normalizeOptions(options);
27443
+ if (!normalizedOptions.ok) {
27444
+ return { results: [], errors: [invalidOptions()], summary: emptySummary() };
27445
+ }
27446
+ const batch = readCaseBatch(cases, normalizedOptions.maxCases);
27447
+ if (!batch.ok) {
27448
+ return {
27449
+ results: [],
27450
+ errors: [batch.error],
27451
+ summary: emptySummary()
27452
+ };
27453
+ }
27454
+ const errors = [...batch.errors];
27413
27455
  const results = [];
27414
27456
  let matchedUrlCount = 0;
27415
27457
  let matchedRuleTotal = 0;
27416
- for (const validCase of validCases) {
27458
+ for (const validCase of batch.valid) {
27417
27459
  const testCase = validCase.value;
27418
27460
  const parsed = parseTestUrl(testCase.url);
27419
27461
  if (!parsed.ok) {
@@ -27422,41 +27464,7 @@ function dryRunProject(operations, cases, options) {
27422
27464
  }
27423
27465
  const rules = [];
27424
27466
  for (const op of operations) {
27425
- const matcher = op.matcher;
27426
- const sourceState = sourceMatches(
27427
- matcher.source,
27428
- testCase.url
27429
- ) ? "matched" : "unmatched";
27430
- const methodState = testCase.method === void 0 ? "not-applicable" : matcher.method === void 0 || matcher.method === testCase.method ? "matched" : "unmatched";
27431
- const resourceState = testCase.resourceType === void 0 ? "not-applicable" : matcher.resourceTypes.length === 0 || matcher.resourceTypes.includes(testCase.resourceType) ? "matched" : "unmatched";
27432
- const matched = sourceState === "matched" && methodState !== "unmatched" && resourceState !== "unmatched";
27433
- const sourceDim = buildDimension(
27434
- sourceState,
27435
- sourceDetail(matcher.source, sourceState === "matched")
27436
- );
27437
- const methodDim = buildDimension(
27438
- methodState,
27439
- methodState === "not-applicable" ? "method not specified" : methodState === "matched" ? matcher.method === void 0 ? "rule has no method constraint" : `method ${testCase.method} matches` : `rule method ${matcher.method} != ${testCase.method}`
27440
- );
27441
- const resourceDim = buildDimension(
27442
- resourceState,
27443
- resourceState === "not-applicable" ? "resource type not specified" : resourceState === "matched" ? matcher.resourceTypes.length === 0 ? "rule has no resource type constraint" : `resource type ${testCase.resourceType} matches` : `rule resource types [${matcher.resourceTypes.join(", ")}] exclude ${testCase.resourceType}`
27444
- );
27445
- const actionPreview = normalizedOptions.previewAction ? safePreview(
27446
- normalizedOptions.previewAction,
27447
- op,
27448
- testCase.url,
27449
- testCase
27450
- ) : null;
27451
- rules.push({
27452
- groupId: op.groupId,
27453
- ruleId: op.ruleId,
27454
- matched,
27455
- source: sourceDim,
27456
- method: methodDim,
27457
- resourceType: resourceDim,
27458
- actionPreview
27459
- });
27467
+ rules.push(evaluateRule(op, testCase, normalizedOptions.previewAction));
27460
27468
  }
27461
27469
  const matchedRuleCount = rules.filter((rule) => rule.matched).length;
27462
27470
  results.push({
@@ -27473,7 +27481,7 @@ function dryRunProject(operations, cases, options) {
27473
27481
  results,
27474
27482
  errors,
27475
27483
  summary: {
27476
- caseCount: validCases.length,
27484
+ caseCount: batch.valid.length,
27477
27485
  urlCount: results.length,
27478
27486
  matchedUrlCount,
27479
27487
  matchedRuleTotal
@@ -27548,6 +27556,45 @@ function previewRuleAction(operation, url) {
27548
27556
  return null;
27549
27557
  }
27550
27558
 
27559
+ // packages/cli/src/commands/diagnose.ts
27560
+ function diagnoseProject(projectData) {
27561
+ const diagnostics = [];
27562
+ const schemaResult = validateProjectDetailed(projectData);
27563
+ if (!schemaResult.valid) {
27564
+ for (const error of schemaResult.errors) {
27565
+ diagnostics.push({
27566
+ code: `schema.${error.keyword}`,
27567
+ severity: "error",
27568
+ path: error.instancePath || "/",
27569
+ message: error.message,
27570
+ params: error.params
27571
+ });
27572
+ }
27573
+ return { diagnostics, stage: "schema" };
27574
+ }
27575
+ const compileResult = compileProject(schemaResult.data);
27576
+ if (!compileResult.ok) {
27577
+ for (const diag of compileResult.diagnostics) {
27578
+ diagnostics.push({
27579
+ code: diag.code,
27580
+ severity: diag.severity,
27581
+ path: diag.path,
27582
+ message: diag.message,
27583
+ params: { ...diag.params }
27584
+ });
27585
+ }
27586
+ return { diagnostics, stage: "compiler" };
27587
+ }
27588
+ return {
27589
+ diagnostics,
27590
+ project: schemaResult.data,
27591
+ operations: compileResult.operations
27592
+ };
27593
+ }
27594
+ function formatDiagnostics(diagnostics) {
27595
+ return diagnostics.map((diag) => `${diag.path}: ${diag.message} (${diag.code})`).join("\n");
27596
+ }
27597
+
27551
27598
  // packages/cli/src/mock-root-config.ts
27552
27599
  import { mkdir as mkdir3, readFile as readFile3, realpath as realpath3, rename as rename2, writeFile as writeFile3 } from "node:fs/promises";
27553
27600
  import { homedir } from "node:os";
@@ -27714,26 +27761,14 @@ function parseUrl(req) {
27714
27761
  );
27715
27762
  return { pathname: url.pathname, searchParams: url.searchParams };
27716
27763
  }
27717
- function parseDiagnostics(result) {
27718
- if (!result.valid) {
27719
- return result.errors.map((e) => ({
27720
- code: `schema.${e.keyword}`,
27721
- severity: "error",
27722
- path: e.instancePath || "/",
27723
- message: e.message,
27724
- params: e.params
27725
- }));
27726
- }
27727
- return [];
27728
- }
27729
27764
  function schemaDiagnosticsForAssist(value) {
27730
- const result = validateProjectDetailed(value);
27731
- if (result.valid) return [];
27732
- return result.errors.map((error) => ({
27733
- code: `schema.${error.keyword}`,
27765
+ const diagnosis = diagnoseProject(value);
27766
+ if (diagnosis.stage !== "schema") return [];
27767
+ return diagnosis.diagnostics.map((diagnostic) => ({
27768
+ code: diagnostic.code,
27734
27769
  severity: "error",
27735
- path: error.instancePath || "/",
27736
- message: error.message ?? "The project contains invalid data."
27770
+ path: diagnostic.path,
27771
+ message: diagnostic.message
27737
27772
  }));
27738
27773
  }
27739
27774
  function isAIAssistRequestBody(body) {
@@ -27745,18 +27780,6 @@ function isAIAssistRequestBody(body) {
27745
27780
  if (!isRecord2(body.context) || !isRecord2(body.context.project)) return false;
27746
27781
  return true;
27747
27782
  }
27748
- function parseCompilerDiagnostics(result) {
27749
- if (!result.ok) {
27750
- return result.diagnostics.map((d) => ({
27751
- code: d.code,
27752
- severity: d.severity,
27753
- path: d.path,
27754
- message: d.message,
27755
- params: d.params
27756
- }));
27757
- }
27758
- return [];
27759
- }
27760
27783
  function createRoutes(context) {
27761
27784
  return async function handleRequest(req, res) {
27762
27785
  const host = headerValue3(req.headers.host);
@@ -27878,14 +27901,9 @@ function createRoutes(context) {
27878
27901
  res.end(JSON.stringify(failure2.body));
27879
27902
  return;
27880
27903
  }
27881
- const schemaResult = validateProjectDetailed(body);
27882
- const diagnostics = [...parseDiagnostics(schemaResult)];
27883
- if (schemaResult.valid) {
27884
- const compileResult = compileProject(schemaResult.data);
27885
- diagnostics.push(...parseCompilerDiagnostics(compileResult));
27886
- }
27904
+ const diagnosis = diagnoseProject(body);
27887
27905
  res.writeHead(200, { "Content-Type": "application/json" });
27888
- res.end(JSON.stringify({ diagnostics }));
27906
+ res.end(JSON.stringify({ diagnostics: diagnosis.diagnostics }));
27889
27907
  return;
27890
27908
  }
27891
27909
  if (pathname === "/api/mock-root" && method === "GET") {
@@ -27963,26 +27981,25 @@ function createRoutes(context) {
27963
27981
  res.end(JSON.stringify(failure2.body));
27964
27982
  return;
27965
27983
  }
27966
- const schemaResult = validateProjectDetailed(body);
27967
- if (!schemaResult.valid) {
27984
+ const diagnosis = diagnoseProject(body);
27985
+ if (diagnosis.stage === "schema") {
27968
27986
  res.writeHead(400, { "Content-Type": "application/json" });
27969
27987
  res.end(
27970
27988
  JSON.stringify({
27971
27989
  code: "validation-failed",
27972
27990
  message: "Project validation failed",
27973
- diagnostics: parseDiagnostics(schemaResult)
27991
+ diagnostics: diagnosis.diagnostics
27974
27992
  })
27975
27993
  );
27976
27994
  return;
27977
27995
  }
27978
- const compileResult = compileProject(schemaResult.data);
27979
- if (!compileResult.ok) {
27996
+ if (diagnosis.stage === "compiler") {
27980
27997
  res.writeHead(400, { "Content-Type": "application/json" });
27981
27998
  res.end(
27982
27999
  JSON.stringify({
27983
28000
  code: "compilation-failed",
27984
28001
  message: "Project compilation failed",
27985
- diagnostics: parseCompilerDiagnostics(compileResult)
28002
+ diagnostics: diagnosis.diagnostics
27986
28003
  })
27987
28004
  );
27988
28005
  return;
@@ -28061,35 +28078,37 @@ function createRoutes(context) {
28061
28078
  );
28062
28079
  return;
28063
28080
  }
28064
- const schemaResult = validateProjectDetailed(body.project);
28065
- if (!schemaResult.valid) {
28081
+ const diagnosis = diagnoseProject(body.project);
28082
+ if (diagnosis.stage === "schema") {
28066
28083
  res.writeHead(400, { "Content-Type": "application/json" });
28067
28084
  res.end(
28068
28085
  JSON.stringify({
28069
28086
  code: "validation-failed",
28070
28087
  message: "Project validation failed",
28071
- diagnostics: parseDiagnostics(schemaResult)
28088
+ diagnostics: diagnosis.diagnostics
28072
28089
  })
28073
28090
  );
28074
28091
  return;
28075
28092
  }
28076
- const compileResult = compileProject(schemaResult.data);
28077
- if (!compileResult.ok) {
28093
+ if (diagnosis.stage === "compiler") {
28078
28094
  res.writeHead(400, { "Content-Type": "application/json" });
28079
28095
  res.end(
28080
28096
  JSON.stringify({
28081
28097
  code: "compilation-failed",
28082
28098
  message: "Project compilation failed",
28083
- diagnostics: parseCompilerDiagnostics(compileResult)
28099
+ diagnostics: diagnosis.diagnostics
28084
28100
  })
28085
28101
  );
28086
28102
  return;
28087
28103
  }
28104
+ if (diagnosis.operations === void 0) {
28105
+ throw new Error("Valid project diagnosis is missing operations");
28106
+ }
28088
28107
  const requested = isRecord2(body.options) && typeof body.options.maxCases === "number" && Number.isSafeInteger(body.options.maxCases) && body.options.maxCases > 0 ? body.options.maxCases : void 0;
28089
28108
  const options = { previewAction: previewRuleAction };
28090
28109
  if (requested !== void 0) options.maxCases = requested;
28091
28110
  const result = dryRunProject(
28092
- compileResult.operations,
28111
+ diagnosis.operations,
28093
28112
  body.cases,
28094
28113
  options
28095
28114
  );
@@ -29217,9 +29236,12 @@ var METHOD_SET = new Set(HTTP_METHODS);
29217
29236
  var SOURCE_CHANGE = {
29218
29237
  caseInsensitive: "The case-insensitive match flag was dropped. Rogatio regular expressions are case-sensitive.",
29219
29238
  hostPort: "Requestly compares the host and a non-default port. This host condition matches the hostname on any port.",
29220
- hostAsUrl: "The host condition was imported as a URL regular expression so the port stays part of the match.",
29239
+ hostAsUrl: "The host condition was imported as a URL regular expression over the authority. Wildcard host patterns accept any numeric port; Contains does not pin a port.",
29221
29240
  path: "The path condition was imported as a URL regular expression."
29222
29241
  };
29242
+ var HOST_MATCHES_SKIP_REASON = "Host regular expressions cannot be confined to the host in a URL regex.";
29243
+ var HOST_AUTHORITY_META_SKIP_REASON = "Host Contains or Wildcard values with /, ?, #, @, or \\ cannot be confined to the host in a URL regex.";
29244
+ var HOST_AUTHORITY_META = /[/?#@\\]/;
29223
29245
  function escapeRegex(literal) {
29224
29246
  return literal.replace(/[\\^$*+?.()|[\]{}]/g, "\\$&");
29225
29247
  }
@@ -29353,24 +29375,26 @@ function convertHost(operator, value) {
29353
29375
  };
29354
29376
  }
29355
29377
  if (operator === "Contains") {
29356
- const body = value.length === 0 ? "[^/?#]*" : `[^/?#]*${escapeRegex(value)}[^/?#]*`;
29378
+ if (HOST_AUTHORITY_META.test(value)) {
29379
+ return { ok: false, reason: HOST_AUTHORITY_META_SKIP_REASON };
29380
+ }
29381
+ const body = value.length === 0 ? "[^/?#@]*" : `[^/?#@]*${escapeRegex(value)}[^/?#@]*`;
29357
29382
  return finishUrl(`^https?:\\/\\/${body}(?:[/?#].*)?$`, [
29358
29383
  SOURCE_CHANGE.hostAsUrl
29359
29384
  ]);
29360
29385
  }
29361
29386
  if (operator === "Wildcard_Matches") {
29362
- return finishUrl(`^https?:\\/\\/${wildcardBody(value)}(?:[/?#].*)?$`, [
29363
- SOURCE_CHANGE.hostAsUrl
29364
- ]);
29365
- }
29366
- if (operator === "Matches") {
29367
- const parsed = parseRequestlyRegex(value);
29368
- if (!parsed.ok) return parsed;
29387
+ if (HOST_AUTHORITY_META.test(value)) {
29388
+ return { ok: false, reason: HOST_AUTHORITY_META_SKIP_REASON };
29389
+ }
29369
29390
  return finishUrl(
29370
- `^https?:\\/\\/(?:${parsed.source})(?:[/?#].*)?$`,
29371
- parsed.caseInsensitive ? [SOURCE_CHANGE.hostAsUrl, SOURCE_CHANGE.caseInsensitive] : [SOURCE_CHANGE.hostAsUrl]
29391
+ `^https?:\\/\\/${hostWildcardBody(value)}(?::[0-9]+)?(?:[/?#].*)?$`,
29392
+ [SOURCE_CHANGE.hostAsUrl]
29372
29393
  );
29373
29394
  }
29395
+ if (operator === "Matches") {
29396
+ return { ok: false, reason: HOST_MATCHES_SKIP_REASON };
29397
+ }
29374
29398
  return { ok: false, reason: `Unsupported source operator "${operator}".` };
29375
29399
  }
29376
29400
  function convertPath(operator, value) {
@@ -29439,6 +29463,9 @@ function parseRequestlyRegex(value) {
29439
29463
  function wildcardBody(pattern) {
29440
29464
  return pattern.split("*").map((part) => escapeRegex(part)).join("(.*?)");
29441
29465
  }
29466
+ function hostWildcardBody(pattern) {
29467
+ return pattern.split("*").map((part) => escapeRegex(part)).join("([^/?#@]*?)");
29468
+ }
29442
29469
  function isRequestlyRegexLiteral(value) {
29443
29470
  return REQUESTLY_REGEX.test(value);
29444
29471
  }
@@ -31023,41 +31050,6 @@ function cloneProject(value) {
31023
31050
  return record;
31024
31051
  }
31025
31052
 
31026
- // packages/cli/src/commands/diagnose.ts
31027
- function diagnoseProject(projectData) {
31028
- const diagnostics = [];
31029
- const schemaResult = validateProjectDetailed(projectData);
31030
- if (!schemaResult.valid) {
31031
- for (const error of schemaResult.errors) {
31032
- diagnostics.push({
31033
- code: `schema.${error.keyword}`,
31034
- severity: "error",
31035
- path: error.instancePath || "/",
31036
- message: error.message,
31037
- params: error.params
31038
- });
31039
- }
31040
- return { diagnostics };
31041
- }
31042
- const compileResult = compileProject(schemaResult.data);
31043
- if (!compileResult.ok) {
31044
- for (const diag of compileResult.diagnostics) {
31045
- diagnostics.push({
31046
- code: diag.code,
31047
- severity: diag.severity,
31048
- path: diag.path,
31049
- message: diag.message,
31050
- params: { ...diag.params }
31051
- });
31052
- }
31053
- return { diagnostics };
31054
- }
31055
- return { diagnostics, project: schemaResult.data };
31056
- }
31057
- function formatDiagnostics(diagnostics) {
31058
- return diagnostics.map((diag) => `${diag.path}: ${diag.message} (${diag.code})`).join("\n");
31059
- }
31060
-
31061
31053
  // packages/cli/src/commands/import.ts
31062
31054
  async function importCommand(args) {
31063
31055
  const parsed = parseImportArgs(args);
@@ -31615,27 +31607,6 @@ function resultOptions(maxCases) {
31615
31607
  if (maxCases !== void 0) options.maxCases = maxCases;
31616
31608
  return options;
31617
31609
  }
31618
- function diagnosticsFromSchema(projectData) {
31619
- const result = validateProjectDetailed(projectData);
31620
- if (result.valid) return [];
31621
- return result.errors.map((error) => ({
31622
- code: `schema.${error.keyword}`,
31623
- severity: "error",
31624
- path: error.instancePath || "/",
31625
- message: error.message,
31626
- params: error.params
31627
- }));
31628
- }
31629
- function diagnosticsFromCompiler(result) {
31630
- if (result.ok) return [];
31631
- return result.diagnostics.map((diagnostic) => ({
31632
- code: diagnostic.code,
31633
- severity: diagnostic.severity,
31634
- path: diagnostic.path,
31635
- message: diagnostic.message,
31636
- params: diagnostic.params
31637
- }));
31638
- }
31639
31610
  function jsonOutput(value) {
31640
31611
  return `${JSON.stringify(value, null, 2)}
31641
31612
  `;
@@ -31777,21 +31748,9 @@ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
31777
31748
  console.error(output.trim());
31778
31749
  return 2;
31779
31750
  }
31780
- const schemaResult = validateProjectDetailed(projectData);
31781
- let diagnostics = diagnosticsFromSchema(projectData);
31782
- if (!schemaResult.valid) {
31783
- const output = jsonMode ? jsonOutput({ diagnostics }) : diagnostics.map(
31784
- (diagnostic) => `${diagnostic.path}: ${diagnostic.message} (${diagnostic.code})
31785
- `
31786
- ).join("");
31787
- if (captureOutput) return output;
31788
- if (jsonMode) console.log(output.trim());
31789
- else console.error(output.trim());
31790
- return 1;
31791
- }
31792
- const compileResult = compileProject(schemaResult.data);
31793
- diagnostics = diagnosticsFromCompiler(compileResult);
31794
- if (!compileResult.ok) {
31751
+ const diagnosis = diagnoseProject(projectData);
31752
+ if (diagnosis.stage !== void 0 || diagnosis.project === void 0 || diagnosis.operations === void 0) {
31753
+ const diagnostics = diagnosis.diagnostics;
31795
31754
  const output = jsonMode ? jsonOutput({ diagnostics }) : diagnostics.map(
31796
31755
  (diagnostic) => `${diagnostic.path}: ${diagnostic.message} (${diagnostic.code})
31797
31756
  `
@@ -31807,7 +31766,7 @@ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
31807
31766
  defaultResourceType
31808
31767
  );
31809
31768
  const dryRunResult = dryRunProject(
31810
- compileResult.operations,
31769
+ diagnosis.operations,
31811
31770
  testCases,
31812
31771
  resultOptions(maxCases)
31813
31772
  );
@@ -31817,13 +31776,11 @@ async function testCommandImpl(args, stdinInput, captureOutput, options = {}) {
31817
31776
  console.log(output.trim());
31818
31777
  } else {
31819
31778
  const groupNames = /* @__PURE__ */ new Map();
31820
- if (schemaResult.valid) {
31821
- for (const group of schemaResult.data.groups) {
31822
- groupNames.set(group.id, group.name);
31823
- }
31779
+ for (const group of diagnosis.project.groups) {
31780
+ groupNames.set(group.id, group.name);
31824
31781
  }
31825
31782
  const labels = /* @__PURE__ */ new Map();
31826
- for (const operation of compileResult.operations) {
31783
+ for (const operation of diagnosis.operations) {
31827
31784
  const groupName = groupNames.get(operation.groupId);
31828
31785
  labels.set(
31829
31786
  `${operation.groupId}/${operation.ruleId}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.14.2",
3
+ "version": "6.14.3",
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.",