@hasna/uptime 0.1.24 → 0.1.26

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/cli/index.js CHANGED
@@ -7217,6 +7217,7 @@ var DEFAULT_WORKSPACE_ID = "workspace-id";
7217
7217
  var DEFAULT_VPC_ID = "vpc-xxxxxxxx";
7218
7218
  var DEFAULT_HOSTED_SQLITE_DB = "/data/uptime/uptime.db";
7219
7219
  var DEFAULT_PROTECTED_ACCESS_MODE = "cloudfront_default_domain";
7220
+ var DEFAULT_CLOUDFRONT_ORIGIN_PROTOCOL_POLICY = "http-only";
7220
7221
  function buildAwsDeploymentPlan(options = {}) {
7221
7222
  const region = clean(options.region, DEFAULT_REGION);
7222
7223
  const stage = clean(options.stage, DEFAULT_STAGE);
@@ -7229,8 +7230,11 @@ function buildAwsDeploymentPlan(options = {}) {
7229
7230
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
7230
7231
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
7231
7232
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
7232
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.24");
7233
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.26");
7234
+ const runtimePackageIntegrity = options.runtimePackageIntegrity?.trim() || undefined;
7233
7235
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
7236
+ const cloudfrontOriginProtocolPolicy = options.cloudfrontOriginProtocolPolicy ?? DEFAULT_CLOUDFRONT_ORIGIN_PROTOCOL_POLICY;
7237
+ const cloudfrontOriginDomainName = clean(options.cloudfrontOriginDomainName, "<alb-dns-name>");
7234
7238
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
7235
7239
  const cluster = `${prefix}-${stage}`;
7236
7240
  const secrets = {
@@ -7297,6 +7301,13 @@ function buildAwsDeploymentPlan(options = {}) {
7297
7301
  protectedAccessMode,
7298
7302
  edgeDistribution: protectedAccessMode === "cloudfront_default_domain" ? `${prefix}-${stage}-edge` : undefined,
7299
7303
  protectedAccessUrl,
7304
+ cloudfrontOrigin: protectedAccessMode === "cloudfront_default_domain" ? {
7305
+ protocolPolicy: cloudfrontOriginProtocolPolicy,
7306
+ domainName: cloudfrontOriginProtocolPolicy === "https-only" ? cloudfrontOriginDomainName : "<alb-dns-name>",
7307
+ requiresMatchingCertificate: cloudfrontOriginProtocolPolicy === "https-only",
7308
+ liveTrafficApproved: false,
7309
+ risk: cloudfrontOriginProtocolPolicy === "http-only" ? "Temporary HTTP-origin bridge: web scale-up requires allow_cloudfront_http_origin_live_traffic=true for bounded smokes, or switch to https-only with cloudfront_origin_domain_name plus certificate_arn." : "CloudFront HTTPS-origin mode requires the origin hostname to resolve to the ALB and match certificate_arn."
7310
+ } : undefined,
7300
7311
  originVerification: protectedAccessMode === "cloudfront_default_domain" ? {
7301
7312
  mode: "cloudfront_origin_header",
7302
7313
  requiredBeforeScaleUp: true,
@@ -7329,6 +7340,7 @@ function buildAwsDeploymentPlan(options = {}) {
7329
7340
  repository: ecrRepository,
7330
7341
  uri: image,
7331
7342
  dockerfile: "Dockerfile.package",
7343
+ expectedIntegrity: runtimePackageIntegrity,
7332
7344
  buildCommand: `BLOCKED: after infra approval, AWS CodeBuild builds Dockerfile.package from @hasna/uptime@${runtimePackageVersion} into ${imageRepositoryUri}`,
7333
7345
  pushCommands: [
7334
7346
  `BLOCKED: start ${prefix}-${stage}-image-builder only through the approved deploy pipeline after @hasna/uptime@${runtimePackageVersion} is published`,
@@ -7355,16 +7367,16 @@ function buildAwsDeploymentPlan(options = {}) {
7355
7367
  `Infra PR must declare CodeBuild image builder ${prefix}-${stage}-image-builder for @hasna/uptime@${runtimePackageVersion}.`,
7356
7368
  `Infra PR must declare hardened S3 evidence bucket ${evidenceBucket} with KMS, versioning, lifecycle, and public access block.`,
7357
7369
  `Infra PR must declare encrypted EFS ${prefix}-${stage}-data with access point, mount targets, and AWS Backup plan.`,
7358
- protectedAccessMode === "cloudfront_default_domain" ? "Infra PR must declare CloudFront default-domain HTTPS edge, ALB HTTP listener restricted to CloudFront origin-facing ranges, CloudFront-only origin verification header binding, ECS/Fargate cluster, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs." : `Infra PR must declare ECS/Fargate cluster ${cluster}, ALB HTTPS listener, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs.`,
7370
+ protectedAccessMode === "cloudfront_default_domain" ? "Infra PR must declare CloudFront default-domain HTTPS edge, ALB origin listener restricted to CloudFront origin-facing ranges, CloudFront-only origin verification header binding, ECS/Fargate cluster, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs. Token-bearing live traffic must use cloudfront_origin_protocol_policy=https-only with a matching origin hostname/certificate, or carry explicit allow_cloudfront_http_origin_live_traffic=true bounded-smoke risk acceptance." : `Infra PR must declare ECS/Fargate cluster ${cluster}, ALB HTTPS listener, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs.`,
7359
7371
  "Only apply the infra plan from the approved infrastructure repository after review evidence is attached."
7360
7372
  ],
7361
7373
  deploy: [
7362
7374
  "Build and publish the image only after the Dockerfile/container target is reviewed.",
7363
- `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion} and record the pushed image digest.`,
7375
+ runtimePackageIntegrity ? `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion}; it must verify npm dist.integrity ${runtimePackageIntegrity} before extracting the package, then record the pushed image digest.` : `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion}; set runtime_package_integrity from npm dist.integrity before live use, then record the pushed image digest.`,
7364
7376
  "For the EFS SQLite bridge, do not run migration, scheduler, public-probe, or reporter tasks; keep them at desired count 0 until Postgres and cloud leases exist.",
7365
7377
  `Register task definitions for ${services.map((service) => service.name).join(", ")} using valueFrom secrets.`,
7366
7378
  `Update ECS services in cluster ${cluster} one component at a time through the approved deploy pipeline.`,
7367
- protectedAccessMode === "cloudfront_default_domain" ? "Use the CloudFront default HTTPS domain with origin verification header binding for first protected access; add custom DNS/certificate only after edge ownership is approved." : `Create Route53/edge record for ${hostname} only after ALB health checks pass and auth denial smokes succeed.`
7379
+ protectedAccessMode === "cloudfront_default_domain" ? "Use the CloudFront default HTTPS domain with origin verification header binding for first protected access; before token-bearing live traffic, switch the origin to https-only with a matching origin hostname/certificate or set allow_cloudfront_http_origin_live_traffic=true only for a bounded approved smoke." : `Create Route53/edge record for ${hostname} only after ALB health checks pass and auth denial smokes succeed.`
7368
7380
  ],
7369
7381
  rollback: [
7370
7382
  "Keep previous task definition ARNs before each service update.",
@@ -7381,6 +7393,8 @@ function buildAwsDeploymentPlan(options = {}) {
7381
7393
  blockers: [
7382
7394
  "The infrastructure owner repository was not found in this workspace.",
7383
7395
  protectedAccessMode === "cloudfront_default_domain" ? "CloudFront origin verification header binding must be enabled and direct-origin denial must be proven before web desired count is raised above 0." : "ALB HTTPS ingress policy and auth-denial smokes must be proven before web desired count is raised above 0.",
7396
+ ...protectedAccessMode === "cloudfront_default_domain" && cloudfrontOriginProtocolPolicy === "http-only" ? ["CloudFront-to-ALB origin transport is still http-only; web scale-up now requires explicit allow_cloudfront_http_origin_live_traffic=true risk acceptance, and token-bearing live traffic should use https-only origin mode."] : [],
7397
+ ...protectedAccessMode === "cloudfront_default_domain" && cloudfrontOriginProtocolPolicy === "https-only" && cloudfrontOriginDomainName === "<alb-dns-name>" ? ["CloudFront https-only origin mode needs cloudfront_origin_domain_name that resolves to the ALB and matches certificate_arn."] : [],
7384
7398
  "The EFS SQLite bridge is single-writer only: web target desired count is 1 and scheduler/public-probe/reporter targets remain 0 until Postgres and cloud leases exist.",
7385
7399
  "Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
7386
7400
  "Public probe execution still needs cloud check-job leases wired to runHostedHttpCheck and live policy-decision log evidence.",
@@ -7389,8 +7403,9 @@ function buildAwsDeploymentPlan(options = {}) {
7389
7403
  requiredEvidence: [
7390
7404
  "Infrastructure PR/synth/plan from the approved infra repository.",
7391
7405
  "CodeBuild image-builder run, container smoke, and immutable image digest.",
7406
+ "Published package dist.integrity pinned in the private infra root or an explicit not-live exception.",
7392
7407
  "ECS task definitions using secrets.valueFrom only.",
7393
- "CloudFront-default-domain origin-header config or ALB TLS auth-denial smokes, direct-origin denial evidence, and web alarm checks.",
7408
+ "CloudFront-default-domain origin-header config, origin transport decision, direct-origin denial evidence, auth-denial smokes, and web alarm checks.",
7394
7409
  "Single-writer ECS evidence: one web task maximum and no scheduler/public-probe/reporter EFS mounts.",
7395
7410
  "EFS encryption, access point, mount-target, AWS Backup, and restore-drill evidence.",
7396
7411
  "S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
@@ -7403,11 +7418,13 @@ function buildAwsDeploymentPlan(options = {}) {
7403
7418
  notes: [
7404
7419
  "This plan generator does not call AWS.",
7405
7420
  "Blocked plan output intentionally avoids copy-pastable AWS mutation commands.",
7406
- "Default protected access uses CloudFront's HTTPS default domain so first deploy is not blocked on custom DNS or ACM.",
7421
+ "Default protected access uses CloudFront's HTTPS default domain so first zero-count deploy is not blocked on custom DNS or ACM.",
7407
7422
  "CloudFront default-domain mode still requires origin verification header binding before live scale-up; the header value is sensitive state/config material, not public documentation.",
7423
+ "CloudFront HTTPS-origin mode requires a dedicated origin DNS hostname and matching ACM certificate; do not assume the ALB DNS name can satisfy TLS verification.",
7408
7424
  "Hosted runtime uses explicit EFS-backed SQLite at HASNA_UPTIME_HOSTED_SQLITE_DB until the async Postgres adapter exists.",
7409
7425
  "Do not set HASNA_UPTIME_DATABASE_URL for hosted tasks until the Postgres adapter is implemented.",
7410
7426
  "Secrets are represented as secret names/refs and must be injected with valueFrom.",
7427
+ "Set runtime_package_integrity in the approved infra root after publish so the AWS image builder verifies the npm tarball before ECR build.",
7411
7428
  "Actual deploy belongs in the deploy_release_operate_final goal node after infra review."
7412
7429
  ]
7413
7430
  }
@@ -7514,6 +7531,72 @@ function shellEscape(value) {
7514
7531
  return `'${value.replace(/'/g, "'\\''")}'`;
7515
7532
  }
7516
7533
 
7534
+ // src/workers.ts
7535
+ var DEFAULT_INTERVAL_MS = 30000;
7536
+ async function runHostedPublicChecksWorker(options) {
7537
+ const intervalMs = normalizePositiveInteger(options.intervalMs ?? DEFAULT_INTERVAL_MS, "intervalMs");
7538
+ const maxRuntimeMs = options.maxRuntimeMs === undefined ? undefined : normalizePositiveInteger(options.maxRuntimeMs, "maxRuntimeMs");
7539
+ const maxIterations = options.maxIterations === undefined ? undefined : normalizePositiveInteger(options.maxIterations, "maxIterations");
7540
+ const clock = options.now ?? (() => new Date);
7541
+ const sleep = options.sleep ?? abortableSleep;
7542
+ const startedAtDate = clock();
7543
+ const startedAt = startedAtDate.toISOString();
7544
+ const deadline = maxRuntimeMs === undefined ? undefined : startedAtDate.getTime() + maxRuntimeMs;
7545
+ let iterations = 0;
7546
+ let checked = 0;
7547
+ while (!options.signal?.aborted) {
7548
+ if (maxIterations !== undefined && iterations >= maxIterations)
7549
+ break;
7550
+ const now = clock();
7551
+ if (deadline !== undefined && now.getTime() >= deadline)
7552
+ break;
7553
+ const iteration = iterations + 1;
7554
+ const iterationStartedAt = now.toISOString();
7555
+ const results = await options.runner.runDueHostedPublicChecks(now, { workspaceId: options.workspaceId });
7556
+ const finishedAt = clock().toISOString();
7557
+ iterations = iteration;
7558
+ checked += results.length;
7559
+ options.onIteration?.({
7560
+ iteration,
7561
+ checked: results.length,
7562
+ startedAt: iterationStartedAt,
7563
+ finishedAt
7564
+ });
7565
+ if (maxIterations !== undefined && iterations >= maxIterations)
7566
+ break;
7567
+ if (deadline !== undefined && clock().getTime() >= deadline)
7568
+ break;
7569
+ await sleep(intervalMs, options.signal);
7570
+ }
7571
+ return {
7572
+ kind: "open-uptime.hosted-public-checks-worker",
7573
+ status: options.signal?.aborted ? "stopped" : "completed",
7574
+ workspaceId: options.workspaceId?.trim() || null,
7575
+ iterations,
7576
+ checked,
7577
+ startedAt,
7578
+ finishedAt: clock().toISOString()
7579
+ };
7580
+ }
7581
+ function normalizePositiveInteger(value, name) {
7582
+ if (!Number.isInteger(value) || value <= 0)
7583
+ throw new Error(`${name} must be a positive integer`);
7584
+ return value;
7585
+ }
7586
+ function abortableSleep(ms, signal) {
7587
+ if (signal?.aborted)
7588
+ return Promise.resolve();
7589
+ return new Promise((resolve) => {
7590
+ const timer = setTimeout(done, ms);
7591
+ function done() {
7592
+ signal?.removeEventListener("abort", done);
7593
+ clearTimeout(timer);
7594
+ resolve();
7595
+ }
7596
+ signal?.addEventListener("abort", done, { once: true });
7597
+ });
7598
+ }
7599
+
7517
7600
  // src/cli/index.ts
7518
7601
  var program2 = new Command;
7519
7602
  program2.name("uptime").description("Local-first uptime and downtime monitoring").version(packageVersion()).option("-j, --json", "print JSON");
@@ -7829,7 +7912,7 @@ program2.command("audit").description("List local audit events").option("--resou
7829
7912
  }
7830
7913
  });
7831
7914
  var cloud = program2.command("cloud").description("Generate dry-run cloud deployment and private-probe configuration artifacts");
7832
- cloud.command("plan").description("Generate a dry-run AWS deployment plan").option("--account <name>", "AWS account/profile label", "aws-profile").option("--region <region>", "AWS region", "us-east-1").option("--stage <stage>", "deployment stage", "prod").option("--hostname <hostname>", "hosted Open Uptime hostname", "uptime.example.com").option("--workspace-id <id>", "workspace id", "workspace-id").option("--vpc-id <id>", "target VPC id").option("--hosted-sqlite-db <path>", "hosted SQLite path on the EFS mount").option("--rds-instance-id <id>", "deprecated; ignored until the hosted Postgres adapter exists").option("--database-secret-name <name>", "deprecated; ignored until the hosted Postgres adapter exists").option("--ecr-repository <name>", "ECR repository name").option("--image <uri>", "container image URI").option("--runtime-package-version <version>", "published @hasna/uptime version for the AWS image builder").addOption(new Option("--protected-access-mode <mode>", "protected web access mode").choices(["cloudfront_default_domain", "alb_https_cert"]).default("cloudfront_default_domain")).option("--evidence-bucket <name>", "S3 evidence bucket name").option("-j, --json", "print JSON").action((opts) => {
7915
+ cloud.command("plan").description("Generate a dry-run AWS deployment plan").option("--account <name>", "AWS account/profile label", "aws-profile").option("--region <region>", "AWS region", "us-east-1").option("--stage <stage>", "deployment stage", "prod").option("--hostname <hostname>", "hosted Open Uptime hostname", "uptime.example.com").option("--workspace-id <id>", "workspace id", "workspace-id").option("--vpc-id <id>", "target VPC id").option("--hosted-sqlite-db <path>", "hosted SQLite path on the EFS mount").option("--rds-instance-id <id>", "deprecated; ignored until the hosted Postgres adapter exists").option("--database-secret-name <name>", "deprecated; ignored until the hosted Postgres adapter exists").option("--ecr-repository <name>", "ECR repository name").option("--image <uri>", "container image URI").option("--runtime-package-version <version>", "published @hasna/uptime version for the AWS image builder").option("--runtime-package-integrity <integrity>", "expected npm dist.integrity for the runtime package").addOption(new Option("--protected-access-mode <mode>", "protected web access mode").choices(["cloudfront_default_domain", "alb_https_cert"]).default("cloudfront_default_domain")).addOption(new Option("--cloudfront-origin-protocol-policy <policy>", "CloudFront-to-ALB origin protocol policy").choices(["http-only", "https-only"]).default("http-only")).option("--cloudfront-origin-domain-name <hostname>", "origin hostname for CloudFront https-only mode; must resolve to the ALB and match certificate_arn").option("--evidence-bucket <name>", "S3 evidence bucket name").option("-j, --json", "print JSON").action((opts) => {
7833
7916
  try {
7834
7917
  const plan = buildAwsDeploymentPlan({
7835
7918
  accountName: opts.account,
@@ -7844,7 +7927,10 @@ cloud.command("plan").description("Generate a dry-run AWS deployment plan").opti
7844
7927
  ecrRepository: opts.ecrRepository,
7845
7928
  image: opts.image,
7846
7929
  runtimePackageVersion: opts.runtimePackageVersion,
7930
+ runtimePackageIntegrity: opts.runtimePackageIntegrity,
7847
7931
  protectedAccessMode: opts.protectedAccessMode,
7932
+ cloudfrontOriginProtocolPolicy: opts.cloudfrontOriginProtocolPolicy,
7933
+ cloudfrontOriginDomainName: opts.cloudfrontOriginDomainName,
7848
7934
  evidenceBucket: opts.evidenceBucket
7849
7935
  });
7850
7936
  print(plan, renderCloudPlan(plan), opts);
@@ -7871,6 +7957,32 @@ cloud.command("private-probe-config").description("Generate hosted-targeted priv
7871
7957
  fail(error);
7872
7958
  }
7873
7959
  });
7960
+ var cloudWorkers = cloud.command("workers").description("Inspect and run hosted worker entrypoints");
7961
+ cloudWorkers.command("preflight").description("Check one hosted worker entrypoint without starting work").requiredOption("--role <role>", "scheduler, public-probe, reporter, or migration").option("--healthcheck", "exit non-zero when hosted mode, component, or workspace env is invalid").option("-j, --json", "print JSON").action((opts) => {
7962
+ try {
7963
+ const preflight = buildHostedWorkerPreflight(parseWorkerRole(opts.role));
7964
+ print(preflight, renderHostedWorkerPreflight(preflight), opts);
7965
+ if (opts.healthcheck && !hostedWorkerEnvironmentOk(preflight))
7966
+ process.exit(1);
7967
+ } catch (error) {
7968
+ fail(error);
7969
+ }
7970
+ });
7971
+ cloudWorkers.command("run").description("Run one hosted worker entrypoint; fails closed until cloud prerequisites exist").requiredOption("--role <role>", "scheduler, public-probe, reporter, or migration").option("-j, --json", "print JSON").action((opts) => {
7972
+ try {
7973
+ const preflight = buildHostedWorkerPreflight(parseWorkerRole(opts.role));
7974
+ const error = `hosted ${preflight.role} worker runtime is blocked until cloud prerequisites exist`;
7975
+ if (wantsJson(opts)) {
7976
+ console.log(JSON.stringify({ ok: false, error, preflight }, null, 2));
7977
+ } else {
7978
+ console.error(source_default.red(sanitizeTerminal(error)));
7979
+ console.error(renderHostedWorkerPreflight(preflight));
7980
+ }
7981
+ process.exit(1);
7982
+ } catch (error) {
7983
+ fail(error);
7984
+ }
7985
+ });
7874
7986
  var cloudPublicChecks = cloud.command("public-checks").description("Run hosted public HTTP/TCP checks against the configured hosted store");
7875
7987
  cloudPublicChecks.command("run-due").description("Run due hosted public HTTP/TCP checks for one workspace").option("--workspace-id <id>", "workspace id; defaults to HASNA_UPTIME_WORKSPACE_ID").option("--now <iso>", "due timestamp", new Date().toISOString()).option("--hosted-sqlite-db <path>", "hosted SQLite path on cloud-mounted storage").option("--allow-hosted-local-store", "allow hosted mode to use local SQLite as an explicit fallback").option("-j, --json", "print JSON").action(async (opts) => {
7876
7988
  try {
@@ -7887,6 +7999,37 @@ cloudPublicChecks.command("run-due").description("Run due hosted public HTTP/TCP
7887
7999
  fail(error);
7888
8000
  }
7889
8001
  });
8002
+ cloudPublicChecks.command("worker").description("Run a bounded EFS SQLite bridge loop around hosted public checks").option("--workspace-id <id>", "workspace id; defaults to HASNA_UPTIME_WORKSPACE_ID").option("--interval-ms <ms>", "sleep interval between iterations", parseInteger, 30000).option("--max-runtime-ms <ms>", "stop after this many milliseconds", parseInteger).option("--max-iterations <n>", "stop after this many iterations", parseInteger).option("--hosted-sqlite-db <path>", "hosted SQLite path on cloud-mounted storage").option("--allow-hosted-local-store", "allow hosted mode to use local SQLite as an explicit fallback").option("-j, --json", "print JSON").action(async (opts) => {
8003
+ const abortController = new AbortController;
8004
+ const onSignal = () => abortController.abort();
8005
+ process.once("SIGINT", onSignal);
8006
+ process.once("SIGTERM", onSignal);
8007
+ try {
8008
+ const svc = hostedService({
8009
+ hostedSqliteDb: opts.hostedSqliteDb,
8010
+ allowHostedLocalStore: opts.allowHostedLocalStore
8011
+ });
8012
+ const workspaceId = opts.workspaceId || process.env.HASNA_UPTIME_WORKSPACE_ID;
8013
+ const summary = await runHostedPublicChecksWorker({
8014
+ runner: svc,
8015
+ workspaceId,
8016
+ intervalMs: opts.intervalMs,
8017
+ maxRuntimeMs: opts.maxRuntimeMs,
8018
+ maxIterations: opts.maxIterations,
8019
+ signal: abortController.signal,
8020
+ onIteration: wantsJson(opts) ? undefined : (iteration) => {
8021
+ console.log(`iteration ${iteration.iteration}: checked ${iteration.checked}`);
8022
+ }
8023
+ });
8024
+ svc.close();
8025
+ print(summary, renderHostedPublicChecksWorkerSummary(summary), opts);
8026
+ } catch (error) {
8027
+ fail(error);
8028
+ } finally {
8029
+ process.removeListener("SIGINT", onSignal);
8030
+ process.removeListener("SIGTERM", onSignal);
8031
+ }
8032
+ });
7890
8033
  program2.command("results").description("List recent check results").option("--monitor <id>", "filter by monitor id").option("--limit <n>", "max rows", parseInteger, 20).option("-j, --json", "print JSON").action((opts) => {
7891
8034
  try {
7892
8035
  const svc = service();
@@ -8248,6 +8391,7 @@ function renderCloudPlan(plan) {
8248
8391
  `host: ${plan.hostname}`,
8249
8392
  `cluster: ${plan.resources.ecsCluster}`,
8250
8393
  `image: ${plan.image.uri}`,
8394
+ ...plan.image.expectedIntegrity ? [`package integrity: ${plan.image.expectedIntegrity}`] : [],
8251
8395
  `image builder: ${plan.resources.imageBuilder}`,
8252
8396
  `dockerfile: ${plan.image.dockerfile}`,
8253
8397
  `infra: ${plan.infra.path}`,
@@ -8255,6 +8399,7 @@ function renderCloudPlan(plan) {
8255
8399
  `efs: ${plan.resources.efsFileSystem}`,
8256
8400
  `hosted sqlite: ${plan.resources.hostedSqliteDbPath}`,
8257
8401
  `protected access: ${plan.resources.protectedAccessMode} ${plan.resources.protectedAccessUrl}`,
8402
+ ...plan.resources.cloudfrontOrigin ? [`cloudfront origin: ${plan.resources.cloudfrontOrigin.protocolPolicy} ${plan.resources.cloudfrontOrigin.domainName}`] : [],
8258
8403
  `services: ${plan.resources.services.map((service2) => `${service2.name}:${service2.desiredCount}/${service2.targetDesiredCount}`).join(", ")}`,
8259
8404
  `evidence bucket: ${plan.resources.evidenceBucket}`,
8260
8405
  `blockers: ${plan.blockers.length}`,
@@ -8277,6 +8422,101 @@ function renderPrivateProbeConfig(config) {
8277
8422
  ].join(`
8278
8423
  `);
8279
8424
  }
8425
+ function renderHostedPublicChecksWorkerSummary(summary) {
8426
+ return [
8427
+ "hosted public checks worker",
8428
+ `status: ${summary.status}`,
8429
+ `workspace: ${summary.workspaceId ?? "<unset>"}`,
8430
+ `iterations: ${summary.iterations}`,
8431
+ `checked: ${summary.checked}`,
8432
+ `started: ${summary.startedAt}`,
8433
+ `finished: ${summary.finishedAt}`
8434
+ ].join(`
8435
+ `);
8436
+ }
8437
+ function parseWorkerRole(value) {
8438
+ if (value === "scheduler" || value === "public-probe" || value === "reporter" || value === "migration")
8439
+ return value;
8440
+ throw new Error(`Unknown hosted worker role: ${value}`);
8441
+ }
8442
+ function buildHostedWorkerPreflight(role) {
8443
+ const mode = process.env.HASNA_UPTIME_MODE?.trim() || "";
8444
+ const component = process.env.HASNA_UPTIME_COMPONENT?.trim() || "";
8445
+ const workspaceId = process.env.HASNA_UPTIME_WORKSPACE_ID?.trim() || "";
8446
+ const checks = [
8447
+ { name: "hosted-mode", ok: mode === "hosted", detail: mode || "<unset>" },
8448
+ { name: "component", ok: !component || component === role, detail: component || "<unset>" },
8449
+ { name: "workspace", ok: Boolean(workspaceId), detail: workspaceId || "<unset>" },
8450
+ { name: "postgres-adapter", ok: false, detail: "not implemented" },
8451
+ { name: "cloud-worker-leases", ok: false, detail: "not implemented" }
8452
+ ];
8453
+ if (role === "reporter") {
8454
+ checks.push({ name: "cloud-channel-refs", ok: false, detail: "not implemented" });
8455
+ }
8456
+ if (role === "public-probe") {
8457
+ checks.push({ name: "public-probe-job-claims", ok: false, detail: "not implemented" });
8458
+ }
8459
+ if (role === "migration") {
8460
+ checks.push({ name: "cloud-migration-plan", ok: false, detail: "not implemented" });
8461
+ }
8462
+ const blockers = checks.filter((check) => !check.ok).map((check) => `${check.name}: ${check.detail}`);
8463
+ return {
8464
+ kind: "open-uptime.hosted-worker-preflight",
8465
+ role,
8466
+ status: "blocked",
8467
+ canStart: false,
8468
+ mode: mode || "<unset>",
8469
+ component: component || "<unset>",
8470
+ workspaceId: workspaceId || null,
8471
+ blockers,
8472
+ checks,
8473
+ nextActions: hostedWorkerNextActions(role)
8474
+ };
8475
+ }
8476
+ function hostedWorkerNextActions(role) {
8477
+ const shared = [
8478
+ "Keep the ECS service desired count at 0 until this preflight reports canStart=true.",
8479
+ "Move authoritative hosted state from the EFS SQLite bridge to the cloud store with transactional leases."
8480
+ ];
8481
+ if (role === "scheduler") {
8482
+ return [
8483
+ ...shared,
8484
+ "Implement deterministic check_jobs creation with a scheduler lease and duplicate-slot protection."
8485
+ ];
8486
+ }
8487
+ if (role === "public-probe") {
8488
+ return [
8489
+ ...shared,
8490
+ "Implement cloud job claiming, fencing tokens, target-policy decision logs, and result ingestion for public HTTP/TCP checks."
8491
+ ];
8492
+ }
8493
+ if (role === "reporter") {
8494
+ return [
8495
+ ...shared,
8496
+ "Implement workspace-authorized report channel refs, idempotent delivery keys, retry/backoff, and delivery alarms."
8497
+ ];
8498
+ }
8499
+ return [
8500
+ ...shared,
8501
+ "Implement reviewed cloud schema migrations with dry-run counts, backup evidence, and rollback instructions."
8502
+ ];
8503
+ }
8504
+ function renderHostedWorkerPreflight(preflight) {
8505
+ return [
8506
+ `${preflight.role} hosted worker preflight`,
8507
+ `status: ${preflight.status}`,
8508
+ `can start: ${preflight.canStart}`,
8509
+ `mode: ${sanitizeField(preflight.mode)}`,
8510
+ `component: ${sanitizeField(preflight.component)}`,
8511
+ `workspace: ${sanitizeField(preflight.workspaceId ?? "<unset>")}`,
8512
+ `blockers: ${preflight.blockers.length}`,
8513
+ ...preflight.blockers.map((blocker) => `- ${sanitizeField(blocker)}`)
8514
+ ].join(`
8515
+ `);
8516
+ }
8517
+ function hostedWorkerEnvironmentOk(preflight) {
8518
+ return preflight.checks.filter((check) => check.name === "hosted-mode" || check.name === "component" || check.name === "workspace").every((check) => check.ok);
8519
+ }
8280
8520
  function renderDeliveries(deliveries) {
8281
8521
  if (deliveries.length === 0)
8282
8522
  return "No report deliveries requested";
@@ -11,7 +11,10 @@ export interface AwsDeploymentPlanOptions {
11
11
  evidenceBucket?: string;
12
12
  hostedSqliteDbPath?: string;
13
13
  runtimePackageVersion?: string;
14
+ runtimePackageIntegrity?: string;
14
15
  protectedAccessMode?: "cloudfront_default_domain" | "alb_https_cert";
16
+ cloudfrontOriginProtocolPolicy?: "http-only" | "https-only";
17
+ cloudfrontOriginDomainName?: string;
15
18
  /** @deprecated Postgres is target-state only until the async adapter is implemented. */
16
19
  rdsInstanceId?: string;
17
20
  /** @deprecated Postgres is target-state only until the async adapter is implemented. */
@@ -49,6 +52,13 @@ export interface AwsDeploymentPlan {
49
52
  protectedAccessMode: "cloudfront_default_domain" | "alb_https_cert";
50
53
  edgeDistribution?: string;
51
54
  protectedAccessUrl: string;
55
+ cloudfrontOrigin?: {
56
+ protocolPolicy: "http-only" | "https-only";
57
+ domainName: string;
58
+ requiresMatchingCertificate: boolean;
59
+ liveTrafficApproved: boolean;
60
+ risk?: string;
61
+ };
52
62
  originVerification: {
53
63
  mode: "cloudfront_origin_header" | "alb_tls";
54
64
  requiredBeforeScaleUp: boolean;
@@ -66,6 +76,7 @@ export interface AwsDeploymentPlan {
66
76
  repository: string;
67
77
  uri: string;
68
78
  dockerfile: string;
79
+ expectedIntegrity?: string;
69
80
  buildCommand: string;
70
81
  pushCommands: string[];
71
82
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-plan.d.ts","sourceRoot":"","sources":["../src/cloud-plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloud-plan.d.ts","sourceRoot":"","sources":["../src/cloud-plan.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ var DEFAULT_WORKSPACE_ID = "workspace-id";
9
9
  var DEFAULT_VPC_ID = "vpc-xxxxxxxx";
10
10
  var DEFAULT_HOSTED_SQLITE_DB = "/data/uptime/uptime.db";
11
11
  var DEFAULT_PROTECTED_ACCESS_MODE = "cloudfront_default_domain";
12
+ var DEFAULT_CLOUDFRONT_ORIGIN_PROTOCOL_POLICY = "http-only";
12
13
  function buildAwsDeploymentPlan(options = {}) {
13
14
  const region = clean(options.region, DEFAULT_REGION);
14
15
  const stage = clean(options.stage, DEFAULT_STAGE);
@@ -21,8 +22,11 @@ function buildAwsDeploymentPlan(options = {}) {
21
22
  const image = clean(options.image, `${imageRepositoryUri}@sha256:<image-digest>`);
22
23
  const evidenceBucket = clean(options.evidenceBucket, `hasna-${stage}-${prefix}-evidence`);
23
24
  const hostedSqliteDbPath = clean(options.hostedSqliteDbPath, DEFAULT_HOSTED_SQLITE_DB);
24
- const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.24");
25
+ const runtimePackageVersion = clean(options.runtimePackageVersion, "0.1.26");
26
+ const runtimePackageIntegrity = options.runtimePackageIntegrity?.trim() || undefined;
25
27
  const protectedAccessMode = options.protectedAccessMode ?? DEFAULT_PROTECTED_ACCESS_MODE;
28
+ const cloudfrontOriginProtocolPolicy = options.cloudfrontOriginProtocolPolicy ?? DEFAULT_CLOUDFRONT_ORIGIN_PROTOCOL_POLICY;
29
+ const cloudfrontOriginDomainName = clean(options.cloudfrontOriginDomainName, "<alb-dns-name>");
26
30
  const protectedAccessUrl = protectedAccessMode === "cloudfront_default_domain" ? "https://<cloudfront-domain>" : `https://${hostname}`;
27
31
  const cluster = `${prefix}-${stage}`;
28
32
  const secrets = {
@@ -89,6 +93,13 @@ function buildAwsDeploymentPlan(options = {}) {
89
93
  protectedAccessMode,
90
94
  edgeDistribution: protectedAccessMode === "cloudfront_default_domain" ? `${prefix}-${stage}-edge` : undefined,
91
95
  protectedAccessUrl,
96
+ cloudfrontOrigin: protectedAccessMode === "cloudfront_default_domain" ? {
97
+ protocolPolicy: cloudfrontOriginProtocolPolicy,
98
+ domainName: cloudfrontOriginProtocolPolicy === "https-only" ? cloudfrontOriginDomainName : "<alb-dns-name>",
99
+ requiresMatchingCertificate: cloudfrontOriginProtocolPolicy === "https-only",
100
+ liveTrafficApproved: false,
101
+ risk: cloudfrontOriginProtocolPolicy === "http-only" ? "Temporary HTTP-origin bridge: web scale-up requires allow_cloudfront_http_origin_live_traffic=true for bounded smokes, or switch to https-only with cloudfront_origin_domain_name plus certificate_arn." : "CloudFront HTTPS-origin mode requires the origin hostname to resolve to the ALB and match certificate_arn."
102
+ } : undefined,
92
103
  originVerification: protectedAccessMode === "cloudfront_default_domain" ? {
93
104
  mode: "cloudfront_origin_header",
94
105
  requiredBeforeScaleUp: true,
@@ -121,6 +132,7 @@ function buildAwsDeploymentPlan(options = {}) {
121
132
  repository: ecrRepository,
122
133
  uri: image,
123
134
  dockerfile: "Dockerfile.package",
135
+ expectedIntegrity: runtimePackageIntegrity,
124
136
  buildCommand: `BLOCKED: after infra approval, AWS CodeBuild builds Dockerfile.package from @hasna/uptime@${runtimePackageVersion} into ${imageRepositoryUri}`,
125
137
  pushCommands: [
126
138
  `BLOCKED: start ${prefix}-${stage}-image-builder only through the approved deploy pipeline after @hasna/uptime@${runtimePackageVersion} is published`,
@@ -147,16 +159,16 @@ function buildAwsDeploymentPlan(options = {}) {
147
159
  `Infra PR must declare CodeBuild image builder ${prefix}-${stage}-image-builder for @hasna/uptime@${runtimePackageVersion}.`,
148
160
  `Infra PR must declare hardened S3 evidence bucket ${evidenceBucket} with KMS, versioning, lifecycle, and public access block.`,
149
161
  `Infra PR must declare encrypted EFS ${prefix}-${stage}-data with access point, mount targets, and AWS Backup plan.`,
150
- protectedAccessMode === "cloudfront_default_domain" ? "Infra PR must declare CloudFront default-domain HTTPS edge, ALB HTTP listener restricted to CloudFront origin-facing ranges, CloudFront-only origin verification header binding, ECS/Fargate cluster, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs." : `Infra PR must declare ECS/Fargate cluster ${cluster}, ALB HTTPS listener, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs.`,
162
+ protectedAccessMode === "cloudfront_default_domain" ? "Infra PR must declare CloudFront default-domain HTTPS edge, ALB origin listener restricted to CloudFront origin-facing ranges, CloudFront-only origin verification header binding, ECS/Fargate cluster, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs. Token-bearing live traffic must use cloudfront_origin_protocol_policy=https-only with a matching origin hostname/certificate, or carry explicit allow_cloudfront_http_origin_live_traffic=true bounded-smoke risk acceptance." : `Infra PR must declare ECS/Fargate cluster ${cluster}, ALB HTTPS listener, target groups, security groups, IAM roles, CloudWatch log groups, and Secrets Manager refs.`,
151
163
  "Only apply the infra plan from the approved infrastructure repository after review evidence is attached."
152
164
  ],
153
165
  deploy: [
154
166
  "Build and publish the image only after the Dockerfile/container target is reviewed.",
155
- `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion} and record the pushed image digest.`,
167
+ runtimePackageIntegrity ? `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion}; it must verify npm dist.integrity ${runtimePackageIntegrity} before extracting the package, then record the pushed image digest.` : `Start the AWS image builder for @hasna/uptime@${runtimePackageVersion}; set runtime_package_integrity from npm dist.integrity before live use, then record the pushed image digest.`,
156
168
  "For the EFS SQLite bridge, do not run migration, scheduler, public-probe, or reporter tasks; keep them at desired count 0 until Postgres and cloud leases exist.",
157
169
  `Register task definitions for ${services.map((service) => service.name).join(", ")} using valueFrom secrets.`,
158
170
  `Update ECS services in cluster ${cluster} one component at a time through the approved deploy pipeline.`,
159
- protectedAccessMode === "cloudfront_default_domain" ? "Use the CloudFront default HTTPS domain with origin verification header binding for first protected access; add custom DNS/certificate only after edge ownership is approved." : `Create Route53/edge record for ${hostname} only after ALB health checks pass and auth denial smokes succeed.`
171
+ protectedAccessMode === "cloudfront_default_domain" ? "Use the CloudFront default HTTPS domain with origin verification header binding for first protected access; before token-bearing live traffic, switch the origin to https-only with a matching origin hostname/certificate or set allow_cloudfront_http_origin_live_traffic=true only for a bounded approved smoke." : `Create Route53/edge record for ${hostname} only after ALB health checks pass and auth denial smokes succeed.`
160
172
  ],
161
173
  rollback: [
162
174
  "Keep previous task definition ARNs before each service update.",
@@ -173,6 +185,8 @@ function buildAwsDeploymentPlan(options = {}) {
173
185
  blockers: [
174
186
  "The infrastructure owner repository was not found in this workspace.",
175
187
  protectedAccessMode === "cloudfront_default_domain" ? "CloudFront origin verification header binding must be enabled and direct-origin denial must be proven before web desired count is raised above 0." : "ALB HTTPS ingress policy and auth-denial smokes must be proven before web desired count is raised above 0.",
188
+ ...protectedAccessMode === "cloudfront_default_domain" && cloudfrontOriginProtocolPolicy === "http-only" ? ["CloudFront-to-ALB origin transport is still http-only; web scale-up now requires explicit allow_cloudfront_http_origin_live_traffic=true risk acceptance, and token-bearing live traffic should use https-only origin mode."] : [],
189
+ ...protectedAccessMode === "cloudfront_default_domain" && cloudfrontOriginProtocolPolicy === "https-only" && cloudfrontOriginDomainName === "<alb-dns-name>" ? ["CloudFront https-only origin mode needs cloudfront_origin_domain_name that resolves to the ALB and matches certificate_arn."] : [],
176
190
  "The EFS SQLite bridge is single-writer only: web target desired count is 1 and scheduler/public-probe/reporter targets remain 0 until Postgres and cloud leases exist.",
177
191
  "Hosted production auth/RBAC must replace broad static hosted-token operation before exposure.",
178
192
  "Public probe execution still needs cloud check-job leases wired to runHostedHttpCheck and live policy-decision log evidence.",
@@ -181,8 +195,9 @@ function buildAwsDeploymentPlan(options = {}) {
181
195
  requiredEvidence: [
182
196
  "Infrastructure PR/synth/plan from the approved infra repository.",
183
197
  "CodeBuild image-builder run, container smoke, and immutable image digest.",
198
+ "Published package dist.integrity pinned in the private infra root or an explicit not-live exception.",
184
199
  "ECS task definitions using secrets.valueFrom only.",
185
- "CloudFront-default-domain origin-header config or ALB TLS auth-denial smokes, direct-origin denial evidence, and web alarm checks.",
200
+ "CloudFront-default-domain origin-header config, origin transport decision, direct-origin denial evidence, auth-denial smokes, and web alarm checks.",
186
201
  "Single-writer ECS evidence: one web task maximum and no scheduler/public-probe/reporter EFS mounts.",
187
202
  "EFS encryption, access point, mount-target, AWS Backup, and restore-drill evidence.",
188
203
  "S3 bucket KMS, versioning, lifecycle, and public-access-block evidence.",
@@ -195,11 +210,13 @@ function buildAwsDeploymentPlan(options = {}) {
195
210
  notes: [
196
211
  "This plan generator does not call AWS.",
197
212
  "Blocked plan output intentionally avoids copy-pastable AWS mutation commands.",
198
- "Default protected access uses CloudFront's HTTPS default domain so first deploy is not blocked on custom DNS or ACM.",
213
+ "Default protected access uses CloudFront's HTTPS default domain so first zero-count deploy is not blocked on custom DNS or ACM.",
199
214
  "CloudFront default-domain mode still requires origin verification header binding before live scale-up; the header value is sensitive state/config material, not public documentation.",
215
+ "CloudFront HTTPS-origin mode requires a dedicated origin DNS hostname and matching ACM certificate; do not assume the ALB DNS name can satisfy TLS verification.",
200
216
  "Hosted runtime uses explicit EFS-backed SQLite at HASNA_UPTIME_HOSTED_SQLITE_DB until the async Postgres adapter exists.",
201
217
  "Do not set HASNA_UPTIME_DATABASE_URL for hosted tasks until the Postgres adapter is implemented.",
202
218
  "Secrets are represented as secret names/refs and must be injected with valueFrom.",
219
+ "Set runtime_package_integrity in the approved infra root after publish so the AWS image builder verifies the npm tarball before ECR build.",
203
220
  "Actual deploy belongs in the deploy_release_operate_final goal node after infra review."
204
221
  ]
205
222
  }
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { applyImport, previewImport, rollbackImport } from "./imports.js";
6
6
  export { buildUptimeReport, sendUptimeReport } from "./report.js";
7
7
  export { generateProbeKeyPair, probePublicKeyFingerprint, probeResultSigningPayload, signProbeResult, verifyProbeResultSignature } from "./probes.js";
8
8
  export { buildAwsDeploymentPlan, buildPrivateProbeCloudConfig, renderPrivateProbeEnv } from "./cloud-plan.js";
9
+ export { runHostedPublicChecksWorker } from "./workers.js";
9
10
  export { uptimeHome, uptimeDbPath, uptimeHostedFallbackDbPath, ensureUptimeHome } from "./paths.js";
10
11
  export type { UptimeBackup, UptimeBackupCheck, UptimeRuntimeMode, UptimeStoreOptions, MonitorProvenance, SaveImportBatchInput, StoredImportBatch, UpsertMonitorProvenanceInput, } from "./store.js";
11
12
  export type { BrowserPageRunner, BrowserPageRunnerResult, FetchLike, HostedDnsResolver, HostedHttpCheckOptions, HostedHttpRequestContext, HostedHttpRequestLike, HostedHttpResponse, HostedTcpCheckOptions, MonitorCheckOptions, } from "./checks.js";
@@ -13,5 +14,6 @@ export type { ImportAction, ImportApplyItem, ImportApplyResult, ImportCandidate,
13
14
  export type { BrowserFailedRequest, BrowserPageEvidence, AuditEvent, CheckAttemptResult, CheckEvidence, CheckResult, CheckStatus, CreateMonitorKind, CreateMonitorInput, CreateReportScheduleInput, ImportedMonitorInput, ImportedUpdateMonitorInput, EvidenceArtifact, HttpTargetPolicyDecision, HttpTargetPolicyEvidence, Incident, IncidentStatus, ListAuditEventsOptions, ListReportRunsOptions, ListResultsOptions, Monitor, MonitorKind, MonitorStatus, MonitorSummary, ProbeCheckJob, ProbeCheckJobStatus, ProbeIdentity, ProbeResultSubmission, ProbeSubmissionReceipt, RecordAuditEventInput, ReportDeliveryChannel, ReportDeliveryRecord, ReportEmailChannelConfig, ReportLogsChannelConfig, ReportRun, ReportRunStatus, ReportSchedule, ReportScheduleChannels, ReportScheduleStatus, ReportSmsChannelConfig, SchedulerHandle, UpdateMonitorInput, UpdateReportScheduleInput, UptimeSummary, } from "./types.js";
14
15
  export type { ProbeKeyPair, ProbeSigningInput } from "./probes.js";
15
16
  export type { AwsDeploymentPlan, AwsDeploymentPlanOptions, AwsServicePlan, PrivateProbeCloudConfig, PrivateProbeCloudConfigOptions, } from "./cloud-plan.js";
17
+ export type { HostedPublicCheckRunner, HostedPublicChecksWorkerIteration, HostedPublicChecksWorkerOptions, HostedPublicChecksWorkerSummary, } from "./workers.js";
16
18
  export type { BuildUptimeReportOptions, SendUptimeReportOptions, UptimeEmailReportTarget, UptimeLogsReportTarget, UptimeReport, UptimeReportDelivery, UptimeSmsReportTarget, } from "./report.js";
17
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,iCAAiC,EACjC,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACtJ,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC9G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpG,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,SAAS,EACT,iBAAiB,EACjB,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,GACb,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,gBAAgB,EAChB,wBAAwB,EACxB,wBAAwB,EACxB,QAAQ,EACR,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,OAAO,EACP,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,SAAS,EACT,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,EACd,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}