@canaryai/cli 0.2.3 → 0.2.4
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/bin.js +1 -0
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-DXJNFJ3A.js → chunk-6WWHXWCS.js} +3 -2
- package/dist/{chunk-DXJNFJ3A.js.map → chunk-6WWHXWCS.js.map} +1 -1
- package/dist/{chunk-HOYYXZPV.js → chunk-DXIAHB72.js} +3 -2
- package/dist/{chunk-HOYYXZPV.js.map → chunk-DXIAHB72.js.map} +1 -1
- package/dist/chunk-FIQBGAKW.js +373 -0
- package/dist/chunk-FIQBGAKW.js.map +1 -0
- package/dist/{chunk-JMI7WWPF.js → chunk-ILEPYWZX.js} +3 -2
- package/dist/{chunk-JMI7WWPF.js.map → chunk-ILEPYWZX.js.map} +1 -1
- package/dist/{chunk-7R4YFGP6.js → chunk-PWWQGYFG.js} +3 -1
- package/dist/{chunk-7R4YFGP6.js.map → chunk-PWWQGYFG.js.map} +1 -1
- package/dist/chunk-VLFUCAPZ.js +12 -0
- package/dist/{debug-workflow-PMLMWKWI.js → debug-workflow-PT3OUR3V.js} +5 -5
- package/dist/docs-GCYDTEOY.js +270 -0
- package/dist/docs-GCYDTEOY.js.map +1 -0
- package/dist/{feature-flag-D3QTHGL6.js → feature-flag-S5B5GLPP.js} +77 -33
- package/dist/feature-flag-S5B5GLPP.js.map +1 -0
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/{issues-6MHRFKTU.js → issues-4ZEDHPLW.js} +5 -4
- package/dist/{issues-6MHRFKTU.js.map → issues-4ZEDHPLW.js.map} +1 -1
- package/dist/{knobs-ED6LXBVM.js → knobs-RKUVK3HC.js} +5 -4
- package/dist/{knobs-ED6LXBVM.js.map → knobs-RKUVK3HC.js.map} +1 -1
- package/dist/{local-browser-YSE3XCUW.js → local-browser-J6WGFLVD.js} +5 -5
- package/dist/mcp-HGYBMDYZ.js +687 -0
- package/dist/mcp-HGYBMDYZ.js.map +1 -0
- package/dist/{psql-U5LF6ELS.js → psql-WVIHMC6A.js} +4 -3
- package/dist/{psql-U5LF6ELS.js.map → psql-WVIHMC6A.js.map} +1 -1
- package/dist/record-X4SVNYP3.js +334 -0
- package/dist/record-X4SVNYP3.js.map +1 -0
- package/dist/{redis-PBQZGU6T.js → redis-RGHECKV5.js} +4 -3
- package/dist/{redis-PBQZGU6T.js.map → redis-RGHECKV5.js.map} +1 -1
- package/dist/{release-QBSP474D.js → release-ZOD4Y2BF.js} +4 -3
- package/dist/{release-QBSP474D.js.map → release-ZOD4Y2BF.js.map} +1 -1
- package/dist/runner/preload.js +3 -2
- package/dist/runner/preload.js.map +1 -1
- package/dist/test.js +3 -2
- package/dist/test.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-OE6O7H45.js +0 -92
- package/dist/chunk-OE6O7H45.js.map +0 -1
- package/dist/chunk-PLDDJCW6.js +0 -49
- package/dist/chunk-ZTWHPIXU.js +0 -42798
- package/dist/chunk-ZTWHPIXU.js.map +0 -1
- package/dist/dist-6NXLJYRZ.js +0 -335
- package/dist/dist-6NXLJYRZ.js.map +0 -1
- package/dist/feature-flag-D3QTHGL6.js.map +0 -1
- package/dist/mcp-LKHFYMA6.js +0 -385
- package/dist/mcp-LKHFYMA6.js.map +0 -1
- package/dist/pdf-extract-YIDRKYUD.js +0 -12
- package/dist/pdf-extract-YIDRKYUD.js.map +0 -1
- package/dist/pdfjs-44AOKLEM.js +0 -35242
- package/dist/pdfjs-44AOKLEM.js.map +0 -1
- /package/dist/{chunk-PLDDJCW6.js.map → chunk-VLFUCAPZ.js.map} +0 -0
- /package/dist/{debug-workflow-PMLMWKWI.js.map → debug-workflow-PT3OUR3V.js.map} +0 -0
- /package/dist/{local-browser-YSE3XCUW.js.map → local-browser-J6WGFLVD.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/release.ts"],"sourcesContent":["/**\n * CLI Release QA Management\n *\n * Trigger, poll, and check status of Release QA runs from CI/CD pipelines.\n * Used by the scheduled-release GitHub Actions workflow to gate deployments.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\n\ntype TriggerResponse = {\n ok: boolean;\n releaseRunId?: string;\n jobId?: string;\n error?: string;\n};\n\ntype RunStatusResponse = {\n ok: boolean;\n run?: {\n id: string;\n status: string;\n triggerSource: string;\n cutoffReason: string | null;\n fromSha: string | null;\n toSha: string | null;\n commitsAnalyzed: number;\n testersSpawned: number;\n testersCompleted: number;\n testersFailed: number;\n issuesFound: number;\n regressionTestsTotal: number;\n regressionTestsPassed: number;\n regressionTestsFailed: number;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n };\n error?: string;\n};\n\n/** Terminal statuses that stop polling */\nconst TERMINAL_STATUSES = new Set([\n \"completed\",\n \"completed_with_errors\",\n \"failed\",\n \"canceled\",\n \"timeout\",\n]);\n\nasync function handleTrigger(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = getArgValue(argv, \"--property-id\");\n if (!propertyId) {\n console.error(\"Error: Missing --property-id <uuid>.\");\n console.error(\"Usage: canary release trigger --property-id <uuid> [--credential-ids <uuid,...>]\");\n process.exit(1);\n }\n\n const credentialIdsRaw = getArgValue(argv, \"--credential-ids\");\n const body: Record<string, unknown> = { propertyId };\n if (credentialIdsRaw) {\n body.credentialIds = credentialIdsRaw.split(\",\").map((id) => id.trim());\n }\n\n const res = await fetch(`${apiUrl}/api/v1/release-qa/trigger`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n const json = (await res.json()) as TriggerResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n console.log(`Release QA run triggered`);\n console.log(` Run ID: ${json.releaseRunId}`);\n console.log(` Job ID: ${json.jobId}`);\n}\n\nasync function fetchRunStatus(\n apiUrl: string,\n token: string,\n runId: string\n): Promise<RunStatusResponse> {\n const res = await fetch(`${apiUrl}/api/v1/release-qa/runs/${encodeURIComponent(runId)}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n return (await res.json()) as RunStatusResponse;\n}\n\nasync function handleStatus(argv: string[], apiUrl: string, token: string): Promise<void> {\n const runId = argv[0];\n if (!runId || runId.startsWith(\"--\")) {\n console.error(\"Error: Missing run ID.\");\n console.error(\"Usage: canary release status <run-id>\");\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, \"--json\");\n const json = await fetchRunStatus(apiUrl, token, runId);\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const run = json.run!;\n\n if (jsonOutput) {\n console.log(JSON.stringify(run, null, 2));\n return;\n }\n\n console.log(`Release QA Run: ${run.id}`);\n console.log(` Status: ${run.status}`);\n console.log(` Trigger: ${run.triggerSource}`);\n console.log(` Commits analyzed: ${run.commitsAnalyzed}`);\n console.log(` Testers: ${run.testersCompleted}/${run.testersSpawned} completed, ${run.testersFailed} failed`);\n console.log(` Issues found: ${run.issuesFound}`);\n console.log(` Regression tests: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed, ${run.regressionTestsFailed} failed`);\n if (run.startedAt) console.log(` Started: ${run.startedAt}`);\n if (run.finishedAt) console.log(` Finished: ${run.finishedAt}`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function handleRun(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = getArgValue(argv, \"--property-id\");\n if (!propertyId) {\n console.error(\"Error: Missing --property-id <uuid>.\");\n console.error(\n \"Usage: canary release run --property-id <uuid> [--timeout 3600] [--poll-interval 30]\"\n );\n process.exit(1);\n }\n\n const timeoutSec = parseInt(getArgValue(argv, \"--timeout\") ?? \"3600\", 10);\n const pollIntervalSec = parseInt(getArgValue(argv, \"--poll-interval\") ?? \"30\", 10);\n\n const credentialIdsRaw = getArgValue(argv, \"--credential-ids\");\n const body: Record<string, unknown> = { propertyId };\n if (credentialIdsRaw) {\n body.credentialIds = credentialIdsRaw.split(\",\").map((id) => id.trim());\n }\n\n // Trigger\n console.log(\"Triggering Release QA run...\");\n const triggerRes = await fetch(`${apiUrl}/api/v1/release-qa/trigger`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (triggerRes.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n const triggerJson = (await triggerRes.json()) as TriggerResponse;\n\n if (!triggerJson.ok) {\n console.error(`Error triggering run: ${triggerJson.error}`);\n process.exit(1);\n }\n\n const runId = triggerJson.releaseRunId!;\n console.log(`Run started: ${runId}`);\n\n // Poll\n const deadline = Date.now() + timeoutSec * 1000;\n let lastStatus = \"\";\n\n while (Date.now() < deadline) {\n await sleep(pollIntervalSec * 1000);\n\n let statusJson: RunStatusResponse;\n try {\n statusJson = await fetchRunStatus(apiUrl, token, runId);\n } catch (err) {\n console.error(`Warning: Failed to fetch status, retrying... (${err})`);\n continue;\n }\n\n if (!statusJson.ok) {\n console.error(`Error: ${statusJson.error}`);\n process.exit(1);\n }\n\n const run = statusJson.run!;\n const statusLine = `[${run.status}] testers: ${run.testersCompleted}/${run.testersSpawned}, regressions: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed`;\n\n if (run.status !== lastStatus) {\n console.log(statusLine);\n lastStatus = run.status;\n } else {\n // Print progress on same status change in metrics\n console.log(statusLine);\n }\n\n if (TERMINAL_STATUSES.has(run.status)) {\n console.log(\"\");\n console.log(`Run finished: ${run.status}`);\n console.log(` Testers: ${run.testersCompleted}/${run.testersSpawned} completed, ${run.testersFailed} failed`);\n console.log(` Regressions: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed, ${run.regressionTestsFailed} failed`);\n console.log(` Issues found: ${run.issuesFound}`);\n\n if (run.status === \"completed\") {\n console.log(\"\\nRelease QA PASSED\");\n process.exit(0);\n } else {\n console.error(`\\nRelease QA FAILED (${run.status})`);\n process.exit(1);\n }\n }\n }\n\n console.error(`\\nTimeout: Release QA did not complete within ${timeoutSec}s`);\n process.exit(1);\n}\n\nfunction printReleaseHelp(): void {\n console.log(\n [\n \"Usage: canary release <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" trigger --property-id <uuid> [--credential-ids <uuid,...>]\",\n \" Trigger a Release QA run\",\n \" status <run-id> [--json] Check run status\",\n \" run --property-id <uuid> [options] Trigger and poll until complete\",\n \"\",\n \"Run options:\",\n \" --timeout <seconds> Max wait time (default: 3600)\",\n \" --poll-interval <secs> Poll frequency (default: 30)\",\n \" --credential-ids <ids> Comma-separated credential UUIDs\",\n \"\",\n \"Options:\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --api-url <url> API URL override\",\n \" --token <key> API token override (or set CANARY_API_TOKEN)\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runRelease(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printReleaseHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"trigger\":\n await handleTrigger(rest, apiUrl, token);\n break;\n case \"status\":\n await handleStatus(rest, apiUrl, token);\n break;\n case \"run\":\n await handleRun(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printReleaseHelp();\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;AAOA,OAAO,aAAa;AAmCpB,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAe,cAAc,MAAgB,QAAgB,OAA8B;AACzF,QAAM,aAAa,YAAY,MAAM,eAAe;AACpD,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,sCAAsC;AACpD,YAAQ,MAAM,kFAAkF;AAChG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,mBAAmB,YAAY,MAAM,kBAAkB;AAC7D,QAAM,OAAgC,EAAE,WAAW;AACnD,MAAI,kBAAkB;AACpB,SAAK,gBAAgB,iBAAiB,MAAM,GAAG,EAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;AAAA,EACxE;AAEA,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,8BAA8B;AAAA,IAC7D,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,4CAA4C;AAC1D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,UAAU,KAAK,KAAK,EAAE;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,0BAA0B;AACtC,UAAQ,IAAI,aAAa,KAAK,YAAY,EAAE;AAC5C,UAAQ,IAAI,aAAa,KAAK,KAAK,EAAE;AACvC;AAEA,eAAe,eACb,QACA,OACA,OAC4B;AAC5B,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,2BAA2B,mBAAmB,KAAK,CAAC,IAAI;AAAA,IACvF,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,EAC9C,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,YAAQ,MAAM,4CAA4C;AAC1D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAEA,eAAe,aAAa,MAAgB,QAAgB,OAA8B;AACxF,QAAM,QAAQ,KAAK,CAAC;AACpB,MAAI,CAAC,SAAS,MAAM,WAAW,IAAI,GAAG;AACpC,YAAQ,MAAM,wBAAwB;AACtC,YAAQ,MAAM,uCAAuC;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,QAAQ,MAAM,QAAQ;AACzC,QAAM,OAAO,MAAM,eAAe,QAAQ,OAAO,KAAK;AAEtD,MAAI,CAAC,KAAK,IAAI;AACZ,YAAQ,MAAM,UAAU,KAAK,KAAK,EAAE;AACpC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,KAAK;AAEjB,MAAI,YAAY;AACd,YAAQ,IAAI,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AACxC;AAAA,EACF;AAEA,UAAQ,IAAI,mBAAmB,IAAI,EAAE,EAAE;AACvC,UAAQ,IAAI,0BAA0B,IAAI,MAAM,EAAE;AAClD,UAAQ,IAAI,0BAA0B,IAAI,aAAa,EAAE;AACzD,UAAQ,IAAI,0BAA0B,IAAI,eAAe,EAAE;AAC3D,UAAQ,IAAI,0BAA0B,IAAI,gBAAgB,IAAI,IAAI,cAAc,eAAe,IAAI,aAAa,SAAS;AACzH,UAAQ,IAAI,0BAA0B,IAAI,WAAW,EAAE;AACvD,UAAQ,IAAI,0BAA0B,IAAI,qBAAqB,IAAI,IAAI,oBAAoB,YAAY,IAAI,qBAAqB,SAAS;AACzI,MAAI,IAAI,UAAW,SAAQ,IAAI,0BAA0B,IAAI,SAAS,EAAE;AACxE,MAAI,IAAI,WAAY,SAAQ,IAAI,0BAA0B,IAAI,UAAU,EAAE;AAC5E;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,eAAe,UAAU,MAAgB,QAAgB,OAA8B;AACrF,QAAM,aAAa,YAAY,MAAM,eAAe;AACpD,MAAI,CAAC,YAAY;AACf,YAAQ,MAAM,sCAAsC;AACpD,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAa,SAAS,YAAY,MAAM,WAAW,KAAK,QAAQ,EAAE;AACxE,QAAM,kBAAkB,SAAS,YAAY,MAAM,iBAAiB,KAAK,MAAM,EAAE;AAEjF,QAAM,mBAAmB,YAAY,MAAM,kBAAkB;AAC7D,QAAM,OAAgC,EAAE,WAAW;AACnD,MAAI,kBAAkB;AACpB,SAAK,gBAAgB,iBAAiB,MAAM,GAAG,EAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;AAAA,EACxE;AAGA,UAAQ,IAAI,8BAA8B;AAC1C,QAAM,aAAa,MAAM,MAAM,GAAG,MAAM,8BAA8B;AAAA,IACpE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,WAAW,WAAW,KAAK;AAC7B,YAAQ,MAAM,4CAA4C;AAC1D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAe,MAAM,WAAW,KAAK;AAE3C,MAAI,CAAC,YAAY,IAAI;AACnB,YAAQ,MAAM,yBAAyB,YAAY,KAAK,EAAE;AAC1D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,YAAY;AAC1B,UAAQ,IAAI,gBAAgB,KAAK,EAAE;AAGnC,QAAM,WAAW,KAAK,IAAI,IAAI,aAAa;AAC3C,MAAI,aAAa;AAEjB,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,kBAAkB,GAAI;AAElC,QAAI;AACJ,QAAI;AACF,mBAAa,MAAM,eAAe,QAAQ,OAAO,KAAK;AAAA,IACxD,SAAS,KAAK;AACZ,cAAQ,MAAM,iDAAiD,GAAG,GAAG;AACrE;AAAA,IACF;AAEA,QAAI,CAAC,WAAW,IAAI;AAClB,cAAQ,MAAM,UAAU,WAAW,KAAK,EAAE;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,MAAM,WAAW;AACvB,UAAM,aAAa,IAAI,IAAI,MAAM,cAAc,IAAI,gBAAgB,IAAI,IAAI,cAAc,kBAAkB,IAAI,qBAAqB,IAAI,IAAI,oBAAoB;AAEhK,QAAI,IAAI,WAAW,YAAY;AAC7B,cAAQ,IAAI,UAAU;AACtB,mBAAa,IAAI;AAAA,IACnB,OAAO;AAEL,cAAQ,IAAI,UAAU;AAAA,IACxB;AAEA,QAAI,kBAAkB,IAAI,IAAI,MAAM,GAAG;AACrC,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,iBAAiB,IAAI,MAAM,EAAE;AACzC,cAAQ,IAAI,cAAc,IAAI,gBAAgB,IAAI,IAAI,cAAc,eAAe,IAAI,aAAa,SAAS;AAC7G,cAAQ,IAAI,kBAAkB,IAAI,qBAAqB,IAAI,IAAI,oBAAoB,YAAY,IAAI,qBAAqB,SAAS;AACjI,cAAQ,IAAI,mBAAmB,IAAI,WAAW,EAAE;AAEhD,UAAI,IAAI,WAAW,aAAa;AAC9B,gBAAQ,IAAI,qBAAqB;AACjC,gBAAQ,KAAK,CAAC;AAAA,MAChB,OAAO;AACL,gBAAQ,MAAM;AAAA,qBAAwB,IAAI,MAAM,GAAG;AACnD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,MAAM;AAAA,8CAAiD,UAAU,GAAG;AAC5E,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,mBAAyB;AAChC,UAAQ;AAAA,IACN;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAsB,WAAW,MAA+B;AAC9D,QAAM,CAAC,YAAY,GAAG,IAAI,IAAI;AAE9B,MAAI,CAAC,cAAc,eAAe,UAAU,QAAQ,MAAM,UAAU,IAAI,GAAG;AACzE,qBAAiB;AACjB;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,MAAM,IAAI,MAAM,cAAc,IAAI;AAElD,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,YAAM,cAAc,MAAM,QAAQ,KAAK;AACvC;AAAA,IACF,KAAK;AACH,YAAM,aAAa,MAAM,QAAQ,KAAK;AACtC;AAAA,IACF,KAAK;AACH,YAAM,UAAU,MAAM,QAAQ,KAAK;AACnC;AAAA,IACF;AACE,cAAQ,MAAM,wBAAwB,UAAU,EAAE;AAClD,uBAAiB;AACjB,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/release.ts"],"sourcesContent":["/**\n * CLI Release QA Management\n *\n * Trigger, poll, and check status of Release QA runs from CI/CD pipelines.\n * Used by the scheduled-release GitHub Actions workflow to gate deployments.\n */\n\nimport process from \"node:process\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\n\ntype TriggerResponse = {\n ok: boolean;\n releaseRunId?: string;\n jobId?: string;\n error?: string;\n};\n\ntype RunStatusResponse = {\n ok: boolean;\n run?: {\n id: string;\n status: string;\n triggerSource: string;\n cutoffReason: string | null;\n fromSha: string | null;\n toSha: string | null;\n commitsAnalyzed: number;\n testersSpawned: number;\n testersCompleted: number;\n testersFailed: number;\n issuesFound: number;\n regressionTestsTotal: number;\n regressionTestsPassed: number;\n regressionTestsFailed: number;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n };\n error?: string;\n};\n\n/** Terminal statuses that stop polling */\nconst TERMINAL_STATUSES = new Set([\n \"completed\",\n \"completed_with_errors\",\n \"failed\",\n \"canceled\",\n \"timeout\",\n]);\n\nasync function handleTrigger(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = getArgValue(argv, \"--property-id\");\n if (!propertyId) {\n console.error(\"Error: Missing --property-id <uuid>.\");\n console.error(\"Usage: canary release trigger --property-id <uuid> [--credential-ids <uuid,...>]\");\n process.exit(1);\n }\n\n const credentialIdsRaw = getArgValue(argv, \"--credential-ids\");\n const body: Record<string, unknown> = { propertyId };\n if (credentialIdsRaw) {\n body.credentialIds = credentialIdsRaw.split(\",\").map((id) => id.trim());\n }\n\n const res = await fetch(`${apiUrl}/api/v1/release-qa/trigger`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n const json = (await res.json()) as TriggerResponse;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n console.log(`Release QA run triggered`);\n console.log(` Run ID: ${json.releaseRunId}`);\n console.log(` Job ID: ${json.jobId}`);\n}\n\nasync function fetchRunStatus(\n apiUrl: string,\n token: string,\n runId: string\n): Promise<RunStatusResponse> {\n const res = await fetch(`${apiUrl}/api/v1/release-qa/runs/${encodeURIComponent(runId)}`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (res.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n return (await res.json()) as RunStatusResponse;\n}\n\nasync function handleStatus(argv: string[], apiUrl: string, token: string): Promise<void> {\n const runId = argv[0];\n if (!runId || runId.startsWith(\"--\")) {\n console.error(\"Error: Missing run ID.\");\n console.error(\"Usage: canary release status <run-id>\");\n process.exit(1);\n }\n\n const jsonOutput = hasFlag(argv, \"--json\");\n const json = await fetchRunStatus(apiUrl, token, runId);\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n const run = json.run!;\n\n if (jsonOutput) {\n console.log(JSON.stringify(run, null, 2));\n return;\n }\n\n console.log(`Release QA Run: ${run.id}`);\n console.log(` Status: ${run.status}`);\n console.log(` Trigger: ${run.triggerSource}`);\n console.log(` Commits analyzed: ${run.commitsAnalyzed}`);\n console.log(` Testers: ${run.testersCompleted}/${run.testersSpawned} completed, ${run.testersFailed} failed`);\n console.log(` Issues found: ${run.issuesFound}`);\n console.log(` Regression tests: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed, ${run.regressionTestsFailed} failed`);\n if (run.startedAt) console.log(` Started: ${run.startedAt}`);\n if (run.finishedAt) console.log(` Finished: ${run.finishedAt}`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function handleRun(argv: string[], apiUrl: string, token: string): Promise<void> {\n const propertyId = getArgValue(argv, \"--property-id\");\n if (!propertyId) {\n console.error(\"Error: Missing --property-id <uuid>.\");\n console.error(\n \"Usage: canary release run --property-id <uuid> [--timeout 3600] [--poll-interval 30]\"\n );\n process.exit(1);\n }\n\n const timeoutSec = parseInt(getArgValue(argv, \"--timeout\") ?? \"3600\", 10);\n const pollIntervalSec = parseInt(getArgValue(argv, \"--poll-interval\") ?? \"30\", 10);\n\n const credentialIdsRaw = getArgValue(argv, \"--credential-ids\");\n const body: Record<string, unknown> = { propertyId };\n if (credentialIdsRaw) {\n body.credentialIds = credentialIdsRaw.split(\",\").map((id) => id.trim());\n }\n\n // Trigger\n console.log(\"Triggering Release QA run...\");\n const triggerRes = await fetch(`${apiUrl}/api/v1/release-qa/trigger`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (triggerRes.status === 401) {\n console.error(\"Error: Unauthorized. Check your API token.\");\n process.exit(1);\n }\n\n const triggerJson = (await triggerRes.json()) as TriggerResponse;\n\n if (!triggerJson.ok) {\n console.error(`Error triggering run: ${triggerJson.error}`);\n process.exit(1);\n }\n\n const runId = triggerJson.releaseRunId!;\n console.log(`Run started: ${runId}`);\n\n // Poll\n const deadline = Date.now() + timeoutSec * 1000;\n let lastStatus = \"\";\n\n while (Date.now() < deadline) {\n await sleep(pollIntervalSec * 1000);\n\n let statusJson: RunStatusResponse;\n try {\n statusJson = await fetchRunStatus(apiUrl, token, runId);\n } catch (err) {\n console.error(`Warning: Failed to fetch status, retrying... (${err})`);\n continue;\n }\n\n if (!statusJson.ok) {\n console.error(`Error: ${statusJson.error}`);\n process.exit(1);\n }\n\n const run = statusJson.run!;\n const statusLine = `[${run.status}] testers: ${run.testersCompleted}/${run.testersSpawned}, regressions: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed`;\n\n if (run.status !== lastStatus) {\n console.log(statusLine);\n lastStatus = run.status;\n } else {\n // Print progress on same status change in metrics\n console.log(statusLine);\n }\n\n if (TERMINAL_STATUSES.has(run.status)) {\n console.log(\"\");\n console.log(`Run finished: ${run.status}`);\n console.log(` Testers: ${run.testersCompleted}/${run.testersSpawned} completed, ${run.testersFailed} failed`);\n console.log(` Regressions: ${run.regressionTestsPassed}/${run.regressionTestsTotal} passed, ${run.regressionTestsFailed} failed`);\n console.log(` Issues found: ${run.issuesFound}`);\n\n if (run.status === \"completed\") {\n console.log(\"\\nRelease QA PASSED\");\n process.exit(0);\n } else {\n console.error(`\\nRelease QA FAILED (${run.status})`);\n process.exit(1);\n }\n }\n }\n\n console.error(`\\nTimeout: Release QA did not complete within ${timeoutSec}s`);\n process.exit(1);\n}\n\nfunction printReleaseHelp(): void {\n console.log(\n [\n \"Usage: canary release <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" trigger --property-id <uuid> [--credential-ids <uuid,...>]\",\n \" Trigger a Release QA run\",\n \" status <run-id> [--json] Check run status\",\n \" run --property-id <uuid> [options] Trigger and poll until complete\",\n \"\",\n \"Run options:\",\n \" --timeout <seconds> Max wait time (default: 3600)\",\n \" --poll-interval <secs> Poll frequency (default: 30)\",\n \" --credential-ids <ids> Comma-separated credential UUIDs\",\n \"\",\n \"Options:\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --api-url <url> API URL override\",\n \" --token <key> API token override (or set CANARY_API_TOKEN)\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runRelease(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printReleaseHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"trigger\":\n await handleTrigger(rest, apiUrl, token);\n break;\n case \"status\":\n await handleStatus(rest, apiUrl, token);\n break;\n case \"run\":\n await handleRun(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printReleaseHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/runner/preload.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
1
2
|
import {
|
|
2
3
|
alreadyPatched,
|
|
3
4
|
getEventLog,
|
|
@@ -7,10 +8,10 @@ import {
|
|
|
7
8
|
setEventLogPath,
|
|
8
9
|
wrapExpect,
|
|
9
10
|
wrapPage
|
|
10
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-ILEPYWZX.js";
|
|
11
12
|
import {
|
|
12
13
|
__require
|
|
13
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-VLFUCAPZ.js";
|
|
14
15
|
|
|
15
16
|
// src/runner/instrumentation.ts
|
|
16
17
|
import Module from "module";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/runner/instrumentation.ts","../../src/runner/preload.ts"],"sourcesContent":["/**\n * Module._load hook instrumentation for Playwright healing.\n *\n * Intercepts `@playwright/test` imports and patches them with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport Module from \"node:module\";\nimport { createRequire } from \"node:module\";\nimport { loadCanaryConfig } from \"./config\";\nimport { alreadyPatched, markPatched, recordHealingEvent, setEventLogPath } from \"./state\";\nimport {\n wrapExpect,\n wrapPage,\n type PlaywrightExports,\n} from \"./healing-helpers\";\n\nlet DEBUG = false;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\n\nexport function installInstrumentation(): void {\n if (alreadyPatched()) return;\n\n const config = loadCanaryConfig();\n DEBUG = !!config.debug;\n if (!config.enabled) {\n markPatched();\n return;\n }\n\n setEventLogPath(config.eventLogPath);\n if (!ensurePlaywrightVersion(config.allowedPlaywrightVersion)) {\n recordHealingEvent({\n kind: \"unknown\",\n action: \"playwright_version_mismatch\",\n errorMessage: \"Playwright version does not match CANARY_PLAYWRIGHT_VERSION\",\n healed: false,\n });\n markPatched();\n return;\n }\n hookPlaywrightModuleLoad();\n\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] instrumentation installed (eventLog=${config.eventLogPath})`);\n }\n\n recordHealingEvent({\n kind: \"unknown\",\n action: \"instrumentation_ready\",\n healed: false,\n });\n markPatched();\n}\n\nfunction hookPlaywrightModuleLoad(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod = Module as any;\n const originalLoad: typeof mod._load = mod._canaryOriginalLoad ?? mod._load;\n if (!mod._canaryOriginalLoad) {\n mod._canaryOriginalLoad = originalLoad;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mod._load = function patchedLoad(request: string, parent: any, isMain: boolean) {\n if (request === \"@playwright/test\" || request === \"playwright/test\") {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] patching playwright module (request=${request})`);\n }\n const real = originalLoad.call(this, request, parent, isMain);\n return createPatchedPlaywright(real);\n }\n return originalLoad.call(this, request, parent, isMain);\n };\n}\n\nfunction createPatchedPlaywright(real: PlaywrightExports): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const options = { debug: DEBUG };\n\n const extended = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: extended,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright patched`);\n }\n return patched;\n}\n\nfunction ensurePlaywrightVersion(expected?: string): boolean {\n if (!expected) return true;\n try {\n const pkg = requireFn(\"playwright/package.json\") as { version?: string };\n if (pkg.version && pkg.version.startsWith(expected)) {\n return true;\n }\n // eslint-disable-next-line no-console\n console.warn(`[canary] Playwright version mismatch. Expected ${expected}, found ${pkg.version ?? \"unknown\"}`);\n return false;\n } catch {\n return true;\n }\n}\n","import { installInstrumentation } from \"./instrumentation\";\nimport { getEventLog } from \"./state\";\n\n// Ensure globals are initialized and patch Playwright classes in each worker process.\ngetEventLog();\ninstallInstrumentation();\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/runner/instrumentation.ts","../../src/runner/preload.ts"],"sourcesContent":["/**\n * Module._load hook instrumentation for Playwright healing.\n *\n * Intercepts `@playwright/test` imports and patches them with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport Module from \"node:module\";\nimport { createRequire } from \"node:module\";\nimport { loadCanaryConfig } from \"./config\";\nimport { alreadyPatched, markPatched, recordHealingEvent, setEventLogPath } from \"./state\";\nimport {\n wrapExpect,\n wrapPage,\n type PlaywrightExports,\n} from \"./healing-helpers\";\n\nlet DEBUG = false;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst requireFn: any = typeof require !== \"undefined\" ? require : createRequire(import.meta.url);\n\nexport function installInstrumentation(): void {\n if (alreadyPatched()) return;\n\n const config = loadCanaryConfig();\n DEBUG = !!config.debug;\n if (!config.enabled) {\n markPatched();\n return;\n }\n\n setEventLogPath(config.eventLogPath);\n if (!ensurePlaywrightVersion(config.allowedPlaywrightVersion)) {\n recordHealingEvent({\n kind: \"unknown\",\n action: \"playwright_version_mismatch\",\n errorMessage: \"Playwright version does not match CANARY_PLAYWRIGHT_VERSION\",\n healed: false,\n });\n markPatched();\n return;\n }\n hookPlaywrightModuleLoad();\n\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] instrumentation installed (eventLog=${config.eventLogPath})`);\n }\n\n recordHealingEvent({\n kind: \"unknown\",\n action: \"instrumentation_ready\",\n healed: false,\n });\n markPatched();\n}\n\nfunction hookPlaywrightModuleLoad(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod = Module as any;\n const originalLoad: typeof mod._load = mod._canaryOriginalLoad ?? mod._load;\n if (!mod._canaryOriginalLoad) {\n mod._canaryOriginalLoad = originalLoad;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n mod._load = function patchedLoad(request: string, parent: any, isMain: boolean) {\n if (request === \"@playwright/test\" || request === \"playwright/test\") {\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] patching playwright module (request=${request})`);\n }\n const real = originalLoad.call(this, request, parent, isMain);\n return createPatchedPlaywright(real);\n }\n return originalLoad.call(this, request, parent, isMain);\n };\n}\n\nfunction createPatchedPlaywright(real: PlaywrightExports): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const options = { debug: DEBUG };\n\n const extended = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: extended,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (DEBUG) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright patched`);\n }\n return patched;\n}\n\nfunction ensurePlaywrightVersion(expected?: string): boolean {\n if (!expected) return true;\n try {\n const pkg = requireFn(\"playwright/package.json\") as { version?: string };\n if (pkg.version && pkg.version.startsWith(expected)) {\n return true;\n }\n // eslint-disable-next-line no-console\n console.warn(`[canary] Playwright version mismatch. Expected ${expected}, found ${pkg.version ?? \"unknown\"}`);\n return false;\n } catch {\n return true;\n }\n}\n","import { installInstrumentation } from \"./instrumentation\";\nimport { getEventLog } from \"./state\";\n\n// Ensure globals are initialized and patch Playwright classes in each worker process.\ngetEventLog();\ninstallInstrumentation();\n"],"mappings":";;;;;;;;;;;;;;;;AAOA,OAAO,YAAY;AACnB,SAAS,qBAAqB;AAS9B,IAAI,QAAQ;AAGZ,IAAM,YAAiB,OAAO,cAAY,cAAc,YAAU,cAAc,YAAY,GAAG;AAExF,SAAS,yBAA+B;AAC7C,MAAI,eAAe,EAAG;AAEtB,QAAM,SAAS,iBAAiB;AAChC,UAAQ,CAAC,CAAC,OAAO;AACjB,MAAI,CAAC,OAAO,SAAS;AACnB,gBAAY;AACZ;AAAA,EACF;AAEA,kBAAgB,OAAO,YAAY;AACnC,MAAI,CAAC,wBAAwB,OAAO,wBAAwB,GAAG;AAC7D,uBAAmB;AAAA,MACjB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,QAAQ;AAAA,IACV,CAAC;AACD,gBAAY;AACZ;AAAA,EACF;AACA,2BAAyB;AAEzB,MAAI,OAAO;AAET,YAAQ,IAAI,uDAAuD,OAAO,YAAY,GAAG;AAAA,EAC3F;AAEA,qBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AACD,cAAY;AACd;AAEA,SAAS,2BAAiC;AAExC,QAAM,MAAM;AACZ,QAAM,eAAiC,IAAI,uBAAuB,IAAI;AACtE,MAAI,CAAC,IAAI,qBAAqB;AAC5B,QAAI,sBAAsB;AAAA,EAC5B;AAGA,MAAI,QAAQ,SAAS,YAAY,SAAiB,QAAa,QAAiB;AAC9E,QAAI,YAAY,sBAAsB,YAAY,mBAAmB;AACnE,UAAI,OAAO;AAET,gBAAQ,IAAI,uDAAuD,OAAO,GAAG;AAAA,MAC/E;AACA,YAAM,OAAO,aAAa,KAAK,MAAM,SAAS,QAAQ,MAAM;AAC5D,aAAO,wBAAwB,IAAI;AAAA,IACrC;AACA,WAAO,aAAa,KAAK,MAAM,SAAS,QAAQ,MAAM;AAAA,EACxD;AACF;AAEA,SAAS,wBAAwB,MAA4C;AAC3E,MAAK,KAAuC,gBAAiB,QAAO;AAEpE,QAAM,UAAU,EAAE,OAAO,MAAM;AAE/B,QAAM,WAAW,KAAK,KAAK,OAAO;AAAA,IAChC,MAAM,OAAO,EAAE,KAAK,GAAG,QAAQ;AAC7B,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AACD,YAAM,cAAc,SAAS,MAAM,OAAO;AAC1C,YAAM,IAAI,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AAED,QAAM,UAA6B;AAAA,IACjC,GAAG;AAAA,IACH,MAAM;AAAA,IACN,QAAQ,WAAW,KAAK,QAAQ,OAAO;AAAA,EACzC;AAEA,EAAC,QAA0C,kBAAkB;AAC7D,MAAI,OAAO;AAET,YAAQ,IAAI,oCAAoC;AAAA,EAClD;AACA,SAAO;AACT;AAEA,SAAS,wBAAwB,UAA4B;AAC3D,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACF,UAAM,MAAM,UAAU,yBAAyB;AAC/C,QAAI,IAAI,WAAW,IAAI,QAAQ,WAAW,QAAQ,GAAG;AACnD,aAAO;AAAA,IACT;AAEA,YAAQ,KAAK,kDAAkD,QAAQ,WAAW,IAAI,WAAW,SAAS,EAAE;AAC5G,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACxHA,YAAY;AACZ,uBAAuB;","names":[]}
|
package/dist/test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
|
|
1
2
|
import {
|
|
2
3
|
getEventLog,
|
|
3
4
|
loadCanaryConfig,
|
|
@@ -6,8 +7,8 @@ import {
|
|
|
6
7
|
setEventLogPath,
|
|
7
8
|
wrapExpect,
|
|
8
9
|
wrapPage
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-ILEPYWZX.js";
|
|
11
|
+
import "./chunk-VLFUCAPZ.js";
|
|
11
12
|
|
|
12
13
|
// src/test.ts
|
|
13
14
|
import * as playwright from "@playwright/test";
|
package/dist/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","/**\n * Direct Playwright wrapper for the `canary/test` import path.\n *\n * Wraps `test`, `expect`, and the `page` fixture with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport { recordHealingEvent } from \"./state\";\nimport {\n wrapExpect,\n wrapPage,\n type PlaywrightExports,\n type WrapOptions,\n} from \"./healing-helpers\";\n\nexport type { PlaywrightExports, WrapOptions };\n\nexport function wrapPlaywright(real: PlaywrightExports, options: WrapOptions): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const patchedTest = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: patchedTest,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (options.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright surface wrapped`);\n }\n return patched;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/test.ts","../src/runner/wrapper.ts"],"sourcesContent":["import * as playwright from \"@playwright/test\";\nimport { loadCanaryConfig } from \"./runner/config\";\nimport { getEventLog, markPatched, recordHealingEvent, setEventLogPath } from \"./runner/state\";\nimport { wrapPlaywright, type PlaywrightExports } from \"./runner/wrapper\";\n\nconst config = loadCanaryConfig();\nsetEventLogPath(config.eventLogPath);\ngetEventLog();\n\nconst patched = wrapPlaywright(playwright as unknown as PlaywrightExports, { debug: !!config.debug });\nmarkPatched();\n\nrecordHealingEvent({\n kind: \"unknown\",\n action: \"module_wrap_ready\",\n healed: false,\n});\n\nconst {\n test,\n expect,\n chromium,\n firefox,\n webkit,\n devices,\n request,\n selectors,\n defineConfig,\n} = patched as typeof import(\"@playwright/test\");\n\nexport { test, expect, chromium, firefox, webkit, devices, request, selectors, defineConfig };\nexport type * from \"@playwright/test\";\n","/**\n * Direct Playwright wrapper for the `canary/test` import path.\n *\n * Wraps `test`, `expect`, and the `page` fixture with healing-aware proxies.\n * Delegates all proxy/healing logic to `healing-helpers.ts`.\n */\n\nimport { recordHealingEvent } from \"./state\";\nimport {\n wrapExpect,\n wrapPage,\n type PlaywrightExports,\n type WrapOptions,\n} from \"./healing-helpers\";\n\nexport type { PlaywrightExports, WrapOptions };\n\nexport function wrapPlaywright(real: PlaywrightExports, options: WrapOptions): PlaywrightExports {\n if ((real as { __canaryPatched?: boolean }).__canaryPatched) return real;\n\n const patchedTest = real.test.extend({\n page: async ({ page }, use) => {\n recordHealingEvent({\n kind: \"page\",\n action: \"fixture_initialized\",\n healed: false,\n });\n const wrappedPage = wrapPage(page, options);\n await use(wrappedPage);\n },\n });\n\n const patched: PlaywrightExports = {\n ...real,\n test: patchedTest,\n expect: wrapExpect(real.expect, options),\n };\n\n (patched as { __canaryPatched?: boolean }).__canaryPatched = true;\n if (options.debug) {\n // eslint-disable-next-line no-console\n console.log(`[canary][debug] playwright surface wrapped`);\n }\n return patched;\n}\n"],"mappings":";;;;;;;;;;;;;AAAA,YAAY,gBAAgB;;;ACiBrB,SAAS,eAAe,MAAyB,SAAyC;AAC/F,MAAK,KAAuC,gBAAiB,QAAO;AAEpE,QAAM,cAAc,KAAK,KAAK,OAAO;AAAA,IACnC,MAAM,OAAO,EAAE,KAAK,GAAG,QAAQ;AAC7B,yBAAmB;AAAA,QACjB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AACD,YAAM,cAAc,SAAS,MAAM,OAAO;AAC1C,YAAM,IAAI,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AAED,QAAMA,WAA6B;AAAA,IACjC,GAAG;AAAA,IACH,MAAM;AAAA,IACN,QAAQ,WAAW,KAAK,QAAQ,OAAO;AAAA,EACzC;AAEA,EAACA,SAA0C,kBAAkB;AAC7D,MAAI,QAAQ,OAAO;AAEjB,YAAQ,IAAI,4CAA4C;AAAA,EAC1D;AACA,SAAOA;AACT;;;ADvCA,IAAM,SAAS,iBAAiB;AAChC,gBAAgB,OAAO,YAAY;AACnC,YAAY;AAEZ,IAAM,UAAU,eAAe,YAA4C,EAAE,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;AACpG,YAAY;AAEZ,mBAAmB;AAAA,EACjB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AACV,CAAC;AAED,IAAM;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;","names":["patched"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canaryai/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@ai-sdk/anthropic": "^2.0.23",
|
|
42
42
|
"@ai-sdk/azure": "^2.0.68",
|
|
43
43
|
"@ai-sdk/openai": "^2.0.42",
|
|
44
|
+
"@chatsdet/browser-core": "workspace:*",
|
|
44
45
|
"@modelcontextprotocol/sdk": "^1.0.3",
|
|
45
46
|
"ai": "^5.0.60",
|
|
46
47
|
"dotenv": "^17.2.3",
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"zod": "^4.1.12"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@chatsdet/browser-core": "workspace:*",
|
|
52
52
|
"@playwright/test": "1.57.0-alpha-2025-10-16",
|
|
53
53
|
"@types/node": "^20.12.12",
|
|
54
54
|
"playwright": "1.57.0-alpha-2025-10-16",
|
package/dist/chunk-OE6O7H45.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// ../browser-core/src/pdf-extract.ts
|
|
2
|
-
import * as fs from "fs/promises";
|
|
3
|
-
|
|
4
|
-
// ../browser-core/src/logger.ts
|
|
5
|
-
var consoleLogger = {
|
|
6
|
-
debug: (msg, data) => {
|
|
7
|
-
if (data) console.debug(msg, data);
|
|
8
|
-
else console.debug(msg);
|
|
9
|
-
},
|
|
10
|
-
info: (msg, data) => {
|
|
11
|
-
if (data) console.info(msg, data);
|
|
12
|
-
else console.info(msg);
|
|
13
|
-
},
|
|
14
|
-
warn: (msg, data) => {
|
|
15
|
-
if (data) console.warn(msg, data);
|
|
16
|
-
else console.warn(msg);
|
|
17
|
-
},
|
|
18
|
-
error: (msg, data) => {
|
|
19
|
-
if (data) console.error(msg, data);
|
|
20
|
-
else console.error(msg);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// ../browser-core/src/pdf-extract.ts
|
|
25
|
-
var _logger = consoleLogger;
|
|
26
|
-
function setPdfExtractLogger(l) {
|
|
27
|
-
_logger = l;
|
|
28
|
-
}
|
|
29
|
-
var MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024;
|
|
30
|
-
var MAX_OUTPUT_CHARS = 1e5;
|
|
31
|
-
async function extractPdfTextFromBuffer(buffer) {
|
|
32
|
-
if (buffer.length > MAX_FILE_SIZE_BYTES) {
|
|
33
|
-
_logger.warn("[PdfExtract] PDF too large for text extraction", {
|
|
34
|
-
sizeBytes: buffer.length,
|
|
35
|
-
maxBytes: MAX_FILE_SIZE_BYTES
|
|
36
|
-
});
|
|
37
|
-
return {
|
|
38
|
-
text: `[PDF too large for text extraction: ${formatBytes(buffer.length)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,
|
|
39
|
-
totalPages: 0
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
const { extractText } = await import("./dist-6NXLJYRZ.js");
|
|
43
|
-
const uint8 = new Uint8Array(buffer);
|
|
44
|
-
const result = await extractText(uint8, { mergePages: true });
|
|
45
|
-
let text = result.text ?? "";
|
|
46
|
-
const totalPages = result.totalPages ?? 0;
|
|
47
|
-
if (text.length > MAX_OUTPUT_CHARS) {
|
|
48
|
-
_logger.info("[PdfExtract] Truncating extracted text", {
|
|
49
|
-
originalLength: text.length,
|
|
50
|
-
truncatedTo: MAX_OUTPUT_CHARS,
|
|
51
|
-
totalPages
|
|
52
|
-
});
|
|
53
|
-
text = text.slice(0, MAX_OUTPUT_CHARS) + `
|
|
54
|
-
|
|
55
|
-
[Text truncated at ${MAX_OUTPUT_CHARS} characters]`;
|
|
56
|
-
}
|
|
57
|
-
_logger.info("[PdfExtract] Text extracted successfully", {
|
|
58
|
-
textLength: text.length,
|
|
59
|
-
totalPages,
|
|
60
|
-
sizeBytes: buffer.length
|
|
61
|
-
});
|
|
62
|
-
return { text, totalPages };
|
|
63
|
-
}
|
|
64
|
-
async function extractPdfText(filePath) {
|
|
65
|
-
const stat2 = await fs.stat(filePath);
|
|
66
|
-
if (stat2.size > MAX_FILE_SIZE_BYTES) {
|
|
67
|
-
_logger.warn("[PdfExtract] PDF file too large for text extraction", {
|
|
68
|
-
filePath,
|
|
69
|
-
sizeBytes: stat2.size,
|
|
70
|
-
maxBytes: MAX_FILE_SIZE_BYTES
|
|
71
|
-
});
|
|
72
|
-
return {
|
|
73
|
-
text: `[PDF too large for text extraction: ${formatBytes(stat2.size)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,
|
|
74
|
-
totalPages: 0
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
const buffer = await fs.readFile(filePath);
|
|
78
|
-
return extractPdfTextFromBuffer(Buffer.from(buffer));
|
|
79
|
-
}
|
|
80
|
-
function formatBytes(bytes) {
|
|
81
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
82
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
83
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export {
|
|
87
|
-
consoleLogger,
|
|
88
|
-
setPdfExtractLogger,
|
|
89
|
-
extractPdfTextFromBuffer,
|
|
90
|
-
extractPdfText
|
|
91
|
-
};
|
|
92
|
-
//# sourceMappingURL=chunk-OE6O7H45.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../browser-core/src/pdf-extract.ts","../../browser-core/src/logger.ts"],"sourcesContent":["/**\n * PDF text extraction utility.\n *\n * Uses `unpdf` to extract text content from PDF files and buffers.\n * Includes size guards to prevent excessive memory usage.\n *\n * @module pdf-extract\n */\n\nimport * as fs from 'fs/promises';\nimport { consoleLogger, type BrowserLogger } from './logger.js';\n\nlet _logger: BrowserLogger = consoleLogger;\n\nexport function setPdfExtractLogger(l: BrowserLogger): void {\n _logger = l;\n}\n\nconst MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50MB\nconst MAX_OUTPUT_CHARS = 100_000;\n\n/**\n * Extract text content from a PDF buffer.\n * Returns the extracted text and total page count.\n */\nexport async function extractPdfTextFromBuffer(\n buffer: Buffer\n): Promise<{ text: string; totalPages: number }> {\n if (buffer.length > MAX_FILE_SIZE_BYTES) {\n _logger.warn('[PdfExtract] PDF too large for text extraction', {\n sizeBytes: buffer.length,\n maxBytes: MAX_FILE_SIZE_BYTES,\n });\n return {\n text: `[PDF too large for text extraction: ${formatBytes(buffer.length)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,\n totalPages: 0,\n };\n }\n\n const { extractText } = await import('unpdf');\n const uint8 = new Uint8Array(buffer);\n const result = await extractText(uint8, { mergePages: true });\n\n let text = result.text ?? '';\n const totalPages = result.totalPages ?? 0;\n\n if (text.length > MAX_OUTPUT_CHARS) {\n _logger.info('[PdfExtract] Truncating extracted text', {\n originalLength: text.length,\n truncatedTo: MAX_OUTPUT_CHARS,\n totalPages,\n });\n text = text.slice(0, MAX_OUTPUT_CHARS) + `\\n\\n[Text truncated at ${MAX_OUTPUT_CHARS} characters]`;\n }\n\n _logger.info('[PdfExtract] Text extracted successfully', {\n textLength: text.length,\n totalPages,\n sizeBytes: buffer.length,\n });\n\n return { text, totalPages };\n}\n\n/**\n * Extract text content from a PDF file on disk.\n */\nexport async function extractPdfText(\n filePath: string\n): Promise<{ text: string; totalPages: number }> {\n const stat = await fs.stat(filePath);\n\n if (stat.size > MAX_FILE_SIZE_BYTES) {\n _logger.warn('[PdfExtract] PDF file too large for text extraction', {\n filePath,\n sizeBytes: stat.size,\n maxBytes: MAX_FILE_SIZE_BYTES,\n });\n return {\n text: `[PDF too large for text extraction: ${formatBytes(stat.size)} exceeds ${formatBytes(MAX_FILE_SIZE_BYTES)} limit]`,\n totalPages: 0,\n };\n }\n\n const buffer = await fs.readFile(filePath);\n return extractPdfTextFromBuffer(Buffer.from(buffer));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n","/**\n * Browser Logger Interface\n *\n * Abstracts logging so browser-core works in any environment:\n * - Cloud (apps/api): Wraps pino logger\n * - CLI (canary-cli): Uses console logger\n *\n * @module logger\n */\n\nexport interface BrowserLogger {\n debug(msg: string, data?: Record<string, unknown>): void;\n info(msg: string, data?: Record<string, unknown>): void;\n warn(msg: string, data?: Record<string, unknown>): void;\n error(msg: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Default console-based logger. Works everywhere without dependencies.\n */\nexport const consoleLogger: BrowserLogger = {\n debug: (msg, data) => {\n if (data) console.debug(msg, data);\n else console.debug(msg);\n },\n info: (msg, data) => {\n if (data) console.info(msg, data);\n else console.info(msg);\n },\n warn: (msg, data) => {\n if (data) console.warn(msg, data);\n else console.warn(msg);\n },\n error: (msg, data) => {\n if (data) console.error(msg, data);\n else console.error(msg);\n },\n};\n"],"mappings":";AASA,YAAY,QAAQ;;;ACWb,IAAM,gBAA+B;AAAA,EAC1C,OAAO,CAAC,KAAK,SAAS;AACpB,QAAI,KAAM,SAAQ,MAAM,KAAK,IAAI;AAAA,QAC5B,SAAQ,MAAM,GAAG;AAAA,EACxB;AAAA,EACA,MAAM,CAAC,KAAK,SAAS;AACnB,QAAI,KAAM,SAAQ,KAAK,KAAK,IAAI;AAAA,QAC3B,SAAQ,KAAK,GAAG;AAAA,EACvB;AAAA,EACA,MAAM,CAAC,KAAK,SAAS;AACnB,QAAI,KAAM,SAAQ,KAAK,KAAK,IAAI;AAAA,QAC3B,SAAQ,KAAK,GAAG;AAAA,EACvB;AAAA,EACA,OAAO,CAAC,KAAK,SAAS;AACpB,QAAI,KAAM,SAAQ,MAAM,KAAK,IAAI;AAAA,QAC5B,SAAQ,MAAM,GAAG;AAAA,EACxB;AACF;;;ADzBA,IAAI,UAAyB;AAEtB,SAAS,oBAAoB,GAAwB;AAC1D,YAAU;AACZ;AAEA,IAAM,sBAAsB,KAAK,OAAO;AACxC,IAAM,mBAAmB;AAMzB,eAAsB,yBACpB,QAC+C;AAC/C,MAAI,OAAO,SAAS,qBAAqB;AACvC,YAAQ,KAAK,kDAAkD;AAAA,MAC7D,WAAW,OAAO;AAAA,MAClB,UAAU;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,MACL,MAAM,uCAAuC,YAAY,OAAO,MAAM,CAAC,YAAY,YAAY,mBAAmB,CAAC;AAAA,MACnH,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAO;AAC5C,QAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,QAAM,SAAS,MAAM,YAAY,OAAO,EAAE,YAAY,KAAK,CAAC;AAE5D,MAAI,OAAO,OAAO,QAAQ;AAC1B,QAAM,aAAa,OAAO,cAAc;AAExC,MAAI,KAAK,SAAS,kBAAkB;AAClC,YAAQ,KAAK,0CAA0C;AAAA,MACrD,gBAAgB,KAAK;AAAA,MACrB,aAAa;AAAA,MACb;AAAA,IACF,CAAC;AACD,WAAO,KAAK,MAAM,GAAG,gBAAgB,IAAI;AAAA;AAAA,qBAA0B,gBAAgB;AAAA,EACrF;AAEA,UAAQ,KAAK,4CAA4C;AAAA,IACvD,YAAY,KAAK;AAAA,IACjB;AAAA,IACA,WAAW,OAAO;AAAA,EACpB,CAAC;AAED,SAAO,EAAE,MAAM,WAAW;AAC5B;AAKA,eAAsB,eACpB,UAC+C;AAC/C,QAAMA,QAAO,MAAS,QAAK,QAAQ;AAEnC,MAAIA,MAAK,OAAO,qBAAqB;AACnC,YAAQ,KAAK,uDAAuD;AAAA,MAClE;AAAA,MACA,WAAWA,MAAK;AAAA,MAChB,UAAU;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,MACL,MAAM,uCAAuC,YAAYA,MAAK,IAAI,CAAC,YAAY,YAAY,mBAAmB,CAAC;AAAA,MAC/G,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,SAAS,MAAS,YAAS,QAAQ;AACzC,SAAO,yBAAyB,OAAO,KAAK,MAAM,CAAC;AACrD;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;","names":["stat"]}
|
package/dist/chunk-PLDDJCW6.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
-
}) : x)(function(x) {
|
|
10
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
-
});
|
|
13
|
-
var __esm = (fn, res) => function __init() {
|
|
14
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
-
};
|
|
16
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
17
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
-
};
|
|
19
|
-
var __export = (target, all) => {
|
|
20
|
-
for (var name in all)
|
|
21
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
22
|
-
};
|
|
23
|
-
var __copyProps = (to, from, except, desc) => {
|
|
24
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
25
|
-
for (let key of __getOwnPropNames(from))
|
|
26
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
27
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
28
|
-
}
|
|
29
|
-
return to;
|
|
30
|
-
};
|
|
31
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
32
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
33
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
34
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
35
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
36
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
37
|
-
mod
|
|
38
|
-
));
|
|
39
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
40
|
-
|
|
41
|
-
export {
|
|
42
|
-
__require,
|
|
43
|
-
__esm,
|
|
44
|
-
__commonJS,
|
|
45
|
-
__export,
|
|
46
|
-
__toESM,
|
|
47
|
-
__toCommonJS
|
|
48
|
-
};
|
|
49
|
-
//# sourceMappingURL=chunk-PLDDJCW6.js.map
|