@pithy-sh/core 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/dist/access/access.d.ts +172 -0
  2. package/dist/access/access.d.ts.map +1 -0
  3. package/dist/access/access.js +147 -0
  4. package/dist/access/acting.d.ts +49 -0
  5. package/dist/access/acting.d.ts.map +1 -0
  6. package/dist/access/acting.js +21 -0
  7. package/dist/access/policyCheck.d.ts +33 -0
  8. package/dist/access/policyCheck.d.ts.map +1 -0
  9. package/dist/access/policyCheck.js +48 -0
  10. package/dist/access/usage.d.ts +83 -0
  11. package/dist/access/usage.d.ts.map +1 -0
  12. package/dist/access/usage.js +104 -0
  13. package/dist/access/visibility.d.ts +66 -0
  14. package/dist/access/visibility.d.ts.map +1 -0
  15. package/dist/access/visibility.js +86 -0
  16. package/dist/audit/auditEvent.d.ts +5 -0
  17. package/dist/audit/auditEvent.d.ts.map +1 -1
  18. package/dist/audit/auditEvent.js +2 -0
  19. package/dist/capability/bindings.d.ts +47 -2
  20. package/dist/capability/bindings.d.ts.map +1 -1
  21. package/dist/capability/bindings.js +117 -7
  22. package/dist/capability/capability.d.ts +98 -28
  23. package/dist/capability/capability.d.ts.map +1 -1
  24. package/dist/capability/manifest.d.ts +2 -1
  25. package/dist/capability/manifest.d.ts.map +1 -1
  26. package/dist/capability/manifest.js +1 -1
  27. package/dist/capability/settings.d.ts +86 -2
  28. package/dist/capability/settings.d.ts.map +1 -1
  29. package/dist/capability/settings.js +80 -1
  30. package/dist/cli/commandCatalog.generated.d.ts +184 -22
  31. package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
  32. package/dist/cli/commandCatalog.generated.js +196 -22
  33. package/dist/cli/commandFacts.d.ts +1 -1
  34. package/dist/cli/commandFacts.d.ts.map +1 -1
  35. package/dist/cli/commandFacts.js +1 -0
  36. package/dist/controlPlane/capability.d.ts.map +1 -1
  37. package/dist/controlPlane/capability.js +12 -2
  38. package/dist/controlPlane/context.d.ts +5 -0
  39. package/dist/controlPlane/context.d.ts.map +1 -1
  40. package/dist/controlPlane/context.js +3 -1
  41. package/dist/controlPlane/data/connection.d.ts +47 -0
  42. package/dist/controlPlane/data/connection.d.ts.map +1 -1
  43. package/dist/controlPlane/data/connection.js +55 -1
  44. package/dist/controlPlane/http/cors.d.ts +4 -4
  45. package/dist/controlPlane/http/cors.js +4 -4
  46. package/dist/controlPlane/http/guard.d.ts +11 -0
  47. package/dist/controlPlane/http/guard.d.ts.map +1 -1
  48. package/dist/controlPlane/http/guard.js +17 -1
  49. package/dist/controlPlane/http/handlers.d.ts +60 -0
  50. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  51. package/dist/controlPlane/http/handlers.js +31 -2
  52. package/dist/controlPlane/http/readiness.d.ts +33 -0
  53. package/dist/controlPlane/http/readiness.d.ts.map +1 -0
  54. package/dist/controlPlane/http/readiness.js +39 -0
  55. package/dist/controlPlane/http/responses.d.ts +147 -0
  56. package/dist/controlPlane/http/responses.d.ts.map +1 -1
  57. package/dist/controlPlane/http/responses.js +61 -4
  58. package/dist/controlPlane/http/routes.d.ts +1 -0
  59. package/dist/controlPlane/http/routes.d.ts.map +1 -1
  60. package/dist/controlPlane/http/routes.js +17 -3
  61. package/dist/controlPlane/http/verify.d.ts.map +1 -1
  62. package/dist/controlPlane/http/verify.js +2 -1
  63. package/dist/controlPlane/migrationState/migrationState.d.ts +102 -0
  64. package/dist/controlPlane/migrationState/migrationState.d.ts.map +1 -0
  65. package/dist/controlPlane/migrationState/migrationState.js +142 -0
  66. package/dist/controlPlane/migrations/0003_connection_principal.d.ts +21 -0
  67. package/dist/controlPlane/migrations/0003_connection_principal.d.ts.map +1 -0
  68. package/dist/controlPlane/migrations/0003_connection_principal.js +33 -0
  69. package/dist/controlPlane/scope/scope.d.ts +13 -2
  70. package/dist/controlPlane/scope/scope.d.ts.map +1 -1
  71. package/dist/controlPlane/scope/scope.js +14 -3
  72. package/dist/createBackend.d.ts.map +1 -1
  73. package/dist/createBackend.js +7 -2
  74. package/dist/createEntrypoint.d.ts.map +1 -1
  75. package/dist/createEntrypoint.js +5 -3
  76. package/dist/entitlement/allowance.d.ts +25 -0
  77. package/dist/entitlement/allowance.d.ts.map +1 -0
  78. package/dist/entitlement/allowance.js +80 -0
  79. package/dist/entitlement/entitlement.d.ts +41 -0
  80. package/dist/entitlement/entitlement.d.ts.map +1 -1
  81. package/dist/entitlement/meter.d.ts +57 -0
  82. package/dist/entitlement/meter.d.ts.map +1 -0
  83. package/dist/entitlement/meter.js +13 -0
  84. package/dist/error/http.d.ts +144 -0
  85. package/dist/error/http.d.ts.map +1 -1
  86. package/dist/error/payload.d.ts +288 -0
  87. package/dist/error/payload.d.ts.map +1 -1
  88. package/dist/error/payload.js +100 -4
  89. package/dist/http/authContext.d.ts +7 -0
  90. package/dist/http/authContext.d.ts.map +1 -1
  91. package/dist/http/authContext.js +8 -1
  92. package/dist/http/scopeGates.d.ts +20 -0
  93. package/dist/http/scopeGates.d.ts.map +1 -0
  94. package/dist/http/scopeGates.js +36 -0
  95. package/dist/index.d.ts +1 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +2 -2
  98. package/dist/logger/record.d.ts +84 -0
  99. package/dist/logger/record.d.ts.map +1 -1
  100. package/dist/migrations/capabilitySets.d.ts +23 -0
  101. package/dist/migrations/capabilitySets.d.ts.map +1 -0
  102. package/dist/migrations/capabilitySets.js +74 -0
  103. package/dist/migrations/registry.d.ts +26 -0
  104. package/dist/migrations/registry.d.ts.map +1 -1
  105. package/dist/migrations/registry.js +38 -7
  106. package/dist/migrations/state.d.ts +60 -0
  107. package/dist/migrations/state.d.ts.map +1 -0
  108. package/dist/migrations/state.js +46 -0
  109. package/dist/naming/feature.d.ts +27 -5
  110. package/dist/naming/feature.d.ts.map +1 -1
  111. package/dist/naming/feature.js +36 -6
  112. package/dist/naming/limits.d.ts +47 -3
  113. package/dist/naming/limits.d.ts.map +1 -1
  114. package/dist/naming/limits.js +47 -3
  115. package/dist/naming/provisionScope.d.ts +6 -0
  116. package/dist/naming/provisionScope.d.ts.map +1 -1
  117. package/dist/naming/provisionScope.js +8 -3
  118. package/dist/naming/resourceNames.d.ts +21 -2
  119. package/dist/naming/resourceNames.d.ts.map +1 -1
  120. package/dist/naming/resourceNames.js +21 -3
  121. package/dist/seed/writeD1.d.ts +7 -1
  122. package/dist/seed/writeD1.d.ts.map +1 -1
  123. package/dist/seed/writeD1.js +9 -3
  124. package/dist/seed/writeKv.d.ts +2 -0
  125. package/dist/seed/writeKv.d.ts.map +1 -1
  126. package/dist/seed/writeKv.js +6 -2
  127. package/dist/version.generated.d.ts +1 -1
  128. package/dist/version.generated.js +1 -1
  129. package/dist/worker/serviceLoopback.d.ts +36 -0
  130. package/dist/worker/serviceLoopback.d.ts.map +1 -0
  131. package/dist/worker/serviceLoopback.js +74 -0
  132. package/dist/workflow/faults.d.ts +12 -1
  133. package/dist/workflow/faults.d.ts.map +1 -1
  134. package/dist/workflow/faults.js +22 -15
  135. package/package.json +2 -2
  136. package/src/access/access.ts +298 -0
  137. package/src/access/acting.ts +59 -0
  138. package/src/access/policyCheck.ts +77 -0
  139. package/src/access/usage.ts +141 -0
  140. package/src/access/visibility.ts +109 -0
  141. package/src/audit/auditEvent.ts +4 -0
  142. package/src/capability/bindings.ts +159 -13
  143. package/src/capability/capability.ts +99 -29
  144. package/src/capability/settings.ts +146 -2
  145. package/src/cli/commandCatalog.generated.ts +212 -22
  146. package/src/cli/commandFacts.ts +1 -0
  147. package/src/controlPlane/capability.ts +25 -1
  148. package/src/controlPlane/context.ts +4 -0
  149. package/src/controlPlane/data/connection.ts +90 -0
  150. package/src/controlPlane/http/cors.ts +4 -4
  151. package/src/controlPlane/http/guard.ts +21 -0
  152. package/src/controlPlane/http/handlers.ts +41 -0
  153. package/src/controlPlane/http/readiness.ts +50 -0
  154. package/src/controlPlane/http/responses.ts +140 -2
  155. package/src/controlPlane/http/routes.ts +25 -1
  156. package/src/controlPlane/http/verify.ts +2 -0
  157. package/src/controlPlane/migrationState/migrationState.ts +200 -0
  158. package/src/controlPlane/migrations/0003_connection_principal.ts +37 -0
  159. package/src/controlPlane/scope/scope.ts +14 -2
  160. package/src/createBackend.ts +24 -6
  161. package/src/createEntrypoint.ts +9 -3
  162. package/src/entitlement/allowance.ts +85 -0
  163. package/src/entitlement/entitlement.ts +38 -0
  164. package/src/entitlement/meter.ts +69 -0
  165. package/src/error/payload.ts +211 -4
  166. package/src/http/authContext.ts +13 -1
  167. package/src/http/scopeGates.ts +34 -0
  168. package/src/index.ts +1 -0
  169. package/src/migrations/capabilitySets.ts +87 -0
  170. package/src/migrations/registry.ts +46 -2
  171. package/src/migrations/state.ts +94 -0
  172. package/src/naming/feature.ts +46 -7
  173. package/src/naming/limits.ts +35 -3
  174. package/src/naming/provisionScope.ts +19 -1
  175. package/src/naming/resourceNames.ts +52 -5
  176. package/src/seed/writeD1.ts +13 -4
  177. package/src/seed/writeKv.ts +6 -2
  178. package/src/version.generated.ts +1 -1
  179. package/src/worker/serviceLoopback.ts +105 -0
  180. package/src/workflow/faults.ts +51 -24
@@ -49,6 +49,8 @@ export interface ScopedNames {
49
49
  kv(binding: string): string;
50
50
  /** An R2 bucket. Cloudflare's 63 — the one namespace where that number was always right. */
51
51
  r2(binding: string): string;
52
+ /** A Queue. Cloudflare's 63, truncated with a stable hash like a bucket (#662). */
53
+ queue(binding: string): string;
52
54
  /** A Vectorize index. 64 bytes, refused rather than truncated: a renamed index has no vectors. */
53
55
  vectorizeIndex(index: string): string;
54
56
  /** A Secrets Store entry. No Cloudflare cap; Pithy's ceiling. */
@@ -56,22 +58,39 @@ export interface ScopedNames {
56
58
  /** A Cloudflare API token label. No Cloudflare cap; Pithy's ceiling. */
57
59
  apiToken(profile: string): string;
58
60
  }
61
+ /**
62
+ * The names in one declared environment: everything a scope has, and the one name that exists per environment
63
+ * and never at `global`.
64
+ */
65
+ export interface EnvironmentNames extends ScopedNames {
66
+ /**
67
+ * The Artifacts namespace — `<project>-<env>`, with no `<thing>` (#673). One per environment, so a feature's
68
+ * repos never sit beside production's and the per-namespace rate limit splits per environment. There is no
69
+ * global one, because a namespace shared across environments is exactly that mix.
70
+ */
71
+ artifactsNamespace(): string;
72
+ }
59
73
  /** The names a feature environment provisions under, with the project already bound. */
60
74
  export interface FeatureNames {
61
- /** A feature's D1, KV, or R2 resource — `<project>-f<issue>-<slug>-<binding>-<kind>`. */
75
+ /** A feature's D1, KV, R2 or Queue resource — `<project>-f<issue>-<slug>--<binding>-<kind>`. */
62
76
  resource(binding: string, kind: FeatureResourceKind): string;
63
77
  /**
64
78
  * A feature's Worker script — `<project>-f<issue>-<slug>-<app>`, from the `apps/<app>` directory and
65
79
  * never the deploy name, which already leads with the project (#587).
66
80
  */
67
81
  worker(app: string): string;
82
+ /**
83
+ * A feature's Artifacts namespace — `<project>-f<issue>-<slug>--artifacts` (#673). The tail is what makes it a
84
+ * feature name `isFeatureOwnedName` reads back, so teardown reaps this feature's repos and no sibling's.
85
+ */
86
+ artifactsNamespace(): string;
68
87
  }
69
88
  /** Everything a project can be asked to name. Created once, from the project, by {@link resourceNames}. */
70
89
  export interface ProjectNames {
71
90
  /** The kebabbed project name every name below leads with. */
72
91
  readonly project: string;
73
92
  /** The names in one environment. Throws if the environment is not one this scheme accepts. */
74
- env(environment: string): ScopedNames;
93
+ env(environment: string): EnvironmentNames;
75
94
  /** The names shared by every environment — the `global` scope, without typing the word. */
76
95
  readonly global: ScopedNames;
77
96
  /** The names a feature environment provisions under. */
@@ -1 +1 @@
1
- {"version":3,"file":"resourceNames.d.ts","sourceRoot":"","sources":["../../src/naming/resourceNames.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAA0C,MAAM,WAAW,CAAC;AAInH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,sGAAsG;IACtG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,uFAAuF;IACvF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kFAAkF;IAClF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,4FAA4F;IAC5F,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kGAAkG;IAClG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAC7D;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED,2GAA2G;AAC3G,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAAC;IACtC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wDAAwD;IACxD,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC;CACnE;AA8DD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAmB3D"}
1
+ {"version":3,"file":"resourceNames.d.ts","sourceRoot":"","sources":["../../src/naming/resourceNames.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAIzB,MAAM,WAAW,CAAC;AAInB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,sGAAsG;IACtG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,uFAAuF;IACvF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kFAAkF;IAClF,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,4FAA4F;IAC5F,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,mFAAmF;IACnF,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,kGAAkG;IAClG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,iEAAiE;IACjE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,CAAC;CAC9B;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAC7D;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,IAAI,MAAM,CAAC;CAC9B;AAED,2GAA2G;AAC3G,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC3C,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wDAAwD;IACxD,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,YAAY,CAAC;CACnE;AA+ED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAuB3D"}
@@ -4,7 +4,7 @@ import { InternalError, ValidationError } from "../error/pithyError.js";
4
4
  import { GLOBAL_SCOPE, assertValidEnvironment } from "./environment.js";
5
5
  import { NAMESPACE_LIMITS } from "./limits.js";
6
6
  import { assertValidProjectName, kebab, resourceName } from "./resource.js";
7
- import { featureResourceName, featureWorkerName } from "./feature.js";
7
+ import { featureArtifactsNamespace, featureResourceName, featureWorkerName } from "./feature.js";
8
8
  import { workflowHostName, workflowScriptName } from "../workflow/naming.js";
9
9
  //#region src/naming/resourceNames.ts
10
10
  /**
@@ -49,6 +49,19 @@ function scopedName(project, scope, thing, limit) {
49
49
  });
50
50
  return name;
51
51
  }
52
+ /**
53
+ * `<project>-<env>`, under the Artifacts namespace rule. The project and environment are both validated by now, and
54
+ * the longest of each compose 34 characters, so the cap is a check rather than a truncation that ever happens.
55
+ */
56
+ function artifactsNamespaceName(project, environment) {
57
+ const limit = NAMESPACE_LIMITS.artifactsNamespace;
58
+ const name = `${project}-${environment}`;
59
+ if (name.length > limit.maxLength || name.length < limit.minLength) throw new InternalError({
60
+ message: `"${name}" does not fit ${limit.label}, which takes ${limit.minLength} to ${limit.maxLength} characters.`,
61
+ action: "Shorten the project or environment name."
62
+ });
63
+ return name;
64
+ }
52
65
  /** The getters for one scope. `global` and every environment share this, so nothing special-cases either. */
53
66
  function scopedNames(project, scope) {
54
67
  return {
@@ -66,6 +79,7 @@ function scopedNames(project, scope) {
66
79
  d1: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.d1),
67
80
  kv: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.kv),
68
81
  r2: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.r2),
82
+ queue: (binding) => scopedName(project, scope, binding, NAMESPACE_LIMITS.queue),
69
83
  vectorizeIndex: (index) => scopedName(project, scope, index, NAMESPACE_LIMITS.vectorizeIndex),
70
84
  secretEntry: (secret) => scopedName(project, scope, secret, NAMESPACE_LIMITS.secretEntry),
71
85
  apiToken: (profile) => scopedName(project, scope, profile, NAMESPACE_LIMITS.apiToken)
@@ -85,7 +99,10 @@ function resourceNames(project) {
85
99
  project: normalized,
86
100
  env(environment) {
87
101
  assertValidEnvironment(environment);
88
- return scopedNames(normalized, environment);
102
+ return {
103
+ ...scopedNames(normalized, environment),
104
+ artifactsNamespace: () => artifactsNamespaceName(normalized, environment)
105
+ };
89
106
  },
90
107
  global: scopedNames(normalized, GLOBAL_SCOPE),
91
108
  feature(identity) {
@@ -95,7 +112,8 @@ function resourceNames(project) {
95
112
  };
96
113
  return {
97
114
  resource: (binding, kind) => featureResourceName(full, binding, kind),
98
- worker: (app) => featureWorkerName(full, app)
115
+ worker: (app) => featureWorkerName(full, app),
116
+ artifactsNamespace: () => featureArtifactsNamespace(full)
99
117
  };
100
118
  }
101
119
  };
@@ -12,8 +12,14 @@ export interface SeedD1Options {
12
12
  export interface SeedD1Result {
13
13
  /** The table seeded. */
14
14
  table: string;
15
- /** The number of rows validated (and, unless `dryRun`, written). */
15
+ /** The number of rows the fixture holds, every one validated. */
16
16
  rows: number;
17
+ /**
18
+ * The number of rows this call inserted. Under `INSERT OR IGNORE` a row whose key is already there is
19
+ * not, so a re-run writes none, and says so here rather than repeating `rows` (#764). Always `0` under
20
+ * `dryRun`.
21
+ */
22
+ written: number;
17
23
  }
18
24
  /**
19
25
  * Seed one D1 group, idempotently and non-destructively.
@@ -1 +1 @@
1
- {"version":3,"file":"writeD1.d.ts","sourceRoot":"","sources":["../../src/seed/writeD1.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;CACd;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,WAAW,CAE/B,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EACf,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,CAAC,CAAC,OAAO,EACjB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA4BvB"}
1
+ {"version":3,"file":"writeD1.d.ts","sourceRoot":"","sources":["../../src/seed/writeD1.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,WAAW,CAE/B,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EACf,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,CAAC,CAAC,OAAO,EACjB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA+BvB"}
@@ -63,12 +63,18 @@ async function seedD1Group(db, group, schema, options = {}) {
63
63
  });
64
64
  if (options.dryRun || encoded.length === 0) return {
65
65
  table: group.table,
66
- rows: encoded.length
66
+ rows: encoded.length,
67
+ written: 0
67
68
  };
68
- for (const chunk of chunkRowsByBoundParameters(encoded, columnsPerRow(encoded))) await db.insertInto(group.table).orIgnore().values(chunk).execute();
69
+ let written = 0n;
70
+ for (const chunk of chunkRowsByBoundParameters(encoded, columnsPerRow(encoded))) {
71
+ const results = await db.insertInto(group.table).orIgnore().values(chunk).execute();
72
+ for (const result of results) written += result.numInsertedOrUpdatedRows ?? 0n;
73
+ }
69
74
  return {
70
75
  table: group.table,
71
- rows: encoded.length
76
+ rows: encoded.length,
77
+ written: Number(written)
72
78
  };
73
79
  }
74
80
  //#endregion
@@ -15,6 +15,8 @@ export interface SeedKvResult {
15
15
  store: string;
16
16
  /** The number of entries validated (and, unless `dryRun`, considered for writing). */
17
17
  entries: number;
18
+ /** The number of entries this call put. An existing key is left alone and not counted (#764). Always `0` under `dryRun`. */
19
+ written: number;
18
20
  }
19
21
  /**
20
22
  * Seed one KV group, idempotently and non-destructively.
@@ -1 +1 @@
1
- {"version":3,"file":"writeKv.d.ts","sourceRoot":"","sources":["../../src/seed/writeKv.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;CACjB;AA4CD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,EAC/F,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACvB,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC3B,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAiBvB"}
1
+ {"version":3,"file":"writeKv.d.ts","sourceRoot":"","sources":["../../src/seed/writeKv.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,4HAA4H;IAC5H,OAAO,EAAE,MAAM,CAAC;CACjB;AA4CD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,EAC/F,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACvB,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC3B,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAmBvB"}
@@ -50,15 +50,19 @@ async function seedKvGroup(store, group, spec, options = {}) {
50
50
  });
51
51
  if (options.dryRun) return {
52
52
  store: group.store,
53
- entries: group.entries.length
53
+ entries: group.entries.length,
54
+ written: 0
54
55
  };
56
+ let written = 0;
55
57
  for (const entry of group.entries) {
56
58
  if (await keyExists(store, entry.key)) continue;
57
59
  await store.put(entry.key, entry.value, entry.metadata !== void 0 ? { metadata: entry.metadata } : {});
60
+ written += 1;
58
61
  }
59
62
  return {
60
63
  store: group.store,
61
- entries: group.entries.length
64
+ entries: group.entries.length,
65
+ written
62
66
  };
63
67
  }
64
68
  //#endregion
@@ -3,5 +3,5 @@
3
3
  /** This package's npm name — the join key against a release feed. */
4
4
  export declare const PACKAGE_NAME = "@pithy-sh/core";
5
5
  /** This package's version, stamped from its own package.json at generation time. */
6
- export declare const PACKAGE_VERSION = "0.10.0";
6
+ export declare const PACKAGE_VERSION = "0.11.0";
7
7
  //# sourceMappingURL=version.generated.d.ts.map
@@ -4,6 +4,6 @@
4
4
  /** This package's npm name — the join key against a release feed. */
5
5
  const PACKAGE_NAME = "@pithy-sh/core";
6
6
  /** This package's version, stamped from its own package.json at generation time. */
7
- const PACKAGE_VERSION = "0.10.0";
7
+ const PACKAGE_VERSION = "0.11.0";
8
8
  //#endregion
9
9
  export { PACKAGE_NAME, PACKAGE_VERSION };
@@ -0,0 +1,36 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * **A service binding under `pithy dev`: the call reaches the local target on its pinned port (#617).**
5
+ *
6
+ * Deployed, `env.APP_B.fetch(request)` is a service binding: Cloudflare runs the call inside the runtime, into
7
+ * the script the binding names. Locally there is no such script. `wrangler dev` would look for one in its own
8
+ * cross-process registry — the one `pithy dev` never relies on — and a Worker started on its own with
9
+ * `pithy dev --app app-a` would find nothing there at all. What does exist is the target itself, on the port its
10
+ * feature pinned for it, and `pithy dev` knows that address before anything starts.
11
+ *
12
+ * So `pithy dev` publishes one var, {@link SERVICE_ORIGINS_VAR}: a JSON map from each service binding this
13
+ * Worker declares to its target's local origin. Under `dev`, and only there, each of those bindings is replaced
14
+ * with a stand-in whose `fetch` sends the same request to that origin. The call site is byte-identical either
15
+ * way. A target that is not running refuses the connection, so the call fails at once as
16
+ * `core/upstream_failed`, naming the binding and the address, rather than hanging on a registry lookup.
17
+ *
18
+ * **Only `dev`, and only what was published.** The var is written by `pithy dev` and by nothing else, and the
19
+ * gate reads `ENVIRONMENT` off the request env as the Workflow loopback does (`workflow/dispatch.ts`): a
20
+ * deployment that lost its `ENVIRONMENT` var never opens it.
21
+ */
22
+ /** The var `pithy dev` publishes: each service binding's local target, as JSON. */
23
+ export declare const SERVICE_ORIGINS_VAR = "PITHY_SERVICE_ORIGINS";
24
+ /** The transport. The runtime's `fetch`, or a test's. */
25
+ export type ServiceLoopbackFetch = (request: Request) => Promise<Response>;
26
+ /** Options for {@link withLocalServices}. */
27
+ export interface LocalServicesOptions {
28
+ /** The transport. Defaults to the runtime's `fetch`. */
29
+ fetch?: ServiceLoopbackFetch;
30
+ }
31
+ /**
32
+ * The env a request runs with: under `pithy dev`, each published service binding replaced with its local
33
+ * stand-in; anywhere else, the env unchanged — the same object, so nothing outside `dev` pays for this.
34
+ */
35
+ export declare function withLocalServices(env: Record<string, unknown>, options?: LocalServicesOptions): Record<string, unknown>;
36
+ //# sourceMappingURL=serviceLoopback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceLoopback.d.ts","sourceRoot":"","sources":["../../src/worker/serviceLoopback.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAa3D,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAgC3E,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,KAAK,CAAC,EAAE,oBAAoB,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB"}
@@ -0,0 +1,74 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { UpstreamError } from "../error/pithyError.js";
4
+ import { commandName } from "../cli/command.js";
5
+ import "./identity.js";
6
+ import { z } from "zod";
7
+ //#region src/worker/serviceLoopback.ts
8
+ /**
9
+ * **A service binding under `pithy dev`: the call reaches the local target on its pinned port (#617).**
10
+ *
11
+ * Deployed, `env.APP_B.fetch(request)` is a service binding: Cloudflare runs the call inside the runtime, into
12
+ * the script the binding names. Locally there is no such script. `wrangler dev` would look for one in its own
13
+ * cross-process registry — the one `pithy dev` never relies on — and a Worker started on its own with
14
+ * `pithy dev --app app-a` would find nothing there at all. What does exist is the target itself, on the port its
15
+ * feature pinned for it, and `pithy dev` knows that address before anything starts.
16
+ *
17
+ * So `pithy dev` publishes one var, {@link SERVICE_ORIGINS_VAR}: a JSON map from each service binding this
18
+ * Worker declares to its target's local origin. Under `dev`, and only there, each of those bindings is replaced
19
+ * with a stand-in whose `fetch` sends the same request to that origin. The call site is byte-identical either
20
+ * way. A target that is not running refuses the connection, so the call fails at once as
21
+ * `core/upstream_failed`, naming the binding and the address, rather than hanging on a registry lookup.
22
+ *
23
+ * **Only `dev`, and only what was published.** The var is written by `pithy dev` and by nothing else, and the
24
+ * gate reads `ENVIRONMENT` off the request env as the Workflow loopback does (`workflow/dispatch.ts`): a
25
+ * deployment that lost its `ENVIRONMENT` var never opens it.
26
+ */
27
+ /** The var `pithy dev` publishes: each service binding's local target, as JSON. */
28
+ const SERVICE_ORIGINS_VAR = "PITHY_SERVICE_ORIGINS";
29
+ /** The one environment the stand-in is made in. */
30
+ const LOCAL = "dev";
31
+ /** What the var carries: binding name to an `http(s)` origin. */
32
+ const ServiceOrigins = z.record(z.string().describe("A service binding's name in the Worker env."), z.url({ protocol: /^https?$/ }).describe("The local origin of the Worker it calls, as `pithy dev` pinned it.")).describe("Each service binding's local target, published by `pithy dev`.");
33
+ /** A stand-in for one service binding: every request sent to `origin`, its path, method, headers and body kept. */
34
+ function localService(binding, origin, send) {
35
+ const target = new URL(origin);
36
+ return { async fetch(input, init) {
37
+ const request = new Request(input, init);
38
+ const url = new URL(request.url);
39
+ url.protocol = target.protocol;
40
+ url.host = target.host;
41
+ try {
42
+ return await send(new Request(url, request));
43
+ } catch (cause) {
44
+ throw new UpstreamError({
45
+ message: `The Worker ${binding} calls is not answering.`,
46
+ action: `Check that it is running under ${commandName("dev")} at ${target.origin}.`,
47
+ detail: `${request.method} ${url.href} failed: ${String(cause)}`
48
+ }, { cause });
49
+ }
50
+ } };
51
+ }
52
+ /**
53
+ * The env a request runs with: under `pithy dev`, each published service binding replaced with its local
54
+ * stand-in; anywhere else, the env unchanged — the same object, so nothing outside `dev` pays for this.
55
+ */
56
+ function withLocalServices(env, options = {}) {
57
+ if (env["ENVIRONMENT"] !== LOCAL) return env;
58
+ const published = env[SERVICE_ORIGINS_VAR];
59
+ if (typeof published !== "string") return env;
60
+ let parsed;
61
+ try {
62
+ parsed = JSON.parse(published);
63
+ } catch {
64
+ return env;
65
+ }
66
+ const origins = ServiceOrigins.safeParse(parsed);
67
+ if (!origins.success || Object.keys(origins.data).length === 0) return env;
68
+ const send = options.fetch ?? ((request) => fetch(request));
69
+ const local = { ...env };
70
+ for (const [binding, origin] of Object.entries(origins.data)) local[binding] = localService(binding, origin, send);
71
+ return local;
72
+ }
73
+ //#endregion
74
+ export { SERVICE_ORIGINS_VAR, withLocalServices };
@@ -102,6 +102,17 @@ export interface DurableStepLike extends WorkflowStepLike {
102
102
  /** Pause the instance for `durationMs`, then resume. */
103
103
  sleep(name: string, durationMs: number): Promise<void>;
104
104
  }
105
+ /** What {@link classifiedSteps} hands back: a pausing runner, and a step that runs exactly once. */
106
+ export interface ClassifiedSteps extends DurableStepLike {
107
+ /**
108
+ * Run a named step with **no retries**, still classified (pithy-sh/pithy#602).
109
+ *
110
+ * For a body that mutates somebody else's system — a provider roll — where a platform re-running the
111
+ * step after an eviction would do it twice. The classification still decides what the instance's
112
+ * failure says; it can no longer decide on a retry, because there is none to give.
113
+ */
114
+ once<T>(name: string, fn: () => Promise<T>): Promise<T>;
115
+ }
105
116
  /**
106
117
  * The platform's terminal-error class — `NonRetryableError` from `cloudflare:workflows`, taken as a
107
118
  * parameter.
@@ -127,5 +138,5 @@ export interface TerminalErrorConstructor {
127
138
  * `runSendBatch`, `runAtRestKeyRotation`) takes this instead with no change: the classification is
128
139
  * declared once, at the entrypoint, and every step below it inherits the decision.
129
140
  */
130
- export declare function classifiedSteps(step: WorkflowStepLike, policy: WorkflowRetryPolicy, terminalError: TerminalErrorConstructor): DurableStepLike;
141
+ export declare function classifiedSteps(step: WorkflowStepLike, policy: WorkflowRetryPolicy, terminalError: TerminalErrorConstructor): ClassifiedSteps;
131
142
  //# sourceMappingURL=faults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"faults.d.ts","sourceRoot":"","sources":["../../src/workflow/faults.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iHAAiH;IACjH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAED,2FAA2F;AAC3F,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,WAAW,EAAE,wBAAwB,CAAC;IACtC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAsDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA0BhG;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtD;;;OAGG;IACH,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,wDAAwD;IACxD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,wBAAwB;IACvC,KAAK,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,mBAAmB,EAC3B,aAAa,EAAE,wBAAwB,GACtC,eAAe,CAqDjB"}
1
+ {"version":3,"file":"faults.d.ts","sourceRoot":"","sources":["../../src/workflow/faults.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,wBAAwB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iHAAiH;IACjH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAED,2FAA2F;AAC3F,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,WAAW,EAAE,wBAAwB,CAAC;IACtC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;CAChB;AAsDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA0BhG;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtD;;;OAGG;IACH,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,wDAAwD;IACxD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAWD,oGAAoG;AACpG,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD;;;;;;OAMG;IACH,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,wBAAwB;IACvC,KAAK,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,mBAAmB,EAC3B,aAAa,EAAE,wBAAwB,GACtC,eAAe,CA2DjB"}
@@ -91,23 +91,30 @@ function classifyWorkflowFault(error, policy) {
91
91
  * declared once, at the entrypoint, and every step below it inherits the decision.
92
92
  */
93
93
  function classifiedSteps(step, policy, terminalError) {
94
+ const classify = (fn) => async () => {
95
+ try {
96
+ return await fn();
97
+ } catch (error) {
98
+ const fault = classifyWorkflowFault(error, policy);
99
+ if (fault.disposition === "retry") throw error;
100
+ const terminal = new terminalError(encodeWorkflowStepMessage({
101
+ code: fault.code,
102
+ message: messageOf(error),
103
+ action: pithyActionOf(error)
104
+ }));
105
+ terminal.cause = error;
106
+ throw terminal;
107
+ }
108
+ };
94
109
  const runner = {
95
110
  do(name, fn) {
96
- return step.do(name, async () => {
97
- try {
98
- return await fn();
99
- } catch (error) {
100
- const fault = classifyWorkflowFault(error, policy);
101
- if (fault.disposition === "retry") throw error;
102
- const terminal = new terminalError(encodeWorkflowStepMessage({
103
- code: fault.code,
104
- message: messageOf(error),
105
- action: pithyActionOf(error)
106
- }));
107
- terminal.cause = error;
108
- throw terminal;
109
- }
110
- });
111
+ return step.do(name, classify(fn));
112
+ },
113
+ once(name, fn) {
114
+ return step.do(name, { retries: {
115
+ limit: 0,
116
+ delay: 0
117
+ } }, classify(fn));
111
118
  },
112
119
  /**
113
120
  * Forwarded, and deliberately unclassified. A pause raises no fault of the capability's — the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pithy-sh/core",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "scripts": {
28
28
  "build": "tsdown && tsc -p tsconfig.build.json && bun ../../tooling/build/src/stampDeclarations.ts",
29
- "typecheck": "tsc -p tsconfig.json",
29
+ "typecheck": "bun check -p tsconfig.json",
30
30
  "test": "vitest run",
31
31
  "test:node": "vitest run --project=node",
32
32
  "test:workers": "vitest run --project=workers",