@pithy-sh/core 0.1.2 → 0.1.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/address/address.d.ts +57 -0
- package/dist/address/address.d.ts.map +1 -0
- package/dist/address/address.js +71 -0
- package/dist/audit/auditEvent.d.ts +89 -0
- package/dist/audit/auditEvent.d.ts.map +1 -0
- package/dist/audit/auditEvent.js +69 -0
- package/dist/audit/recorder.d.ts +18 -0
- package/dist/audit/recorder.d.ts.map +1 -0
- package/dist/audit/recorder.js +8 -0
- package/dist/capability/bindings.d.ts +62 -0
- package/dist/capability/bindings.d.ts.map +1 -0
- package/dist/capability/bindings.js +97 -0
- package/dist/capability/capability.d.ts +501 -0
- package/dist/capability/capability.d.ts.map +1 -0
- package/dist/capability/capability.js +16 -0
- package/dist/capability/client.d.ts +70 -0
- package/dist/capability/client.d.ts.map +1 -0
- package/dist/capability/client.js +86 -0
- package/dist/capability/compose.d.ts +25 -0
- package/dist/capability/compose.d.ts.map +1 -0
- package/dist/capability/compose.js +54 -0
- package/dist/capability/composition.d.ts +26 -0
- package/dist/capability/composition.d.ts.map +1 -0
- package/dist/capability/composition.js +84 -0
- package/dist/capability/config.d.ts +7 -0
- package/dist/capability/config.d.ts.map +1 -0
- package/dist/capability/config.js +14 -0
- package/dist/capability/devSecret.d.ts +32 -0
- package/dist/capability/devSecret.d.ts.map +1 -0
- package/dist/capability/devSecret.js +28 -0
- package/dist/capability/manifest.d.ts +298 -0
- package/dist/capability/manifest.d.ts.map +1 -0
- package/dist/capability/manifest.js +356 -0
- package/dist/capability/secretOrigin.d.ts +121 -0
- package/dist/capability/secretOrigin.d.ts.map +1 -0
- package/dist/capability/secretOrigin.js +134 -0
- package/dist/capability/settings.d.ts +119 -0
- package/dist/capability/settings.d.ts.map +1 -0
- package/dist/capability/settings.js +66 -0
- package/dist/capability/validateBindings.d.ts +4 -0
- package/dist/capability/validateBindings.d.ts.map +1 -0
- package/dist/capability/validateBindings.js +34 -0
- package/dist/capability/vanishingKey.d.ts +31 -0
- package/dist/capability/vanishingKey.d.ts.map +1 -0
- package/dist/capability/vanishingKey.js +79 -0
- package/dist/controlPlane/audit/actions.d.ts +70 -0
- package/dist/controlPlane/audit/actions.d.ts.map +1 -0
- package/dist/controlPlane/audit/actions.js +76 -0
- package/dist/controlPlane/capability.d.ts +47 -0
- package/dist/controlPlane/capability.d.ts.map +1 -0
- package/dist/controlPlane/capability.js +138 -0
- package/dist/controlPlane/config/config.d.ts +19 -0
- package/dist/controlPlane/config/config.d.ts.map +1 -0
- package/dist/controlPlane/config/config.js +85 -0
- package/dist/controlPlane/context.d.ts +27 -0
- package/dist/controlPlane/context.d.ts.map +1 -0
- package/dist/controlPlane/context.js +29 -0
- package/dist/controlPlane/data/connection.d.ts +79 -0
- package/dist/controlPlane/data/connection.d.ts.map +1 -0
- package/dist/controlPlane/data/connection.js +55 -0
- package/dist/controlPlane/data/keyLifecycle.d.ts +62 -0
- package/dist/controlPlane/data/keyLifecycle.d.ts.map +1 -0
- package/dist/controlPlane/data/keyLifecycle.js +126 -0
- package/dist/controlPlane/data/replay.d.ts +18 -0
- package/dist/controlPlane/data/replay.d.ts.map +1 -0
- package/dist/controlPlane/data/replay.js +20 -0
- package/dist/controlPlane/data/tables.d.ts +34 -0
- package/dist/controlPlane/data/tables.d.ts.map +1 -0
- package/dist/controlPlane/data/tables.js +37 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts +313 -0
- package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -0
- package/dist/controlPlane/discovery/adminRoute.js +136 -0
- package/dist/controlPlane/discovery/configuration.d.ts +108 -0
- package/dist/controlPlane/discovery/configuration.d.ts.map +1 -0
- package/dist/controlPlane/discovery/configuration.js +187 -0
- package/dist/controlPlane/discovery/drift.d.ts +64 -0
- package/dist/controlPlane/discovery/drift.d.ts.map +1 -0
- package/dist/controlPlane/discovery/drift.js +58 -0
- package/dist/controlPlane/discovery/health.d.ts +99 -0
- package/dist/controlPlane/discovery/health.d.ts.map +1 -0
- package/dist/controlPlane/discovery/health.js +151 -0
- package/dist/controlPlane/discovery/healthSummary.d.ts +236 -0
- package/dist/controlPlane/discovery/healthSummary.d.ts.map +1 -0
- package/dist/controlPlane/discovery/healthSummary.js +278 -0
- package/dist/controlPlane/error/errors.d.ts +67 -0
- package/dist/controlPlane/error/errors.d.ts.map +1 -0
- package/dist/controlPlane/error/errors.js +79 -0
- package/dist/controlPlane/http/cors.d.ts +112 -0
- package/dist/controlPlane/http/cors.d.ts.map +1 -0
- package/dist/controlPlane/http/cors.js +192 -0
- package/dist/controlPlane/http/guard.d.ts +77 -0
- package/dist/controlPlane/http/guard.d.ts.map +1 -0
- package/dist/controlPlane/http/guard.js +142 -0
- package/dist/controlPlane/http/handlers.d.ts +179 -0
- package/dist/controlPlane/http/handlers.d.ts.map +1 -0
- package/dist/controlPlane/http/handlers.js +245 -0
- package/dist/controlPlane/http/responses.d.ts +99 -0
- package/dist/controlPlane/http/responses.d.ts.map +1 -0
- package/dist/controlPlane/http/responses.js +63 -0
- package/dist/controlPlane/http/routes.d.ts +40 -0
- package/dist/controlPlane/http/routes.d.ts.map +1 -0
- package/dist/controlPlane/http/routes.js +83 -0
- package/dist/controlPlane/http/schemas.d.ts +39 -0
- package/dist/controlPlane/http/schemas.d.ts.map +1 -0
- package/dist/controlPlane/http/schemas.js +31 -0
- package/dist/controlPlane/http/verify.d.ts +84 -0
- package/dist/controlPlane/http/verify.d.ts.map +1 -0
- package/dist/controlPlane/http/verify.js +65 -0
- package/dist/controlPlane/migrations/0001_init.d.ts +51 -0
- package/dist/controlPlane/migrations/0001_init.d.ts.map +1 -0
- package/dist/controlPlane/migrations/0001_init.js +65 -0
- package/dist/controlPlane/replay/d1Guard.d.ts +29 -0
- package/dist/controlPlane/replay/d1Guard.d.ts.map +1 -0
- package/dist/controlPlane/replay/d1Guard.js +39 -0
- package/dist/controlPlane/replay/guard.d.ts +52 -0
- package/dist/controlPlane/replay/guard.d.ts.map +1 -0
- package/dist/controlPlane/replay/guard.js +0 -0
- package/dist/controlPlane/replay/kvGuard.d.ts +99 -0
- package/dist/controlPlane/replay/kvGuard.d.ts.map +1 -0
- package/dist/controlPlane/replay/kvGuard.js +101 -0
- package/dist/controlPlane/scope/scope.d.ts +68 -0
- package/dist/controlPlane/scope/scope.d.ts.map +1 -0
- package/dist/controlPlane/scope/scope.js +63 -0
- package/dist/controlPlane/token/base64url.d.ts +10 -0
- package/dist/controlPlane/token/base64url.d.ts.map +1 -0
- package/dist/controlPlane/token/base64url.js +48 -0
- package/dist/controlPlane/token/claims.d.ts +83 -0
- package/dist/controlPlane/token/claims.d.ts.map +1 -0
- package/dist/controlPlane/token/claims.js +86 -0
- package/dist/controlPlane/token/digest.d.ts +44 -0
- package/dist/controlPlane/token/digest.d.ts.map +1 -0
- package/dist/controlPlane/token/digest.js +56 -0
- package/dist/controlPlane/token/jws.d.ts +52 -0
- package/dist/controlPlane/token/jws.d.ts.map +1 -0
- package/dist/controlPlane/token/jws.js +65 -0
- package/dist/controlPlane/token/mint.d.ts +56 -0
- package/dist/controlPlane/token/mint.d.ts.map +1 -0
- package/dist/controlPlane/token/mint.js +55 -0
- package/dist/controlPlane/wire.d.ts +122 -0
- package/dist/controlPlane/wire.d.ts.map +1 -0
- package/dist/controlPlane/wire.js +123 -0
- package/dist/createBackend.d.ts +58 -0
- package/dist/createBackend.d.ts.map +1 -0
- package/dist/createBackend.js +170 -0
- package/dist/createEntrypoint.d.ts +53 -0
- package/dist/createEntrypoint.d.ts.map +1 -0
- package/dist/createEntrypoint.js +52 -0
- package/dist/data/boundParameters.d.ts +83 -0
- package/dist/data/boundParameters.d.ts.map +1 -0
- package/dist/data/boundParameters.js +162 -0
- package/dist/data/codecs.d.ts +34 -0
- package/dist/data/codecs.d.ts.map +1 -0
- package/dist/data/codecs.js +135 -0
- package/dist/data/cursor.d.ts +81 -0
- package/dist/data/cursor.d.ts.map +1 -0
- package/dist/data/cursor.js +104 -0
- package/dist/data/databases.d.ts +55 -0
- package/dist/data/databases.d.ts.map +1 -0
- package/dist/data/databases.js +32 -0
- package/dist/data/db.d.ts +42 -0
- package/dist/data/db.d.ts.map +1 -0
- package/dist/data/db.js +24 -0
- package/dist/data/withD1Retry.d.ts +91 -0
- package/dist/data/withD1Retry.d.ts.map +1 -0
- package/dist/data/withD1Retry.js +152 -0
- package/dist/entitlement/entitlement.d.ts +118 -0
- package/dist/entitlement/entitlement.d.ts.map +1 -0
- package/dist/entitlement/entitlement.js +59 -0
- package/dist/entitlement/gateScan.d.ts +26 -0
- package/dist/entitlement/gateScan.d.ts.map +1 -0
- package/dist/entitlement/gateScan.js +96 -0
- package/dist/entitlement/require.d.ts +12 -0
- package/dist/entitlement/require.d.ts.map +1 -0
- package/dist/entitlement/require.js +123 -0
- package/dist/env/ambient.d.ts +52 -0
- package/dist/env/ambient.d.ts.map +1 -0
- package/dist/env/ambient.js +35 -0
- package/dist/env/ci.d.ts +36 -0
- package/dist/env/ci.d.ts.map +1 -0
- package/dist/env/ci.js +40 -0
- package/dist/env/stem.d.ts +23 -0
- package/dist/env/stem.d.ts.map +1 -0
- package/dist/env/stem.js +29 -0
- package/dist/error/cause.d.ts +143 -0
- package/dist/error/cause.d.ts.map +1 -0
- package/dist/error/cause.js +192 -0
- package/dist/error/client.d.ts +35 -0
- package/dist/error/client.d.ts.map +1 -0
- package/dist/error/client.js +38 -0
- package/dist/error/extend.d.ts +101 -0
- package/dist/error/extend.d.ts.map +1 -0
- package/dist/error/extend.js +76 -0
- package/dist/error/http.d.ts +1516 -0
- package/dist/error/http.d.ts.map +1 -0
- package/dist/error/http.js +72 -0
- package/dist/error/payload.d.ts +3034 -0
- package/dist/error/payload.d.ts.map +1 -0
- package/dist/error/payload.js +1110 -0
- package/dist/error/pithyError.d.ts +154 -0
- package/dist/error/pithyError.d.ts.map +1 -0
- package/dist/error/pithyError.js +197 -0
- package/dist/error/terminal.d.ts +28 -0
- package/dist/error/terminal.d.ts.map +1 -0
- package/dist/error/terminal.js +33 -0
- package/dist/http/authContext.d.ts +21 -0
- package/dist/http/authContext.d.ts.map +1 -0
- package/dist/http/authContext.js +23 -0
- package/dist/http/routeContract.d.ts +39 -0
- package/dist/http/routeContract.d.ts.map +1 -0
- package/dist/http/routeContract.js +82 -0
- package/dist/http/sameOrigin.d.ts +47 -0
- package/dist/http/sameOrigin.d.ts.map +1 -0
- package/dist/http/sameOrigin.js +24 -0
- package/dist/http/signedWebhook.d.ts +225 -0
- package/dist/http/signedWebhook.d.ts.map +1 -0
- package/dist/http/signedWebhook.js +247 -0
- package/dist/http/validation.d.ts +37 -0
- package/dist/http/validation.d.ts.map +1 -0
- package/dist/http/validation.js +14 -0
- package/dist/http/verification.d.ts +10 -0
- package/dist/http/verification.d.ts.map +1 -0
- package/dist/http/verification.js +17 -0
- package/dist/i18n/acceptLanguage.d.ts +22 -0
- package/dist/i18n/acceptLanguage.d.ts.map +1 -0
- package/dist/i18n/acceptLanguage.js +55 -0
- package/dist/i18n/catalog.d.ts +39 -0
- package/dist/i18n/catalog.d.ts.map +1 -0
- package/dist/i18n/catalog.js +57 -0
- package/dist/i18n/locale.d.ts +74 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +109 -0
- package/dist/i18n/localeMarker.d.ts +29 -0
- package/dist/i18n/localeMarker.d.ts.map +1 -0
- package/dist/i18n/localeMarker.js +112 -0
- package/dist/i18n/match.d.ts +26 -0
- package/dist/i18n/match.d.ts.map +1 -0
- package/dist/i18n/match.js +88 -0
- package/dist/i18n/registry.d.ts +25 -0
- package/dist/i18n/registry.d.ts.map +1 -0
- package/dist/i18n/registry.js +51 -0
- package/dist/i18n/translator.d.ts +107 -0
- package/dist/i18n/translator.d.ts.map +1 -0
- package/dist/i18n/translator.js +80 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/kv/kv.d.ts +166 -0
- package/dist/kv/kv.d.ts.map +1 -0
- package/dist/kv/kv.js +261 -0
- package/dist/kv/namespaces.d.ts +58 -0
- package/dist/kv/namespaces.d.ts.map +1 -0
- package/dist/kv/namespaces.js +40 -0
- package/dist/logger/local.d.ts +41 -0
- package/dist/logger/local.d.ts.map +1 -0
- package/dist/logger/local.js +45 -0
- package/dist/logger/logger.d.ts +56 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +83 -0
- package/dist/logger/record.d.ts +919 -0
- package/dist/logger/record.d.ts.map +1 -0
- package/dist/logger/record.js +79 -0
- package/dist/logger/worker.d.ts +74 -0
- package/dist/logger/worker.d.ts.map +1 -0
- package/dist/logger/worker.js +54 -0
- package/dist/migrations/batch.d.ts +10 -0
- package/dist/migrations/batch.d.ts.map +1 -0
- package/dist/migrations/batch.js +193 -0
- package/dist/migrations/bookkeeping.d.ts +24 -0
- package/dist/migrations/bookkeeping.d.ts.map +1 -0
- package/dist/migrations/bookkeeping.js +57 -0
- package/dist/migrations/owner.d.ts +41 -0
- package/dist/migrations/owner.d.ts.map +1 -0
- package/dist/migrations/owner.js +105 -0
- package/dist/migrations/registry.d.ts +36 -0
- package/dist/migrations/registry.d.ts.map +1 -0
- package/dist/migrations/registry.js +70 -0
- package/dist/migrations/runner.d.ts +67 -0
- package/dist/migrations/runner.d.ts.map +1 -0
- package/dist/migrations/runner.js +218 -0
- package/dist/naming/domains.d.ts +108 -0
- package/dist/naming/domains.d.ts.map +1 -0
- package/dist/naming/domains.js +156 -0
- package/dist/naming/environment.d.ts +118 -0
- package/dist/naming/environment.d.ts.map +1 -0
- package/dist/naming/environment.js +190 -0
- package/dist/naming/feature.d.ts +68 -0
- package/dist/naming/feature.d.ts.map +1 -0
- package/dist/naming/feature.js +106 -0
- package/dist/naming/limits.d.ts +229 -0
- package/dist/naming/limits.d.ts.map +1 -0
- package/dist/naming/limits.js +187 -0
- package/dist/naming/provisionScope.d.ts +91 -0
- package/dist/naming/provisionScope.d.ts.map +1 -0
- package/dist/naming/provisionScope.js +65 -0
- package/dist/naming/resource.d.ts +169 -0
- package/dist/naming/resource.d.ts.map +1 -0
- package/dist/naming/resource.js +220 -0
- package/dist/naming/resourceNames.d.ts +83 -0
- package/dist/naming/resourceNames.d.ts.map +1 -0
- package/dist/naming/resourceNames.js +102 -0
- package/dist/naming/segment.d.ts +29 -0
- package/dist/naming/segment.d.ts.map +1 -0
- package/dist/naming/segment.js +31 -0
- package/dist/projection/asRead.d.ts +72 -0
- package/dist/projection/asRead.d.ts.map +1 -0
- package/dist/projection/asRead.js +169 -0
- package/dist/projection/published.d.ts +95 -0
- package/dist/projection/published.d.ts.map +1 -0
- package/dist/projection/published.js +114 -0
- package/dist/schema/describedness.d.ts +55 -0
- package/dist/schema/describedness.d.ts.map +1 -0
- package/dist/schema/describedness.js +180 -0
- package/dist/seed/compose.d.ts +41 -0
- package/dist/seed/compose.d.ts.map +1 -0
- package/dist/seed/compose.js +51 -0
- package/dist/seed/devLogin.d.ts +50 -0
- package/dist/seed/devLogin.d.ts.map +1 -0
- package/dist/seed/devLogin.js +52 -0
- package/dist/seed/exampleIdentities.d.ts +58 -0
- package/dist/seed/exampleIdentities.d.ts.map +1 -0
- package/dist/seed/exampleIdentities.js +26 -0
- package/dist/seed/metadata.d.ts +20 -0
- package/dist/seed/metadata.d.ts.map +1 -0
- package/dist/seed/metadata.js +27 -0
- package/dist/seed/seed.d.ts +264 -0
- package/dist/seed/seed.d.ts.map +1 -0
- package/dist/seed/seed.js +37 -0
- package/dist/seed/seededRows.d.ts +26 -0
- package/dist/seed/seededRows.d.ts.map +1 -0
- package/dist/seed/seededRows.js +20 -0
- package/dist/seed/writeD1.d.ts +43 -0
- package/dist/seed/writeD1.d.ts.map +1 -0
- package/dist/seed/writeD1.js +73 -0
- package/dist/seed/writeKv.d.ts +29 -0
- package/dist/seed/writeKv.d.ts.map +1 -0
- package/dist/seed/writeKv.js +63 -0
- package/dist/semver/semver.d.ts +62 -0
- package/dist/semver/semver.d.ts.map +1 -0
- package/dist/semver/semver.js +137 -0
- package/dist/text/comments.d.ts +62 -0
- package/dist/text/comments.d.ts.map +1 -0
- package/dist/text/comments.js +161 -0
- package/dist/version.generated.d.ts +5 -0
- package/dist/version.generated.d.ts.map +1 -0
- package/dist/version.generated.js +7 -0
- package/dist/worker/health.d.ts +39 -0
- package/dist/worker/health.d.ts.map +1 -0
- package/dist/worker/health.js +41 -0
- package/dist/worker/identity.d.ts +191 -0
- package/dist/worker/identity.d.ts.map +1 -0
- package/dist/worker/identity.js +162 -0
- package/dist/workflow/bindings.d.ts +41 -0
- package/dist/workflow/bindings.d.ts.map +1 -0
- package/dist/workflow/bindings.js +53 -0
- package/dist/workflow/dispatch.d.ts +98 -0
- package/dist/workflow/dispatch.d.ts.map +1 -0
- package/dist/workflow/dispatch.js +148 -0
- package/dist/workflow/dispatchRoute.d.ts +80 -0
- package/dist/workflow/dispatchRoute.d.ts.map +1 -0
- package/dist/workflow/dispatchRoute.js +134 -0
- package/dist/workflow/faults.d.ts +97 -0
- package/dist/workflow/faults.d.ts.map +1 -0
- package/dist/workflow/faults.js +111 -0
- package/dist/workflow/host.d.ts +176 -0
- package/dist/workflow/host.d.ts.map +1 -0
- package/dist/workflow/host.js +116 -0
- package/dist/workflow/hostEntry.d.ts +50 -0
- package/dist/workflow/hostEntry.d.ts.map +1 -0
- package/dist/workflow/hostEntry.js +24 -0
- package/dist/workflow/hostEnv.d.ts +143 -0
- package/dist/workflow/hostEnv.d.ts.map +1 -0
- package/dist/workflow/hostEnv.js +170 -0
- package/dist/workflow/loopback.d.ts +52 -0
- package/dist/workflow/loopback.d.ts.map +1 -0
- package/dist/workflow/loopback.js +76 -0
- package/dist/workflow/naming.d.ts +79 -0
- package/dist/workflow/naming.d.ts.map +1 -0
- package/dist/workflow/naming.js +138 -0
- package/dist/workflow/register.d.ts +19 -0
- package/dist/workflow/register.d.ts.map +1 -0
- package/dist/workflow/register.js +40 -0
- package/dist/workflow/schemas.d.ts +44 -0
- package/dist/workflow/schemas.d.ts.map +1 -0
- package/dist/workflow/schemas.js +43 -0
- package/dist/workflow/spec.d.ts +81 -0
- package/dist/workflow/spec.d.ts.map +1 -0
- package/dist/workflow/spec.js +0 -0
- package/dist/workflow/stepMessage.d.ts +122 -0
- package/dist/workflow/stepMessage.d.ts.map +1 -0
- package/dist/workflow/stepMessage.js +152 -0
- package/package.json +20 -9
- package/src/capability/manifest.ts +21 -0
- package/src/version.generated.ts +1 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a thrown value that no type describes — the one place the kit records **what a runtime does to
|
|
3
|
+
* an error on its way out** (#223), and **the one place that decides whether what it says may be shown**
|
|
4
|
+
* (#228).
|
|
5
|
+
*
|
|
6
|
+
* Nothing here classifies anything. Classification is policy and belongs to the surface that refuses:
|
|
7
|
+
* which causes a loader recognizes, and what each refusal says, differ per surface and are meant to.
|
|
8
|
+
* This module holds only what does not — the facts about the shapes a runtime hands over, and the filter
|
|
9
|
+
* that decides whether one of its strings is safe to put in front of an adopter.
|
|
10
|
+
*
|
|
11
|
+
* **The filter is here for the same reason the facts are, and the argument took two issues to finish.**
|
|
12
|
+
* #223 moved `rootCause`, `prop` and `isBuildFailureWrapper` in and deliberately left `safeReason` beside
|
|
13
|
+
* the three refusals, on the reading that what a surface may say is that surface's business. That is true
|
|
14
|
+
* of the sentences and false of the filter: whether a string carries a path, a stack frame or half a
|
|
15
|
+
* parser's ANSI box is a property of the string, and the answer cannot differ between the CLI, a
|
|
16
|
+
* capability loader and the vite plugin without one of them being wrong. It was three near-verbatim
|
|
17
|
+
* copies, and the hole #223 found in it had to be closed three times (#228).
|
|
18
|
+
*
|
|
19
|
+
* `@pithy-sh/vite` depends on this package and on nothing else in the kit. That constraint is what made
|
|
20
|
+
* core the right home for `rootCause`; it applies to the filter identically.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Read a property off an unknown throwable without widening anything to `any`.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately duck-typed. Bun's `ResolveMessage` and `BuildMessage` — the two shapes the CLI's `bin` and
|
|
26
|
+
* the vite plugin actually catch, since both run on Bun — are their own classes and are **not**
|
|
27
|
+
* `instanceof Error`. An `instanceof` gate here passes a whole suite under vitest, which runs on Node,
|
|
28
|
+
* and silently drops the parser's own sentence on the runtime that ships. That is #207, and it is why
|
|
29
|
+
* every reader of a cause in this kit goes through this function.
|
|
30
|
+
*
|
|
31
|
+
* Non-enumerable properties are read too — `AggregateError.errors` is one, and {@link rootCause}
|
|
32
|
+
* depends on it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function prop(cause: unknown, key: string): unknown;
|
|
35
|
+
/**
|
|
36
|
+
* The diagnostic **inside** Bun's `AggregateError` wrapper — the thing a classifier must actually look at.
|
|
37
|
+
*
|
|
38
|
+
* `import()` on Bun hands one build diagnostic over bare and **two or more inside an `AggregateError`**,
|
|
39
|
+
* whose own `message` is `N errors building "<absolute path>"` and whose `Object.keys` is empty. Node
|
|
40
|
+
* throws the diagnostic directly, so no Node-shaped fixture can show this: it is found by running on Bun,
|
|
41
|
+
* and only by running on Bun with a failure of the *realistic* shape.
|
|
42
|
+
*
|
|
43
|
+
* **And the realistic shape is the wrapped one.** A stray brace cascades — one missing `}` in a
|
|
44
|
+
* `pithy.config.ts` produced four diagnostics on Bun 1.3.14 — so a syntax error arrives wrapped far more
|
|
45
|
+
* often than bare. Classified as-is, the wrapper matches nothing: not a resolution failure, not a parse
|
|
46
|
+
* error, just something with a message. The refusal then says *the config threw while loading, run the
|
|
47
|
+
* file directly* while holding the line and column in its hand. #207 fixed the bare case and left this
|
|
48
|
+
* one; #217 hit it in the capability loaders; #223 is it a third time, which is why it lives here.
|
|
49
|
+
*
|
|
50
|
+
* The **first** diagnostic only. The rest are the cascade, not the fault, and naming four positions
|
|
51
|
+
* buries the one that matters.
|
|
52
|
+
*
|
|
53
|
+
* Bounded to four levels: a wrapper holding a wrapper is not a shape any runtime produces today, and an
|
|
54
|
+
* unbounded walk over an adopter's own `errors` field is not something this should be talked into.
|
|
55
|
+
* Anything else — a bare diagnostic, an empty `errors`, a non-array `errors` — is returned untouched, so
|
|
56
|
+
* a runtime that stops wrapping needs no change here.
|
|
57
|
+
*/
|
|
58
|
+
export declare function rootCause(cause: unknown): unknown;
|
|
59
|
+
/**
|
|
60
|
+
* The wrapper **after the diagnostics are gone** — a build that failed, and that is all that is left.
|
|
61
|
+
*
|
|
62
|
+
* {@link rootCause} handles the first `import()`. It cannot handle the second: Bun caches a failed module
|
|
63
|
+
* and re-throws an `AggregateError` with **no `errors` at all**, just the count and the path. There is
|
|
64
|
+
* nothing to unwrap, so a classifier that only unwraps reads a syntax error as "it threw" from the second
|
|
65
|
+
* caller onward — which is what `pithy doctor` shows an adopter, because resolving the project's account
|
|
66
|
+
* loads the root config before the report does (#206, #223).
|
|
67
|
+
*
|
|
68
|
+
* Nothing else Bun throws degrades this way: a `ResolveMessage`, a bare `BuildMessage` and a module's own
|
|
69
|
+
* `Error` are byte-identical on every import. Only the wrapper forgets.
|
|
70
|
+
*
|
|
71
|
+
* So the wrapper is recognized by its own message. **What it proves is narrow and worth stating:** the
|
|
72
|
+
* module reached the builder and the builder produced diagnostics. It did not fail to resolve — Bun
|
|
73
|
+
* throws the first `ResolveMessage` bare, wrapping nothing, even for a file with several bad imports —
|
|
74
|
+
* and it did not run, so it cannot have thrown. It failed to parse or to build. A caller may say that
|
|
75
|
+
* much and no more: the message itself carries an absolute path and a count, neither of which is the
|
|
76
|
+
* adopter's business, so it is a *predicate* here rather than a string anyone is tempted to print.
|
|
77
|
+
*/
|
|
78
|
+
export declare function isBuildFailureWrapper(cause: unknown): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The thrown value's message, **de-colored** — `undefined` when it has none.
|
|
81
|
+
*
|
|
82
|
+
* Duck-typed for {@link prop}'s reason. The de-coloring is here rather than at each caller because an
|
|
83
|
+
* escape sequence is formatting the runtime added, not something a surface has an opinion about — and
|
|
84
|
+
* because the three copies of this had already drifted: two stripped with `\u001b`, the third with a
|
|
85
|
+
* literal escape character sitting in its source where no reviewer would see it (#228).
|
|
86
|
+
*
|
|
87
|
+
* This says what the cause *says*. Whether any of it may be shown is {@link safeReason}.
|
|
88
|
+
*/
|
|
89
|
+
export declare function causeMessage(cause: unknown): string | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* The cause's own message, but **only when the whole of it is one safe sentence** — the kit's single
|
|
92
|
+
* decision about whether a runtime's string may reach an adopter (#228).
|
|
93
|
+
*
|
|
94
|
+
* A parser's reason (`Expected identifier but found "{"`) is a sentence an adopter acts on. The
|
|
95
|
+
* diagnostic a bundler wraps the same fault in is a multi-line ANSI box quoting an absolute path and the
|
|
96
|
+
* source line, which is throw-site context wearing a message's clothes. What is dropped here still
|
|
97
|
+
* reaches `detail`, which the CLI renderer never prints and the HTTP codec strips — so a refusal costs a
|
|
98
|
+
* developer one frame of digging, and a mistake puts a path, a source line or a stack frame in a field
|
|
99
|
+
* that is rendered.
|
|
100
|
+
*
|
|
101
|
+
* **Provenance first, then content.** Bun's build-failure wrapper is refused outright, before a single
|
|
102
|
+
* content test runs. The content tests exist for a diagnostic that *might* be safe; the wrapper never is,
|
|
103
|
+
* because its whole message is a count nobody asked for and a path that must not travel. Leaving it to
|
|
104
|
+
* the content tests is what leaked: `2 errors building "app/config:12:5.ts"` has no leading slash, sailed
|
|
105
|
+
* through the absolute-path check, and produced *The config does not parse: 2 errors building
|
|
106
|
+
* "app/config:12:5.ts". Line 12, column 5.* — a path the adopter never wrote, and a position fabricated
|
|
107
|
+
* out of a file name (#223). That suppression was written three times because the filter was. It is
|
|
108
|
+
* written once here, and {@link failurePosition} refuses the same shape for the same reason.
|
|
109
|
+
*
|
|
110
|
+
* A trailing period comes off: every caller puts this inside a sentence of its own.
|
|
111
|
+
*/
|
|
112
|
+
export declare function safeReason(cause: unknown): string | undefined;
|
|
113
|
+
/** Where a runtime says a failure was. Two numbers, and never the file they were quoted beside. */
|
|
114
|
+
export interface FailurePosition {
|
|
115
|
+
/** 1-based line, as the runtime reported it. */
|
|
116
|
+
line: number;
|
|
117
|
+
/** Column, as the runtime reported it. */
|
|
118
|
+
column: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `line`/`column`, from the structured position where a runtime gives one, else from its own `…:LINE:COL`.
|
|
122
|
+
*
|
|
123
|
+
* A recorded fact about two runtimes: Bun's `BuildMessage` carries `position`, and a transform that has no
|
|
124
|
+
* such field states the same thing in its message tail. Only the two numbers are lifted out — never the
|
|
125
|
+
* path they are appended to. Whether a surface prints them is that surface's policy: two of the three
|
|
126
|
+
* classifiers do, and the capability loader deliberately does not.
|
|
127
|
+
*
|
|
128
|
+
* Refuses Bun's build-failure wrapper on provenance, exactly as {@link safeReason} does. That message
|
|
129
|
+
* holds a file name and a count and nothing about where anything failed, so a `:12:5` found in it is a
|
|
130
|
+
* coincidence of the path's own characters — and a fabricated position reads as authoritative.
|
|
131
|
+
*/
|
|
132
|
+
export declare function failurePosition(cause: unknown): FailurePosition | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* The specifier that did not resolve.
|
|
135
|
+
*
|
|
136
|
+
* A recorded fact about two runtimes: Bun's `ResolveMessage` carries it as a **field**, and Node states it
|
|
137
|
+
* in prose. The field is preferred wherever there is one, because the prose around it names the referrer's
|
|
138
|
+
* absolute path — the specifier is the adopter's own import, the referrer is our frame.
|
|
139
|
+
*
|
|
140
|
+
* Whether a surface names it, and what it advises when it does, is that surface's policy and stays there.
|
|
141
|
+
*/
|
|
142
|
+
export declare function unresolvedSpecifier(cause: unknown): string | undefined;
|
|
143
|
+
//# sourceMappingURL=cause.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cause.d.ts","sourceRoot":"","sources":["../../src/error/cause.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQjD;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG7D;AAeD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG/D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQ7D;AAED,mGAAmG;AACnG,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAS3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKtE"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
//#region src/error/cause.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reading a thrown value that no type describes — the one place the kit records **what a runtime does to
|
|
4
|
+
* an error on its way out** (#223), and **the one place that decides whether what it says may be shown**
|
|
5
|
+
* (#228).
|
|
6
|
+
*
|
|
7
|
+
* Nothing here classifies anything. Classification is policy and belongs to the surface that refuses:
|
|
8
|
+
* which causes a loader recognizes, and what each refusal says, differ per surface and are meant to.
|
|
9
|
+
* This module holds only what does not — the facts about the shapes a runtime hands over, and the filter
|
|
10
|
+
* that decides whether one of its strings is safe to put in front of an adopter.
|
|
11
|
+
*
|
|
12
|
+
* **The filter is here for the same reason the facts are, and the argument took two issues to finish.**
|
|
13
|
+
* #223 moved `rootCause`, `prop` and `isBuildFailureWrapper` in and deliberately left `safeReason` beside
|
|
14
|
+
* the three refusals, on the reading that what a surface may say is that surface's business. That is true
|
|
15
|
+
* of the sentences and false of the filter: whether a string carries a path, a stack frame or half a
|
|
16
|
+
* parser's ANSI box is a property of the string, and the answer cannot differ between the CLI, a
|
|
17
|
+
* capability loader and the vite plugin without one of them being wrong. It was three near-verbatim
|
|
18
|
+
* copies, and the hole #223 found in it had to be closed three times (#228).
|
|
19
|
+
*
|
|
20
|
+
* `@pithy-sh/vite` depends on this package and on nothing else in the kit. That constraint is what made
|
|
21
|
+
* core the right home for `rootCause`; it applies to the filter identically.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Read a property off an unknown throwable without widening anything to `any`.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately duck-typed. Bun's `ResolveMessage` and `BuildMessage` — the two shapes the CLI's `bin` and
|
|
27
|
+
* the vite plugin actually catch, since both run on Bun — are their own classes and are **not**
|
|
28
|
+
* `instanceof Error`. An `instanceof` gate here passes a whole suite under vitest, which runs on Node,
|
|
29
|
+
* and silently drops the parser's own sentence on the runtime that ships. That is #207, and it is why
|
|
30
|
+
* every reader of a cause in this kit goes through this function.
|
|
31
|
+
*
|
|
32
|
+
* Non-enumerable properties are read too — `AggregateError.errors` is one, and {@link rootCause}
|
|
33
|
+
* depends on it.
|
|
34
|
+
*/
|
|
35
|
+
function prop(cause, key) {
|
|
36
|
+
if (typeof cause !== "object" || cause === null) return void 0;
|
|
37
|
+
return cause[key];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The diagnostic **inside** Bun's `AggregateError` wrapper — the thing a classifier must actually look at.
|
|
41
|
+
*
|
|
42
|
+
* `import()` on Bun hands one build diagnostic over bare and **two or more inside an `AggregateError`**,
|
|
43
|
+
* whose own `message` is `N errors building "<absolute path>"` and whose `Object.keys` is empty. Node
|
|
44
|
+
* throws the diagnostic directly, so no Node-shaped fixture can show this: it is found by running on Bun,
|
|
45
|
+
* and only by running on Bun with a failure of the *realistic* shape.
|
|
46
|
+
*
|
|
47
|
+
* **And the realistic shape is the wrapped one.** A stray brace cascades — one missing `}` in a
|
|
48
|
+
* `pithy.config.ts` produced four diagnostics on Bun 1.3.14 — so a syntax error arrives wrapped far more
|
|
49
|
+
* often than bare. Classified as-is, the wrapper matches nothing: not a resolution failure, not a parse
|
|
50
|
+
* error, just something with a message. The refusal then says *the config threw while loading, run the
|
|
51
|
+
* file directly* while holding the line and column in its hand. #207 fixed the bare case and left this
|
|
52
|
+
* one; #217 hit it in the capability loaders; #223 is it a third time, which is why it lives here.
|
|
53
|
+
*
|
|
54
|
+
* The **first** diagnostic only. The rest are the cascade, not the fault, and naming four positions
|
|
55
|
+
* buries the one that matters.
|
|
56
|
+
*
|
|
57
|
+
* Bounded to four levels: a wrapper holding a wrapper is not a shape any runtime produces today, and an
|
|
58
|
+
* unbounded walk over an adopter's own `errors` field is not something this should be talked into.
|
|
59
|
+
* Anything else — a bare diagnostic, an empty `errors`, a non-array `errors` — is returned untouched, so
|
|
60
|
+
* a runtime that stops wrapping needs no change here.
|
|
61
|
+
*/
|
|
62
|
+
function rootCause(cause) {
|
|
63
|
+
let current = cause;
|
|
64
|
+
for (let depth = 0; depth < 4; depth += 1) {
|
|
65
|
+
const errors = prop(current, "errors");
|
|
66
|
+
if (!Array.isArray(errors) || errors.length === 0 || errors[0] === void 0) return current;
|
|
67
|
+
current = errors[0];
|
|
68
|
+
}
|
|
69
|
+
return current;
|
|
70
|
+
}
|
|
71
|
+
/** Bun's wrapper announces itself: `4 errors building "<absolute path>"`, and nothing else says that. */
|
|
72
|
+
const BUILD_FAILURE = /^\d+ errors? building "/;
|
|
73
|
+
/**
|
|
74
|
+
* The wrapper **after the diagnostics are gone** — a build that failed, and that is all that is left.
|
|
75
|
+
*
|
|
76
|
+
* {@link rootCause} handles the first `import()`. It cannot handle the second: Bun caches a failed module
|
|
77
|
+
* and re-throws an `AggregateError` with **no `errors` at all**, just the count and the path. There is
|
|
78
|
+
* nothing to unwrap, so a classifier that only unwraps reads a syntax error as "it threw" from the second
|
|
79
|
+
* caller onward — which is what `pithy doctor` shows an adopter, because resolving the project's account
|
|
80
|
+
* loads the root config before the report does (#206, #223).
|
|
81
|
+
*
|
|
82
|
+
* Nothing else Bun throws degrades this way: a `ResolveMessage`, a bare `BuildMessage` and a module's own
|
|
83
|
+
* `Error` are byte-identical on every import. Only the wrapper forgets.
|
|
84
|
+
*
|
|
85
|
+
* So the wrapper is recognized by its own message. **What it proves is narrow and worth stating:** the
|
|
86
|
+
* module reached the builder and the builder produced diagnostics. It did not fail to resolve — Bun
|
|
87
|
+
* throws the first `ResolveMessage` bare, wrapping nothing, even for a file with several bad imports —
|
|
88
|
+
* and it did not run, so it cannot have thrown. It failed to parse or to build. A caller may say that
|
|
89
|
+
* much and no more: the message itself carries an absolute path and a count, neither of which is the
|
|
90
|
+
* adopter's business, so it is a *predicate* here rather than a string anyone is tempted to print.
|
|
91
|
+
*/
|
|
92
|
+
function isBuildFailureWrapper(cause) {
|
|
93
|
+
const message = prop(cause, "message");
|
|
94
|
+
return typeof message === "string" && BUILD_FAILURE.test(message);
|
|
95
|
+
}
|
|
96
|
+
/** Escape sequences a runtime colors its diagnostics with. They are formatting, and they never travel. */
|
|
97
|
+
const ANSI = /\u001b\[[0-9;]*m/g;
|
|
98
|
+
/** Anything that looks like the start of an absolute path — POSIX, `~`, or a Windows drive. */
|
|
99
|
+
const ABSOLUTE_PATH = /(^|[\s'"(])(\/|~\/|[A-Za-z]:[\\/])/;
|
|
100
|
+
/** A stack frame, in the one spelling every runtime here agrees on. */
|
|
101
|
+
const STACK_FRAME = /\bat \S+:\d+:\d+/;
|
|
102
|
+
/** The longest a reason may be before it has stopped being a sentence and started being a diagnostic. */
|
|
103
|
+
const REASON_LIMIT = 160;
|
|
104
|
+
/**
|
|
105
|
+
* The thrown value's message, **de-colored** — `undefined` when it has none.
|
|
106
|
+
*
|
|
107
|
+
* Duck-typed for {@link prop}'s reason. The de-coloring is here rather than at each caller because an
|
|
108
|
+
* escape sequence is formatting the runtime added, not something a surface has an opinion about — and
|
|
109
|
+
* because the three copies of this had already drifted: two stripped with `\u001b`, the third with a
|
|
110
|
+
* literal escape character sitting in its source where no reviewer would see it (#228).
|
|
111
|
+
*
|
|
112
|
+
* This says what the cause *says*. Whether any of it may be shown is {@link safeReason}.
|
|
113
|
+
*/
|
|
114
|
+
function causeMessage(cause) {
|
|
115
|
+
const message = prop(cause, "message");
|
|
116
|
+
return typeof message === "string" ? message.replace(ANSI, "") : void 0;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The cause's own message, but **only when the whole of it is one safe sentence** — the kit's single
|
|
120
|
+
* decision about whether a runtime's string may reach an adopter (#228).
|
|
121
|
+
*
|
|
122
|
+
* A parser's reason (`Expected identifier but found "{"`) is a sentence an adopter acts on. The
|
|
123
|
+
* diagnostic a bundler wraps the same fault in is a multi-line ANSI box quoting an absolute path and the
|
|
124
|
+
* source line, which is throw-site context wearing a message's clothes. What is dropped here still
|
|
125
|
+
* reaches `detail`, which the CLI renderer never prints and the HTTP codec strips — so a refusal costs a
|
|
126
|
+
* developer one frame of digging, and a mistake puts a path, a source line or a stack frame in a field
|
|
127
|
+
* that is rendered.
|
|
128
|
+
*
|
|
129
|
+
* **Provenance first, then content.** Bun's build-failure wrapper is refused outright, before a single
|
|
130
|
+
* content test runs. The content tests exist for a diagnostic that *might* be safe; the wrapper never is,
|
|
131
|
+
* because its whole message is a count nobody asked for and a path that must not travel. Leaving it to
|
|
132
|
+
* the content tests is what leaked: `2 errors building "app/config:12:5.ts"` has no leading slash, sailed
|
|
133
|
+
* through the absolute-path check, and produced *The config does not parse: 2 errors building
|
|
134
|
+
* "app/config:12:5.ts". Line 12, column 5.* — a path the adopter never wrote, and a position fabricated
|
|
135
|
+
* out of a file name (#223). That suppression was written three times because the filter was. It is
|
|
136
|
+
* written once here, and {@link failurePosition} refuses the same shape for the same reason.
|
|
137
|
+
*
|
|
138
|
+
* A trailing period comes off: every caller puts this inside a sentence of its own.
|
|
139
|
+
*/
|
|
140
|
+
function safeReason(cause) {
|
|
141
|
+
if (isBuildFailureWrapper(cause)) return void 0;
|
|
142
|
+
const text = (causeMessage(cause) ?? "").trim();
|
|
143
|
+
if (text.length === 0 || text.length > REASON_LIMIT) return void 0;
|
|
144
|
+
if (text.includes("\n")) return void 0;
|
|
145
|
+
if (ABSOLUTE_PATH.test(text)) return void 0;
|
|
146
|
+
if (STACK_FRAME.test(text)) return void 0;
|
|
147
|
+
return text.replace(/\.$/, "");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* `line`/`column`, from the structured position where a runtime gives one, else from its own `…:LINE:COL`.
|
|
151
|
+
*
|
|
152
|
+
* A recorded fact about two runtimes: Bun's `BuildMessage` carries `position`, and a transform that has no
|
|
153
|
+
* such field states the same thing in its message tail. Only the two numbers are lifted out — never the
|
|
154
|
+
* path they are appended to. Whether a surface prints them is that surface's policy: two of the three
|
|
155
|
+
* classifiers do, and the capability loader deliberately does not.
|
|
156
|
+
*
|
|
157
|
+
* Refuses Bun's build-failure wrapper on provenance, exactly as {@link safeReason} does. That message
|
|
158
|
+
* holds a file name and a count and nothing about where anything failed, so a `:12:5` found in it is a
|
|
159
|
+
* coincidence of the path's own characters — and a fabricated position reads as authoritative.
|
|
160
|
+
*/
|
|
161
|
+
function failurePosition(cause) {
|
|
162
|
+
if (isBuildFailureWrapper(cause)) return void 0;
|
|
163
|
+
const position = prop(cause, "position");
|
|
164
|
+
const line = prop(position, "line");
|
|
165
|
+
const column = prop(position, "column");
|
|
166
|
+
if (typeof line === "number" && line > 0 && typeof column === "number") return {
|
|
167
|
+
line,
|
|
168
|
+
column
|
|
169
|
+
};
|
|
170
|
+
const match = /:(\d+):(\d+)/.exec(causeMessage(cause) ?? "");
|
|
171
|
+
if (!match?.[1] || !match[2]) return void 0;
|
|
172
|
+
return {
|
|
173
|
+
line: Number(match[1]),
|
|
174
|
+
column: Number(match[2])
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The specifier that did not resolve.
|
|
179
|
+
*
|
|
180
|
+
* A recorded fact about two runtimes: Bun's `ResolveMessage` carries it as a **field**, and Node states it
|
|
181
|
+
* in prose. The field is preferred wherever there is one, because the prose around it names the referrer's
|
|
182
|
+
* absolute path — the specifier is the adopter's own import, the referrer is our frame.
|
|
183
|
+
*
|
|
184
|
+
* Whether a surface names it, and what it advises when it does, is that surface's policy and stays there.
|
|
185
|
+
*/
|
|
186
|
+
function unresolvedSpecifier(cause) {
|
|
187
|
+
const field = prop(cause, "specifier");
|
|
188
|
+
if (typeof field === "string" && field.length > 0) return field;
|
|
189
|
+
return /Cannot find (?:package|module) ['"]([^'"]+)['"]/.exec(causeMessage(cause) ?? "")?.[1];
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
export { causeMessage, failurePosition, isBuildFailureWrapper, prop, rootCause, safeReason, unresolvedSpecifier };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { ErrorPayload } from "./payload";
|
|
3
|
+
import { PublicErrorPayload } from "./payload";
|
|
4
|
+
/**
|
|
5
|
+
* **The one place an error becomes bytes for a client.** Whatever the transport.
|
|
6
|
+
*
|
|
7
|
+
* An error leaves this process by more than one road. A JSON response is the busiest, and it had the
|
|
8
|
+
* boundary written into it — but a Durable Object also pushes error frames down a WebSocket, and a
|
|
9
|
+
* transport that grew its own projection grew its own idea of what a client may read. That is how
|
|
10
|
+
* `action` reached a browser after the schema had already classified it: not because anyone disagreed
|
|
11
|
+
* with the rule, but because the rule lived inside the HTTP codec, and a socket is not HTTP.
|
|
12
|
+
*
|
|
13
|
+
* So the rule sits here, above every transport, and each transport calls it:
|
|
14
|
+
*
|
|
15
|
+
* - `HttpError.encode` (./http) — the JSON body of a response.
|
|
16
|
+
* - The multiplayer session Durable Object — the `error` frame on a player's socket.
|
|
17
|
+
* - Anything added next. An SSE `event:`, a queue consumer echoing a failure, a Workflow surfacing one.
|
|
18
|
+
* None of them needs to know *what* is stripped; they need to know they must come through here.
|
|
19
|
+
*
|
|
20
|
+
* What it strips, and why that is not a list to maintain: `action` and `detail` are removed by name,
|
|
21
|
+
* and the result is then parsed by `PublicErrorPayload`, which has neither key. The parse is what makes
|
|
22
|
+
* the boundary hold for an **adopter's** code as well as the kit's — it is a property of the schema, so
|
|
23
|
+
* a field classified as operator-facing tomorrow is stripped here without this function changing.
|
|
24
|
+
*
|
|
25
|
+
* The mirror is {@link operatorError} in ./terminal, which keeps `action` because whoever ran the
|
|
26
|
+
* command is the person who can act on "Bind a D1 database named DB in wrangler.jsonc". Two audiences,
|
|
27
|
+
* two functions, and neither one is a transport.
|
|
28
|
+
*
|
|
29
|
+
* It takes the **input** side of `ErrorPayload` rather than the output side, which is the wider of the
|
|
30
|
+
* two: an adopter's code carries a brand once parsed, and a codec's encode side is handed the shape
|
|
31
|
+
* before that brand exists. A `PithyError`'s own payload satisfies it either way, and the brand is not
|
|
32
|
+
* something this function needs — it parses what it is given.
|
|
33
|
+
*/
|
|
34
|
+
export declare function clientError(payload: z.input<typeof ErrorPayload>): PublicErrorPayload;
|
|
35
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/error/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,GAAG,kBAAkB,CAKrF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PublicErrorPayload } from "./payload.js";
|
|
2
|
+
//#region src/error/client.ts
|
|
3
|
+
/**
|
|
4
|
+
* **The one place an error becomes bytes for a client.** Whatever the transport.
|
|
5
|
+
*
|
|
6
|
+
* An error leaves this process by more than one road. A JSON response is the busiest, and it had the
|
|
7
|
+
* boundary written into it — but a Durable Object also pushes error frames down a WebSocket, and a
|
|
8
|
+
* transport that grew its own projection grew its own idea of what a client may read. That is how
|
|
9
|
+
* `action` reached a browser after the schema had already classified it: not because anyone disagreed
|
|
10
|
+
* with the rule, but because the rule lived inside the HTTP codec, and a socket is not HTTP.
|
|
11
|
+
*
|
|
12
|
+
* So the rule sits here, above every transport, and each transport calls it:
|
|
13
|
+
*
|
|
14
|
+
* - `HttpError.encode` (./http) — the JSON body of a response.
|
|
15
|
+
* - The multiplayer session Durable Object — the `error` frame on a player's socket.
|
|
16
|
+
* - Anything added next. An SSE `event:`, a queue consumer echoing a failure, a Workflow surfacing one.
|
|
17
|
+
* None of them needs to know *what* is stripped; they need to know they must come through here.
|
|
18
|
+
*
|
|
19
|
+
* What it strips, and why that is not a list to maintain: `action` and `detail` are removed by name,
|
|
20
|
+
* and the result is then parsed by `PublicErrorPayload`, which has neither key. The parse is what makes
|
|
21
|
+
* the boundary hold for an **adopter's** code as well as the kit's — it is a property of the schema, so
|
|
22
|
+
* a field classified as operator-facing tomorrow is stripped here without this function changing.
|
|
23
|
+
*
|
|
24
|
+
* The mirror is {@link operatorError} in ./terminal, which keeps `action` because whoever ran the
|
|
25
|
+
* command is the person who can act on "Bind a D1 database named DB in wrangler.jsonc". Two audiences,
|
|
26
|
+
* two functions, and neither one is a transport.
|
|
27
|
+
*
|
|
28
|
+
* It takes the **input** side of `ErrorPayload` rather than the output side, which is the wider of the
|
|
29
|
+
* two: an adopter's code carries a brand once parsed, and a codec's encode side is handed the shape
|
|
30
|
+
* before that brand exists. A `PithyError`'s own payload satisfies it either way, and the brand is not
|
|
31
|
+
* something this function needs — it parses what it is given.
|
|
32
|
+
*/
|
|
33
|
+
function clientError(payload) {
|
|
34
|
+
const { action: _action, detail: _detail, ...wire } = payload;
|
|
35
|
+
return PublicErrorPayload.parse(wire);
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { clientError };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { MessageParams } from "../i18n/catalog";
|
|
2
|
+
import type { ErrorPayload, KitErrorDomain } from "./payload";
|
|
3
|
+
import { ExtendedErrorPayload } from "./payload";
|
|
4
|
+
/**
|
|
5
|
+
* A kit domain used in an adopter's code. The false branch is `unknown`, which intersects away; the
|
|
6
|
+
* true branch is a string the caller's own `code` cannot possibly be, so the mistake surfaces at the
|
|
7
|
+
* declaration with its reason spelled out in the type error rather than as `never`.
|
|
8
|
+
*/
|
|
9
|
+
type ReservedDomain<Code extends string> = Code extends `${KitErrorDomain}/${string}` ? {
|
|
10
|
+
code: "That domain is the kit's. Namespace your code under a domain of your own.";
|
|
11
|
+
} : unknown;
|
|
12
|
+
/**
|
|
13
|
+
* The payload behind one code, kit or adopter. `Extract` finds a kit member by its literal `code`;
|
|
14
|
+
* an adopter's code is branded and so extracts to nothing, and the open member stands in with the
|
|
15
|
+
* literal intersected back on. Type a vehicle class with it exactly as the kit's own subclasses are:
|
|
16
|
+
* `declare readonly payload: ErrorPayloadOf<"connect/device_code_expired">`.
|
|
17
|
+
*/
|
|
18
|
+
export type ErrorPayloadOf<Code extends string> = [Extract<ErrorPayload, {
|
|
19
|
+
code: Code;
|
|
20
|
+
}>] extends [never] ? ExtendedErrorPayload & {
|
|
21
|
+
code: Code;
|
|
22
|
+
} : Extract<ErrorPayload, {
|
|
23
|
+
code: Code;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Narrow a caught payload to one code. `payload.code === "core/not_found"` narrows a kit member on
|
|
27
|
+
* its own, but an adopter's code is branded — the brand is what keeps a kit narrow honest — so the
|
|
28
|
+
* same comparison against a bare literal is a type error, and `Extract` on it is silently `never`.
|
|
29
|
+
* This is the way in for both: one guard, kit codes and adopter codes alike.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* catch (error) {
|
|
33
|
+
* if (error instanceof PithyError && isErrorCode(error.payload, "connect/device_code_expired")) {
|
|
34
|
+
* return restartDeviceFlow();
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function isErrorCode<const Code extends `${string}/${string}`>(payload: ErrorPayload, code: Code): payload is ErrorPayloadOf<Code>;
|
|
40
|
+
/**
|
|
41
|
+
* The one door into `ErrorPayload` for a code the kit does not define. An adopter writing a control
|
|
42
|
+
* plane, a device-code flow, or a key-rotation lock has failures the kit has no word for; without
|
|
43
|
+
* this they reuse a kit code that means something else, or stop throwing `PithyError` at all.
|
|
44
|
+
*
|
|
45
|
+
* It is a function rather than a plain object literal for the same reason `defineSecretRegistry` is:
|
|
46
|
+
* the declaration is where the mistake is, so the declaration is where it should fail. A code under
|
|
47
|
+
* one of the kit's domains does not compile — the kit's domains are reserved the way `pithy_` table
|
|
48
|
+
* names are, so nobody redefines `auth/forbidden` with a status of their choosing, and a
|
|
49
|
+
* capability's own typo stays a hard failure instead of becoming somebody's custom error. The parse
|
|
50
|
+
* below is the second gate, for the grammar, the 4xx/5xx bound, and any caller who cast their way
|
|
51
|
+
* past the first.
|
|
52
|
+
*
|
|
53
|
+
* What it does **not** do is pin a status to a code. A kit member fixes one status per code, and
|
|
54
|
+
* that cannot survive a set the kit does not hold: this seam bounds the range and nothing more, so
|
|
55
|
+
* throwing `connect/device_code_expired` as a 410 from one site and a 500 from another passes every
|
|
56
|
+
* layer. Declare each of your codes in one vehicle class, once, and that stays true by construction.
|
|
57
|
+
*
|
|
58
|
+
* It does not move the security boundary either. The payload it returns carries `action` and
|
|
59
|
+
* `detail` exactly like a kit member, and the HTTP codec strips both from every member alike. An
|
|
60
|
+
* adopter cannot opt into leaking, because there is nothing to opt into — the wire shape has no such
|
|
61
|
+
* key to fill, whoever defined the code.
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* export class DeviceCodeExpiredError extends PithyError {
|
|
65
|
+
* declare readonly payload: ErrorPayloadOf<"connect/device_code_expired">;
|
|
66
|
+
*
|
|
67
|
+
* constructor(detail?: string) {
|
|
68
|
+
* super(defineErrorPayload({
|
|
69
|
+
* code: "connect/device_code_expired",
|
|
70
|
+
* status: 410,
|
|
71
|
+
* message: "That device code has expired.",
|
|
72
|
+
* action: "Run pithy dashboard connect again.",
|
|
73
|
+
* detail,
|
|
74
|
+
* }));
|
|
75
|
+
* }
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare function defineErrorPayload<const Code extends `${string}/${string}`>(payload: {
|
|
80
|
+
/** `domain/reason`, lower snake_case, under a domain of the adopter's own — never one the kit uses. */
|
|
81
|
+
code: Code;
|
|
82
|
+
/** The HTTP status this code maps to. A 4xx or a 5xx — an error is not a success. */
|
|
83
|
+
status: number;
|
|
84
|
+
/** Public, safe-to-expose summary. Sent to clients and shown in the terminal. */
|
|
85
|
+
message: string;
|
|
86
|
+
/** Operator remediation hint. Becomes the CLI action line. Never sent to a client. */
|
|
87
|
+
action?: string;
|
|
88
|
+
/** Internal context for logs + audit. NEVER serialized to clients — the HTTP codec strips it. */
|
|
89
|
+
detail?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Values a translating client interpolates into its own wording for this code. Client-facing:
|
|
92
|
+
* these cross the boundary with `message`, unlike `action` and `detail`.
|
|
93
|
+
*
|
|
94
|
+
* Stated here because this parameter type is **hand-written**, not derived from the schema, and
|
|
95
|
+
* the `as ErrorPayloadOf<Code>` below hides the gap from the compiler. A field left off this
|
|
96
|
+
* literal is not a type error anywhere — it is simply a field an adopter cannot pass.
|
|
97
|
+
*/
|
|
98
|
+
params?: MessageParams;
|
|
99
|
+
} & ReservedDomain<Code>): ErrorPayloadOf<Code>;
|
|
100
|
+
export {};
|
|
101
|
+
//# sourceMappingURL=extend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extend.d.ts","sourceRoot":"","sources":["../../src/error/extend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAMjD;;;;GAIG;AACH,KAAK,cAAc,CAAC,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,GAAG,cAAc,IAAI,MAAM,EAAE,GACjF;IAAE,IAAI,EAAE,2EAA2E,CAAA;CAAE,GACrF,OAAO,CAAC;AAEZ;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACrG,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACrC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAE1C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM,EAAE,EAClE,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,IAAI,GACT,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,CAEjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,IAAI,SAAS,GAAG,MAAM,IAAI,MAAM,EAAE,EACzE,OAAO,EAAE;IACP,uGAAuG;IACvG,IAAI,EAAE,IAAI,CAAC;IACX,qFAAqF;IACrF,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,GAAG,cAAc,CAAC,IAAI,CAAC,GACvB,cAAc,CAAC,IAAI,CAAC,CAiBtB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ExtendedErrorPayload } from "./payload.js";
|
|
2
|
+
import { InternalError } from "./pithyError.js";
|
|
3
|
+
//#region src/error/extend.ts
|
|
4
|
+
/** How much of a rejected code the public message will quote. Enough to debug, not enough to abuse. */
|
|
5
|
+
const QUOTED_CODE_LIMIT = 72;
|
|
6
|
+
/**
|
|
7
|
+
* Narrow a caught payload to one code. `payload.code === "core/not_found"` narrows a kit member on
|
|
8
|
+
* its own, but an adopter's code is branded — the brand is what keeps a kit narrow honest — so the
|
|
9
|
+
* same comparison against a bare literal is a type error, and `Extract` on it is silently `never`.
|
|
10
|
+
* This is the way in for both: one guard, kit codes and adopter codes alike.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* catch (error) {
|
|
14
|
+
* if (error instanceof PithyError && isErrorCode(error.payload, "connect/device_code_expired")) {
|
|
15
|
+
* return restartDeviceFlow();
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function isErrorCode(payload, code) {
|
|
21
|
+
return payload.code === code;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The one door into `ErrorPayload` for a code the kit does not define. An adopter writing a control
|
|
25
|
+
* plane, a device-code flow, or a key-rotation lock has failures the kit has no word for; without
|
|
26
|
+
* this they reuse a kit code that means something else, or stop throwing `PithyError` at all.
|
|
27
|
+
*
|
|
28
|
+
* It is a function rather than a plain object literal for the same reason `defineSecretRegistry` is:
|
|
29
|
+
* the declaration is where the mistake is, so the declaration is where it should fail. A code under
|
|
30
|
+
* one of the kit's domains does not compile — the kit's domains are reserved the way `pithy_` table
|
|
31
|
+
* names are, so nobody redefines `auth/forbidden` with a status of their choosing, and a
|
|
32
|
+
* capability's own typo stays a hard failure instead of becoming somebody's custom error. The parse
|
|
33
|
+
* below is the second gate, for the grammar, the 4xx/5xx bound, and any caller who cast their way
|
|
34
|
+
* past the first.
|
|
35
|
+
*
|
|
36
|
+
* What it does **not** do is pin a status to a code. A kit member fixes one status per code, and
|
|
37
|
+
* that cannot survive a set the kit does not hold: this seam bounds the range and nothing more, so
|
|
38
|
+
* throwing `connect/device_code_expired` as a 410 from one site and a 500 from another passes every
|
|
39
|
+
* layer. Declare each of your codes in one vehicle class, once, and that stays true by construction.
|
|
40
|
+
*
|
|
41
|
+
* It does not move the security boundary either. The payload it returns carries `action` and
|
|
42
|
+
* `detail` exactly like a kit member, and the HTTP codec strips both from every member alike. An
|
|
43
|
+
* adopter cannot opt into leaking, because there is nothing to opt into — the wire shape has no such
|
|
44
|
+
* key to fill, whoever defined the code.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* export class DeviceCodeExpiredError extends PithyError {
|
|
48
|
+
* declare readonly payload: ErrorPayloadOf<"connect/device_code_expired">;
|
|
49
|
+
*
|
|
50
|
+
* constructor(detail?: string) {
|
|
51
|
+
* super(defineErrorPayload({
|
|
52
|
+
* code: "connect/device_code_expired",
|
|
53
|
+
* status: 410,
|
|
54
|
+
* message: "That device code has expired.",
|
|
55
|
+
* action: "Run pithy dashboard connect again.",
|
|
56
|
+
* detail,
|
|
57
|
+
* }));
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
function defineErrorPayload(payload) {
|
|
63
|
+
const parsed = ExtendedErrorPayload.safeParse(payload);
|
|
64
|
+
if (!parsed.success) throw new InternalError({
|
|
65
|
+
message: `error payload: "${quoteCode(payload.code)}" (${payload.status}) is not a usable adopter error code.`,
|
|
66
|
+
action: "Use `<your_domain>/<reason>` in lower snake_case, a 4xx or 5xx status, and a domain the kit does not own.",
|
|
67
|
+
detail: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
68
|
+
});
|
|
69
|
+
return parsed.data;
|
|
70
|
+
}
|
|
71
|
+
/** The offending code, bounded, for an author-error message that reaches a client. */
|
|
72
|
+
function quoteCode(code) {
|
|
73
|
+
return code.length <= QUOTED_CODE_LIMIT ? code : `${code.slice(0, QUOTED_CODE_LIMIT)}…`;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { defineErrorPayload, isErrorCode };
|