effect-jobs 0.1.0-alpha.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.
- package/CHANGELOG.md +16 -0
- package/LICENSE +22 -0
- package/README.md +142 -0
- package/dist/Job.d.ts +33 -0
- package/dist/Job.d.ts.map +1 -0
- package/dist/Job.js +66 -0
- package/dist/Job.js.map +1 -0
- package/dist/JobConsumer.d.ts +34 -0
- package/dist/JobConsumer.d.ts.map +1 -0
- package/dist/JobConsumer.js +28 -0
- package/dist/JobConsumer.js.map +1 -0
- package/dist/JobContract.d.ts +128 -0
- package/dist/JobContract.d.ts.map +1 -0
- package/dist/JobContract.js +15 -0
- package/dist/JobContract.js.map +1 -0
- package/dist/JobFailure.d.ts +51 -0
- package/dist/JobFailure.d.ts.map +1 -0
- package/dist/JobFailure.js +34 -0
- package/dist/JobFailure.js.map +1 -0
- package/dist/JobId.d.ts +5 -0
- package/dist/JobId.d.ts.map +1 -0
- package/dist/JobId.js +4 -0
- package/dist/JobId.js.map +1 -0
- package/dist/JobIdentity.d.ts +54 -0
- package/dist/JobIdentity.d.ts.map +1 -0
- package/dist/JobIdentity.js +82 -0
- package/dist/JobIdentity.js.map +1 -0
- package/dist/JobLifecycle.d.ts +173 -0
- package/dist/JobLifecycle.d.ts.map +1 -0
- package/dist/JobLifecycle.js +234 -0
- package/dist/JobLifecycle.js.map +1 -0
- package/dist/JobPayload.d.ts +18 -0
- package/dist/JobPayload.d.ts.map +1 -0
- package/dist/JobPayload.js +10 -0
- package/dist/JobPayload.js.map +1 -0
- package/dist/JobPayloadCodec.d.ts +14 -0
- package/dist/JobPayloadCodec.d.ts.map +1 -0
- package/dist/JobPayloadCodec.js +78 -0
- package/dist/JobPayloadCodec.js.map +1 -0
- package/dist/JobPolicy.d.ts +50 -0
- package/dist/JobPolicy.d.ts.map +1 -0
- package/dist/JobPolicy.js +85 -0
- package/dist/JobPolicy.js.map +1 -0
- package/dist/JobProducer.d.ts +14 -0
- package/dist/JobProducer.d.ts.map +1 -0
- package/dist/JobProducer.js +23 -0
- package/dist/JobProducer.js.map +1 -0
- package/dist/JobQueue.d.ts +16 -0
- package/dist/JobQueue.d.ts.map +1 -0
- package/dist/JobQueue.js +16 -0
- package/dist/JobQueue.js.map +1 -0
- package/dist/JobRegistry.d.ts +37 -0
- package/dist/JobRegistry.d.ts.map +1 -0
- package/dist/JobRegistry.js +47 -0
- package/dist/JobRegistry.js.map +1 -0
- package/dist/JobStore.d.ts +348 -0
- package/dist/JobStore.d.ts.map +1 -0
- package/dist/JobStore.js +19 -0
- package/dist/JobStore.js.map +1 -0
- package/dist/JobTelemetry.d.ts +7 -0
- package/dist/JobTelemetry.d.ts.map +1 -0
- package/dist/JobTelemetry.js +61 -0
- package/dist/JobTelemetry.js.map +1 -0
- package/dist/JobTransaction.d.ts +24 -0
- package/dist/JobTransaction.d.ts.map +1 -0
- package/dist/JobTransaction.js +7 -0
- package/dist/JobTransaction.js.map +1 -0
- package/dist/JobWorker.d.ts +24 -0
- package/dist/JobWorker.d.ts.map +1 -0
- package/dist/JobWorker.js +155 -0
- package/dist/JobWorker.js.map +1 -0
- package/dist/JobWorkerRuntime.d.ts +18 -0
- package/dist/JobWorkerRuntime.d.ts.map +1 -0
- package/dist/JobWorkerRuntime.js +5 -0
- package/dist/JobWorkerRuntime.js.map +1 -0
- package/dist/PollingJobWorker.d.ts +9 -0
- package/dist/PollingJobWorker.d.ts.map +1 -0
- package/dist/PollingJobWorker.js +54 -0
- package/dist/PollingJobWorker.js.map +1 -0
- package/dist/PostgreSqlDrizzleSchema.d.ts +432 -0
- package/dist/PostgreSqlDrizzleSchema.d.ts.map +1 -0
- package/dist/PostgreSqlDrizzleSchema.js +72 -0
- package/dist/PostgreSqlDrizzleSchema.js.map +1 -0
- package/dist/PostgreSqlJobs.d.ts +25 -0
- package/dist/PostgreSqlJobs.d.ts.map +1 -0
- package/dist/PostgreSqlJobs.js +38 -0
- package/dist/PostgreSqlJobs.js.map +1 -0
- package/dist/PostgreSqlSchema.d.ts +30 -0
- package/dist/PostgreSqlSchema.d.ts.map +1 -0
- package/dist/PostgreSqlSchema.js +63 -0
- package/dist/PostgreSqlSchema.js.map +1 -0
- package/dist/PostgreSqlTransaction.d.ts +42 -0
- package/dist/PostgreSqlTransaction.d.ts.map +1 -0
- package/dist/PostgreSqlTransaction.js +9 -0
- package/dist/PostgreSqlTransaction.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/JobTransaction.d.ts +16 -0
- package/dist/internal/JobTransaction.d.ts.map +1 -0
- package/dist/internal/JobTransaction.js +21 -0
- package/dist/internal/JobTransaction.js.map +1 -0
- package/dist/internal/codec/CanonicalJson.d.ts +9 -0
- package/dist/internal/codec/CanonicalJson.d.ts.map +1 -0
- package/dist/internal/codec/CanonicalJson.js +184 -0
- package/dist/internal/codec/CanonicalJson.js.map +1 -0
- package/dist/internal/codec/ProtectedProjection.d.ts +12 -0
- package/dist/internal/codec/ProtectedProjection.d.ts.map +1 -0
- package/dist/internal/codec/ProtectedProjection.js +121 -0
- package/dist/internal/codec/ProtectedProjection.js.map +1 -0
- package/dist/internal/lifecycle/Artifacts.d.ts +6 -0
- package/dist/internal/lifecycle/Artifacts.d.ts.map +1 -0
- package/dist/internal/lifecycle/Artifacts.js +49 -0
- package/dist/internal/lifecycle/Artifacts.js.map +1 -0
- package/dist/internal/lifecycle/Reconciliation.d.ts +82 -0
- package/dist/internal/lifecycle/Reconciliation.d.ts.map +1 -0
- package/dist/internal/lifecycle/Reconciliation.js +98 -0
- package/dist/internal/lifecycle/Reconciliation.js.map +1 -0
- package/dist/internal/postgresql/Producer.d.ts +14 -0
- package/dist/internal/postgresql/Producer.d.ts.map +1 -0
- package/dist/internal/postgresql/Producer.js +55 -0
- package/dist/internal/postgresql/Producer.js.map +1 -0
- package/dist/internal/postgresql/Readiness.d.ts +5 -0
- package/dist/internal/postgresql/Readiness.d.ts.map +1 -0
- package/dist/internal/postgresql/Readiness.js +53 -0
- package/dist/internal/postgresql/Readiness.js.map +1 -0
- package/dist/internal/postgresql/Rows.d.ts +45 -0
- package/dist/internal/postgresql/Rows.d.ts.map +1 -0
- package/dist/internal/postgresql/Rows.js +75 -0
- package/dist/internal/postgresql/Rows.js.map +1 -0
- package/dist/internal/postgresql/Store.d.ts +5 -0
- package/dist/internal/postgresql/Store.d.ts.map +1 -0
- package/dist/internal/postgresql/Store.js +211 -0
- package/dist/internal/postgresql/Store.js.map +1 -0
- package/dist/internal/worker/Capability.d.ts +106 -0
- package/dist/internal/worker/Capability.d.ts.map +1 -0
- package/dist/internal/worker/Capability.js +11 -0
- package/dist/internal/worker/Capability.js.map +1 -0
- package/dist/internal/worker/Drain.d.ts +22 -0
- package/dist/internal/worker/Drain.d.ts.map +1 -0
- package/dist/internal/worker/Drain.js +59 -0
- package/dist/internal/worker/Drain.js.map +1 -0
- package/dist/internal/worker/Execution.d.ts +7 -0
- package/dist/internal/worker/Execution.d.ts.map +1 -0
- package/dist/internal/worker/Execution.js +125 -0
- package/dist/internal/worker/Execution.js.map +1 -0
- package/dist/internal/worker/Results.d.ts +108 -0
- package/dist/internal/worker/Results.d.ts.map +1 -0
- package/dist/internal/worker/Results.js +3 -0
- package/dist/internal/worker/Results.js.map +1 -0
- package/package.json +169 -0
- package/specs/architecture-decisions.md +841 -0
- package/specs/core-contract.md +323 -0
- package/specs/drizzle-compatibility.md +167 -0
- package/specs/postgresql-backend.md +291 -0
- package/specs/release-qualification.md +107 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Shared core contracts
|
|
2
|
+
|
|
3
|
+
Implemented serial prerequisite for UPVE-1031, against accepted D1–D8 at
|
|
4
|
+
`0e10e8a5a0b21101c00ea064e87ac641b73743c3`. The earlier
|
|
5
|
+
`core-contract-proposal.md` is historical, not a merge input. This document freezes
|
|
6
|
+
the contracts consumed by definitions/producer, lifecycle/store and codec units.
|
|
7
|
+
It does not implement or qualify their runtimes or activate those tasks.
|
|
8
|
+
|
|
9
|
+
## Modules and import directions
|
|
10
|
+
|
|
11
|
+
The package exports these exact public subpaths (ESM and declarations):
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
effect-jobs/JobId
|
|
15
|
+
effect-jobs/JobIdentity
|
|
16
|
+
effect-jobs/JobPolicy
|
|
17
|
+
effect-jobs/JobFailure
|
|
18
|
+
effect-jobs/JobContract
|
|
19
|
+
effect-jobs/JobTransaction
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The root remains empty, not an all-modules barrel. No subpath imports auth,
|
|
23
|
+
PostgreSQL, Drizzle, environment configuration, pools, workers or registry Layers.
|
|
24
|
+
No import starts work. The backend must not enter core through a default Layer.
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Effect <- JobId / JobIdentity / JobPolicy
|
|
28
|
+
JobPolicy <- JobFailure
|
|
29
|
+
JobId + JobIdentity + JobPolicy <- JobContract
|
|
30
|
+
JobContract <- JobTransaction (public opaque capability and errors)
|
|
31
|
+
JobContract + JobTransaction <- internal/JobTransaction (lifetime mechanics)
|
|
32
|
+
JobContract + JobIdentity + JobPolicy + internal/JobTransaction <- later Job/JobProducer
|
|
33
|
+
JobContract <- later JobPayload/JobPayloadCodec
|
|
34
|
+
JobContract + JobFailure + JobPolicy <- later JobStore/internal/JobLifecycle
|
|
35
|
+
shared contracts + qualified active application handle <- later backend bridges
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`JobTransaction` has only a **type-only** reference to the internal symbol; the
|
|
39
|
+
internal leaf imports its public errors at runtime. There is no runtime cycle.
|
|
40
|
+
Only the later definition implementation and qualified backend bridges consume
|
|
41
|
+
the internal leaf. `internal/*` is not a package export or custom-adapter API.
|
|
42
|
+
The transaction declaration references the internal symbol declaration; that file
|
|
43
|
+
must remain in the build artifact even though package export resolution denies
|
|
44
|
+
runtime deep imports. Do not duplicate symbols, brands or errors in later units.
|
|
45
|
+
|
|
46
|
+
## Identity and synchronous declaration validation
|
|
47
|
+
|
|
48
|
+
`JobId.JobId` is a nonempty string Schema branded `effect-jobs/JobId`; the Schema
|
|
49
|
+
and its inferred type share the name. IDs are library-assigned; their generation
|
|
50
|
+
and uniqueness are backend obligations. No UUID algorithm or caller ID override
|
|
51
|
+
is selected here. Schema decoding brands persisted IDs; it is not production.
|
|
52
|
+
|
|
53
|
+
`JobIdentity` exports Schemas `QueueName`, `JobKind`, `JobVersion`, `OperationName`,
|
|
54
|
+
`SlotName`, `OperationId`, `CatalogIdentity` and `ProducerIdentity`. The last two
|
|
55
|
+
also have literal-preserving structural interfaces:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
interface CatalogIdentity<
|
|
59
|
+
Q extends string = string,
|
|
60
|
+
K extends string = string,
|
|
61
|
+
V extends number = number
|
|
62
|
+
> {
|
|
63
|
+
readonly queue: Q
|
|
64
|
+
readonly kind: K
|
|
65
|
+
readonly version: V
|
|
66
|
+
}
|
|
67
|
+
interface ProducerIdentity<O extends string = string, S extends string = string> {
|
|
68
|
+
readonly operation: O
|
|
69
|
+
readonly operationId: string
|
|
70
|
+
readonly slot: S
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Queue names are 1–64 characters; kind/operation/slot names 1–128, all ASCII
|
|
75
|
+
`^[a-z][a-z0-9.-]*$`. Version is integer 1–32,767. These preserve source bounds.
|
|
76
|
+
Operation IDs are valid scalar strings without NUL, 1–256 UTF-8 bytes, with exact
|
|
77
|
+
equality: no trimming, normalization or case folding. Application-branded strings
|
|
78
|
+
are accepted. Durable collation/equality must preserve this behavior.
|
|
79
|
+
|
|
80
|
+
`validateOperationId(value: string): void` throws `InvalidOperationId` with only
|
|
81
|
+
`rule: "unicode-scalars" | "no-nul" | "utf8-length"` and length-rule
|
|
82
|
+
`byteLength`. It never includes the supplied ID. Untrusted non-string values use
|
|
83
|
+
Schema validation at the consumer boundary; do not export raw Schema diagnostics
|
|
84
|
+
containing sensitive inputs as enqueue errors.
|
|
85
|
+
|
|
86
|
+
`validateProducerDeclaration({ operation: string, slots: ReadonlyArray<string> }):
|
|
87
|
+
void` validates names, nonempty slots and uniqueness synchronously. It throws
|
|
88
|
+
`JobDeclarationError` containing `field` (`operation`, `slots`, or `slots[index]`),
|
|
89
|
+
the invalid declaration `value` where applicable, and actionable `expected` text.
|
|
90
|
+
The producer unit **must call this before returning its immutable declaration**;
|
|
91
|
+
there is no producer implementation here. It must preserve inferred operation and
|
|
92
|
+
slot literals, reject undeclared slots, and copy/freeze its slot array. No global
|
|
93
|
+
registry or mutable builder is needed.
|
|
94
|
+
|
|
95
|
+
## Complete immutable policy
|
|
96
|
+
|
|
97
|
+
Use `import * as JobPolicy from "effect-jobs/JobPolicy"` and
|
|
98
|
+
`JobPolicy.make(options?: JobPolicyOptions): JobPolicy.JobPolicy`.
|
|
99
|
+
Every public input field is optional:
|
|
100
|
+
|
|
101
|
+
| Field | Input | Default |
|
|
102
|
+
| ------------------ | ------------------------------------ | --------------- |
|
|
103
|
+
| leaseDuration | Effect Duration | 90 seconds |
|
|
104
|
+
| attemptTimeout | Effect Duration | 30 seconds |
|
|
105
|
+
| retryDelay | Effect Duration | fixed 5 seconds |
|
|
106
|
+
| maxAttempts | integer | 3 |
|
|
107
|
+
| maxStalledCount | integer | 1 |
|
|
108
|
+
| completedRetention | Effect Duration or Duration.infinity | 90 days |
|
|
109
|
+
| deadRetention | Effect Duration or Duration.infinity | infinity |
|
|
110
|
+
|
|
111
|
+
No raw millisecond input or hand-written `_tag` is required publicly. `make` is a
|
|
112
|
+
synchronous configuration factory and throws `JobPolicyConfigurationError` with
|
|
113
|
+
the exact `field` and `expected` constraint. Counters retain source limits:
|
|
114
|
+
maxAttempts 1–100, maxStalledCount 0–100. Finite durations must resolve to integer
|
|
115
|
+
milliseconds 1–253,402,300,799,999; no rounding, truncation or clamping. Zero,
|
|
116
|
+
negative, fractional-millisecond and negative-infinity retention are rejected.
|
|
117
|
+
Only positive infinity is Forever, and only for retention.
|
|
118
|
+
|
|
119
|
+
`attemptTimeoutMillis < leaseDurationMillis` is mandatory. Response budget is
|
|
120
|
+
backend configuration, not a policy field; later backend qualification must
|
|
121
|
+
ensure the stored lease can support timeout plus its budget.
|
|
122
|
+
|
|
123
|
+
The returned object and every nested configuration object are frozen:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
interface JobPolicy {
|
|
127
|
+
readonly leaseDurationMillis: number
|
|
128
|
+
readonly attemptTimeoutMillis: number
|
|
129
|
+
readonly maxAttempts: number
|
|
130
|
+
readonly maxStalledCount: number
|
|
131
|
+
readonly retrySchedule: { readonly _tag: "FixedDelayV1"; readonly delayMillis: number }
|
|
132
|
+
readonly completedRetention: JobRetention
|
|
133
|
+
readonly deadRetention: JobRetention
|
|
134
|
+
}
|
|
135
|
+
type JobRetention =
|
|
136
|
+
{ readonly _tag: "Forever" } | { readonly _tag: "Duration"; readonly millis: number }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`PersistedJobPolicy`, `FixedDelayV1`, `JobRetention`, `PositiveMillis` and
|
|
140
|
+
`EpochMillis` are runtime Schemas. Persisted decode requires the **complete**
|
|
141
|
+
resolved configuration and the cross-field constraint; it never fills current
|
|
142
|
+
defaults into old rows. The layout is the V1 policy representation; concrete
|
|
143
|
+
backend storage/version discriminator belongs to its schema task. Updated
|
|
144
|
+
deployment defaults cannot rewrite retained rows.
|
|
145
|
+
|
|
146
|
+
`EpochMillis` preserves the source positive integer bound 1–253,402,300,799,999
|
|
147
|
+
for availableAt/notAfter; accepting epoch zero would be a policy change, not
|
|
148
|
+
silently inferred from the superseded proposal. Ordinary past positive timestamps
|
|
149
|
+
are valid. The backend must validate computed timestamp additions against the
|
|
150
|
+
same bound rather than overflowing/clamping. No database-time arithmetic is
|
|
151
|
+
implemented in this unit.
|
|
152
|
+
|
|
153
|
+
## Handler and enqueue contracts
|
|
154
|
+
|
|
155
|
+
`JobContract.HandlerInput<Payload>` is `{ readonly payload: Payload; readonly
|
|
156
|
+
context: HandlerContext }`. Context contains exactly jobId, catalog, producer and
|
|
157
|
+
attemptNumber, all readonly. No client, lease token, transaction, finalizer or
|
|
158
|
+
ambient handler dependency service is exposed.
|
|
159
|
+
|
|
160
|
+
attemptNumber is `attemptsMade + 1`. Claim/release do not increment attempts;
|
|
161
|
+
confirmed fenced Complete/Retry/Dead/Isolate increments exactly once. Unknown
|
|
162
|
+
leaves attempts unchanged; stalled recovery has its own counter. Consequently
|
|
163
|
+
attempt numbers may repeat and are not external idempotency keys or invocation
|
|
164
|
+
counts. Enforcing those transitions belongs to the lifecycle unit.
|
|
165
|
+
|
|
166
|
+
`JobFailure` is a Schema/type union. `JobFailures` supplies checked frozen factories:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
JobFailures.Retry({ code: string, notAfter?: number })
|
|
170
|
+
JobFailures.Dead({ code: string })
|
|
171
|
+
JobFailures.Isolate({ code: string })
|
|
172
|
+
JobFailures.OutcomeUnknown({ code: string })
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Codes are 1–64 characters matching `^[a-z0-9][a-z0-9_-]*$`; cutoffs use
|
|
176
|
+
EpochMillis. Invalid factories throw `InvalidJobFailure { field: "code" |
|
|
177
|
+
"notAfter" }`, without recording input text. Domain errors are mapped by ordinary
|
|
178
|
+
Effect composition. Unknown is not a seventh lifecycle state; mixed causes,
|
|
179
|
+
defects, interruption or timeout never imply known-safe Retry. Runtime boundaries
|
|
180
|
+
must validate even structurally constructed failure values with `JobFailure`.
|
|
181
|
+
|
|
182
|
+
`EnqueueInput<Payload>` has required producer, decoded payload, resolved policy
|
|
183
|
+
and optional availableAt. No job ID is supplied. `EnqueueResults` is a
|
|
184
|
+
`Data.taggedEnum` factory for `Inserted { jobId } | AlreadyPresent { jobId }`.
|
|
185
|
+
Results remain provisional until application commit.
|
|
186
|
+
|
|
187
|
+
For matching producer tuples, duplicate equality compares catalog and validated
|
|
188
|
+
canonical semantic payload/fingerprints only. It excludes generated IDs, policy,
|
|
189
|
+
initial/current availability, ciphertext randomness and lifecycle state. Matching
|
|
190
|
+
duplicates return the original ID and retain first policy/availability. Omitted
|
|
191
|
+
availability uses DB time only on first insertion. Conflicts are typed
|
|
192
|
+
`JobIntegrityConflict` with no raw payload/identifiers; they cannot be swallowed
|
|
193
|
+
to commit partial application writes.
|
|
194
|
+
|
|
195
|
+
`InvalidJobInput { field }` and `JobPayloadCodecError { reason }` are bounded
|
|
196
|
+
operation-owned errors. `JobTransaction.JobEnqueueError` unites those with
|
|
197
|
+
JobIntegrityConflict and JobsTransactionClosed. Provider errors stay in the
|
|
198
|
+
backend's explicit E channel, not raw public diagnostic strings.
|
|
199
|
+
|
|
200
|
+
## Codec and prepared-payload seam
|
|
201
|
+
|
|
202
|
+
`EncodedJobPayload` contains `formatVersion: 1`, `payloadBytes: Uint8Array` and
|
|
203
|
+
`semanticProjectionBytes: Uint8Array`. `PreparedJob` contains catalog, producer,
|
|
204
|
+
complete policy, optional availableAt and encoded. It has no generated candidate
|
|
205
|
+
ID. Readonly byte properties **do not make Uint8Array immutable**: the codec owns
|
|
206
|
+
its output buffers, and a receiver must copy before retaining them. Internal
|
|
207
|
+
forwarding deliberately does not implement serialization or pretend to validate
|
|
208
|
+
fake byte fixtures.
|
|
209
|
+
|
|
210
|
+
Shared constants: payload and projection each 65,536 bytes; maximum depth 32;
|
|
211
|
+
maximum protected subtrees 16; maximum UTF-8 schema path 256 bytes; formatVersion 1.
|
|
212
|
+
The codec implements exported function-type contracts:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
type JobPayloadEncoder = <S extends Schema.Top>(
|
|
216
|
+
schema: S,
|
|
217
|
+
payload: S["Type"]
|
|
218
|
+
) => Effect.Effect<EncodedJobPayload, JobPayloadCodecError, S["EncodingServices"]>
|
|
219
|
+
type JobPayloadDecoder = <S extends Schema.Top>(
|
|
220
|
+
schema: S,
|
|
221
|
+
encoded: EncodedJobPayload
|
|
222
|
+
) => Effect.Effect<S["Type"], JobPayloadCodecError, S["DecodingServices"]>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The later codec exposes matching encode/decode functions; definitions must carry
|
|
226
|
+
encoding services into enqueue and capture decoding/handler services through
|
|
227
|
+
ordinary Layers. No `EncodingServices: never` limitation or hidden provisioning.
|
|
228
|
+
|
|
229
|
+
Protected markers describe application-owned envelope/fingerprint Schemas.
|
|
230
|
+
Require an **own** fingerprint with validated canonical JSON representation;
|
|
231
|
+
semantic projection excludes the remainder of that subtree. Marking proves no
|
|
232
|
+
encryption/authenticity. No projection callbacks, mandated crypto envelope, keys,
|
|
233
|
+
automatic encryption or generic AAD are introduced here. Full marker/codec runtime
|
|
234
|
+
and own-field checks belong to UPVE-857, not a parallel stub in contracts.
|
|
235
|
+
|
|
236
|
+
Codec qualification must cover finite fractional JSON numbers, nonfinite rejects,
|
|
237
|
+
locale-independent object order, meaningful array order, supported structs/arrays/
|
|
238
|
+
optionals/unions and explicit unsupported transformed/recursive mapping. Decode
|
|
239
|
+
validates format, bounds, UTF-8, schema and projection consistency before dispatch.
|
|
240
|
+
Canonical projection comparison is not an authenticity proof.
|
|
241
|
+
|
|
242
|
+
## Scoped explicit transaction seam
|
|
243
|
+
|
|
244
|
+
`JobsTransaction<E = never, R = never>` is an opaque callback capability. It has
|
|
245
|
+
no public constructor, arbitrary insert callback, query client, transaction
|
|
246
|
+
runner, savepoint, commit, rollback or replay. Its symbol is internal only.
|
|
247
|
+
|
|
248
|
+
The internal `withJoinedTransaction(insertOrCompare, body)` is a lifetime helper
|
|
249
|
+
for **qualified backend callbacks**, not a generic public extension point:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
withJoinedTransaction<A, E, R, BackendError, BackendRequirements>(
|
|
253
|
+
insertOrCompare: (prepared: PreparedJob) =>
|
|
254
|
+
Effect.Effect<EnqueueResult, BackendError | JobIntegrityConflict, BackendRequirements>,
|
|
255
|
+
body: (tx: JobsTransaction<BackendError, BackendRequirements>) => Effect.Effect<A, E, R>
|
|
256
|
+
): Effect.Effect<A, E, R>
|
|
257
|
+
|
|
258
|
+
insertPrepared<E, R>(tx: JobsTransaction<E, R>, prepared: PreparedJob):
|
|
259
|
+
Effect.Effect<EnqueueResult, E | JobIntegrityConflict | JobsTransactionClosed, R>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
It allocates a fresh frozen capability per Effect execution, scopes the callback
|
|
263
|
+
and invalidates it at callback exit on success, typed failure, defect, synchronous
|
|
264
|
+
throw or interruption. Deferred/escaped enqueue Effects check liveness when run,
|
|
265
|
+
before invoking the backend. Closed failure carries reason `callback-exited` and
|
|
266
|
+
an actionable message directing enqueue into the joinTransaction callback.
|
|
267
|
+
Callback-scoped fibers are scoped normally; unscoped escaping fibers and concurrent
|
|
268
|
+
transaction operations are not supported. Branding/lifetime checks are atomicity
|
|
269
|
+
correctness, not a sandbox against trusted application code or casts.
|
|
270
|
+
|
|
271
|
+
The backend must bind the supplied exact active connection/source. The helper
|
|
272
|
+
cannot prove connection provenance, insert correctness, rollback or durable
|
|
273
|
+
atomicity, and these tests do not claim that it can. Custom adapters need a later
|
|
274
|
+
explicit qualified backend extension contract, not a public re-export of this
|
|
275
|
+
helper. No Effect SQL/Drizzle/pg compatibility claim exists yet.
|
|
276
|
+
|
|
277
|
+
`JobTransactionsService<ManagerError, ManagerRequirements = never>` is the explicit
|
|
278
|
+
configured manager integration contract, not an implementation or Context tag:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
withTransaction<A, E, R>(
|
|
282
|
+
body: (tx: JobsTransaction<ManagerError>) => Effect.Effect<A, E, R>
|
|
283
|
+
): Effect.Effect<A, E | ManagerError, R | ManagerRequirements>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
A qualified integration delegates join-or-establish to the application's manager;
|
|
287
|
+
it must not instantiate a second engine/pool. Explicit backend join must never
|
|
288
|
+
open a new connection/transaction/savepoint, or independently commit/replay.
|
|
289
|
+
Application owner retains whole-operation commit knowledge and replay authority.
|
|
290
|
+
|
|
291
|
+
## Qualification and ownership
|
|
292
|
+
|
|
293
|
+
Tests under `tests/core` exercise identity diagnostics, defaults and policy bounds,
|
|
294
|
+
strict timeout edge, failure bounds, forwarding and capability lifetimes.
|
|
295
|
+
`tests/types/contracts.ts` is included by the existing strict compiler gate,
|
|
296
|
+
using positive assertions, checked `@ts-expect-error` cases and type-level
|
|
297
|
+
service/error-negative assertions. Tests cover distinct Schema encode/decode
|
|
298
|
+
requirements against real Effect 4.0.0 declarations.
|
|
299
|
+
|
|
300
|
+
Source reference inspected read-only at auth revision
|
|
301
|
+
`5c2e2b81c1aabb102d71894ecb6977611028b78a`: JobQueue, AuthJobs, AuthJobData and
|
|
302
|
+
JobPayloadCodec bounds. Auth remains independent and untouched.
|
|
303
|
+
|
|
304
|
+
This serial unit owns these six public contract modules, their internal
|
|
305
|
+
transaction leaf, tests/core and tests/types/contracts, this specification and
|
|
306
|
+
required package/root export integration. Later units must not edit shared
|
|
307
|
+
contracts, package/lock/config or exports concurrently; request serial changes.
|
|
308
|
+
No dependency/toolchain upgrade, worker, store, producer/registry, full codec,
|
|
309
|
+
generic crypto, migration or backend is included.
|
|
310
|
+
|
|
311
|
+
The current repository has no tracked `.github` workflow or extra gate scripts.
|
|
312
|
+
The actual local CI-equivalent gate is package `verify`: check, lint, whole-tree
|
|
313
|
+
format check, build and tests. Harness-generated untracked files are not owned:
|
|
314
|
+
if whole-tree format fails on them, report that failure and independently check
|
|
315
|
+
tracked/owned files without modifying runtime files. Exact command results,
|
|
316
|
+
tool versions, read manifest and immutable base/head/tree are delivered in the
|
|
317
|
+
issue report; this document does not invent successful future receipts.
|
|
318
|
+
|
|
319
|
+
No PG or packed worker/backend qualification, database operation, push, PR,
|
|
320
|
+
publication, runtime modification or merge is authorized. Temporal runtime edges
|
|
321
|
+
(due `<=`, owned `>`, expiry `<=`, usable lease `>= timeout + budget`, retry cutoff
|
|
322
|
+
`>= notAfter`, one DB-time snapshot) remain accepted obligations of later units;
|
|
323
|
+
contract tests qualify only the policy inequality, not those unimplemented writes.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Effect-native Drizzle compatibility
|
|
2
|
+
|
|
3
|
+
This qualification targets exactly `drizzle-orm@1.0.0-rc.5-169397b`,
|
|
4
|
+
`effect@4.0.0` and the compatible native peer `@effect/sql-pg@4.0.0`, on
|
|
5
|
+
Node 24.15.0, Bun 1.4.2 and PostgreSQL 16.15. The owner accepted this dependency
|
|
6
|
+
direction in UPVE-1032; UPVE-1165 owns its isolated compatibility qualification.
|
|
7
|
+
The coordinator's prepared package/lock changes are part of the tested inputs.
|
|
8
|
+
The existing production schema and backend need no runtime changes for this pin.
|
|
9
|
+
|
|
10
|
+
## Actual driver and application bridge
|
|
11
|
+
|
|
12
|
+
`drizzle-orm/effect-postgres.makeWithDefaults` requires the native `PgClient`
|
|
13
|
+
service. It creates a session using that client; queries are Effects, executed
|
|
14
|
+
through `client.unsafe(...).values`, `.withoutTransform` or `.raw`. The session
|
|
15
|
+
wraps a transaction callback in `client.withTransaction`. A nested Drizzle
|
|
16
|
+
transaction delegates to the session again: native Effect SQL emits a savepoint.
|
|
17
|
+
The executable negative control confirms this on the real server.
|
|
18
|
+
|
|
19
|
+
The qualification application registrar captures the exact connection published
|
|
20
|
+
by **that client's** `transactionService`, inside its owning callback. A private
|
|
21
|
+
WeakMap binds an opaque handle to source object, connection and active lifetime.
|
|
22
|
+
`PostgreSqlJobs.joinTransaction` validates that registration and executes through
|
|
23
|
+
the captured connection. It neither borrows nor calls another native transaction
|
|
24
|
+
wrapper. A separate registrar around the same client is foreign. A Drizzle
|
|
25
|
+
transaction object or structural query object alone is unqualified.
|
|
26
|
+
|
|
27
|
+
Native raw int8 results are bigint. The application query adapter converts those
|
|
28
|
+
top-level values to exact decimal strings, preserving the existing backend row
|
|
29
|
+
contract and its bounds validation. It does not convert arbitrary int8 directly
|
|
30
|
+
to potentially rounded JavaScript numbers. Drizzle's own query mapper handles
|
|
31
|
+
the declared number-mode bigint columns. The existing custom bytea codec accepts
|
|
32
|
+
the native Uint8Array result and copies it; its Buffer parameter encoding and
|
|
33
|
+
binary bytes including zero and values above 127 round-trip on both runtimes.
|
|
34
|
+
|
|
35
|
+
Native SQL control failures can occur as defects because Effect SQL uses `orDie`
|
|
36
|
+
at the control boundary. Native COMMIT checks its actual command tag and rejects
|
|
37
|
+
ROLLBACK. The fixture sanitizes recognized SQL provider reasons to bounded
|
|
38
|
+
`PostgreSqlFailure`, conservatively Unknown for controls, and recognized Drizzle
|
|
39
|
+
query failures to NotCommitted. Caller errors, defects, interruption and other
|
|
40
|
+
reasons in mixed Causes remain caller-owned. It performs no retry or independent
|
|
41
|
+
receipt reconciliation. The outer application manager owns durable commit.
|
|
42
|
+
|
|
43
|
+
This is a concrete qualification application, **not a shipped universal native
|
|
44
|
+
Drizzle adapter**. Applications must supply their actual registered capability
|
|
45
|
+
and the complete documented application adapter contract. The probe does not
|
|
46
|
+
qualify auth's executor, arbitrary native SQL configurations, other native driver
|
|
47
|
+
versions, independent foreign unregistered transaction managers, concurrent
|
|
48
|
+
operations on one connection, or exhaustive native control/network fault recovery.
|
|
49
|
+
It adds no public constructor or changes to the core capability, lifecycle,
|
|
50
|
+
worker, codecs, migrations, pool ownership or accepted D1–D8 decisions.
|
|
51
|
+
|
|
52
|
+
## Executable qualification
|
|
53
|
+
|
|
54
|
+
From the repository, with the private resource directory in
|
|
55
|
+
`D6_RESOURCE_DIRECTORY` and the pinned runtimes on PATH:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
timeout 60s bun install --frozen-lockfile
|
|
59
|
+
# Run each fixture install in its directory, also with --frozen-lockfile.
|
|
60
|
+
timeout 300s bun run build
|
|
61
|
+
export EFFECT_JOBS_ARCHIVE=/path/to/immutable-versioned-effect-jobs.tgz
|
|
62
|
+
timeout 300s node tests/qualification/drizzle-native/Run.mjs
|
|
63
|
+
timeout 600s bun run verify
|
|
64
|
+
timeout 300s bun run check:postgresql
|
|
65
|
+
timeout 300s bun run test:postgresql:node
|
|
66
|
+
timeout 300s bun run test:postgresql:bun
|
|
67
|
+
timeout 600s bun run qualify:postgresql:installed
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The release runner consumes the immutable `EFFECT_JOBS_ARCHIVE`, creates a fresh
|
|
71
|
+
independent consumer, and installs that same archive with npm's normal peer resolver alongside
|
|
72
|
+
the exact pins. It uses no force or legacy-peer-deps. It retains a lockfile,
|
|
73
|
+
archive hash, root manifest hash, logs, consumer declarations and sanitized
|
|
74
|
+
runtime receipts. Each child command is foreground and bounded to 60 seconds.
|
|
75
|
+
It compiles the `.mts` fixture inside that consumer, outside the repository's
|
|
76
|
+
core-only TypeScript include set; no native optional peer becomes a root
|
|
77
|
+
development requirement. The runner prepares an ignored fixture-local dependency
|
|
78
|
+
link for subsequent repository lint in a fresh checkout. All public exports are typechecked and imported on
|
|
79
|
+
Node and Bun. Existing installed qualification separately proves imports without
|
|
80
|
+
pg/Drizzle peers and strict non-Drizzle backend declarations.
|
|
81
|
+
|
|
82
|
+
The 17 native assertions per runtime cover:
|
|
83
|
+
|
|
84
|
+
- Native Drizzle domain + production enqueue/job/payload + receipt atomic commit.
|
|
85
|
+
- Duplicate equality, original durable evidence and caught integrity conflict
|
|
86
|
+
causing actual outer COMMIT rejection, with one callback invocation.
|
|
87
|
+
- Caller Context requirements and custom binary bytea read/write round trips.
|
|
88
|
+
- Real CHECK failures at each of the four insertion boundaries, with zero
|
|
89
|
+
remaining writes and sanitized provider failures.
|
|
90
|
+
- Matching physical backend PID, zero extra joined control statements, inner
|
|
91
|
+
success invisible to an independent observer and outer rejection rollback.
|
|
92
|
+
- A real nested Drizzle savepoint as the negative control.
|
|
93
|
+
- Private registration, foreign source, inactive handles and owned-operation
|
|
94
|
+
rejection inside an active native transaction.
|
|
95
|
+
- Escaped capability rejection after success/failure/defect/interruption and
|
|
96
|
+
interruption after all four writes rolling back the complete operation.
|
|
97
|
+
|
|
98
|
+
The original compatibility source PostgreSQL matrix passed 27 tests per runtime;
|
|
99
|
+
installed production qualification passed 25 real-PG tests per runtime. The alpha
|
|
100
|
+
release adds a public worker pipeline and records its larger final counts separately. The root test gate passes
|
|
101
|
+
388 tests with 47 optional PG tests skipped when it has no PG environment; those
|
|
102
|
+
skips are NotTested in that invocation. The explicit source and installed gates
|
|
103
|
+
above supply the required real-PG evidence. Historical D6 receipts remain tied to
|
|
104
|
+
their original snapshots and were not rerun for confirmation.
|
|
105
|
+
|
|
106
|
+
## Declaration limits and reproducibility
|
|
107
|
+
|
|
108
|
+
Consumer declarations pass with `skipLibCheck: true`, matching the repository's
|
|
109
|
+
existing Drizzle setting. The runner also executes the full upstream declaration
|
|
110
|
+
check with `skipLibCheck: false` and retains its **failure**, rather than claiming
|
|
111
|
+
complete declaration compatibility. In this version Drizzle still imports the
|
|
112
|
+
removed `effect/unstable/sql/SqlError` path; policy/role declarations also fail
|
|
113
|
+
exact optional property checks. The fixture restores only the transaction
|
|
114
|
+
function's error declaration to the actual pinned native `SqlError` at its
|
|
115
|
+
explicit typed invocation boundary. It does not patch installed dependencies or
|
|
116
|
+
hide that full upstream checking failed. Intentionally version-pinned unstable
|
|
117
|
+
SQL API warnings are documented and disabled only in these two qualification
|
|
118
|
+
modules; any/unknown Effect-channel diagnostics remain enabled.
|
|
119
|
+
|
|
120
|
+
The initial fresh-worktree `verify` failed lint before build because an existing
|
|
121
|
+
portable codec smoke imports absent `dist` declarations. Building first resolves
|
|
122
|
+
that bootstrap issue; the subsequent complete `verify` passes. No shared test,
|
|
123
|
+
manifest, root registrar or lint policy was changed to make it pass.
|
|
124
|
+
|
|
125
|
+
The fixtures verify the exact task-owned container and deadline before use.
|
|
126
|
+
Native objects use only the `drizzle_native` schema and `drizzle_native_runtime`
|
|
127
|
+
role in `effect_jobs_d6`; application setup owns DDL and runtime uses its separate
|
|
128
|
+
login. Native client scopes and observer/admin pools close in finally, and both
|
|
129
|
+
runtime receipts confirm zero remaining fixture schemas and roles. Existing
|
|
130
|
+
backend fixtures run serially. Container lifecycle stays with the coordinator.
|
|
131
|
+
Credentials are read only in-process and are absent from argv, Git and receipts.
|
|
132
|
+
|
|
133
|
+
No unresolved architecture decision or additional package dependency is needed
|
|
134
|
+
for the qualified schema and producer path. The coordinator may register the
|
|
135
|
+
new runner as a shared CI/package gate; that registry is outside author ownership.
|
|
136
|
+
Publication, deployment, push, PR, merge and review dispatch remain outside this
|
|
137
|
+
author's scope. Final exact Git and artifact receipts accompany the issue handoff.
|
|
138
|
+
|
|
139
|
+
## Documentation read manifest
|
|
140
|
+
|
|
141
|
+
All 16 recursive ai-docs files were read before implementation:
|
|
142
|
+
|
|
143
|
+
- `ai-docs/README.md`
|
|
144
|
+
- `ai-docs/tsconfig.json`
|
|
145
|
+
- `ai-docs/src/index.md`
|
|
146
|
+
- `ai-docs/src/01_services/10_services-and-layers.ts`
|
|
147
|
+
- `ai-docs/src/01_services/20_module-consumption.ts`
|
|
148
|
+
- `ai-docs/src/01_services/30_layer-composition.ts`
|
|
149
|
+
- `ai-docs/src/02_modeling/10_schema-tagged-match.ts`
|
|
150
|
+
- `ai-docs/src/03_errors/10_errors-and-causes.ts`
|
|
151
|
+
- `ai-docs/src/04_observability/10_operation-observability.ts`
|
|
152
|
+
- `ai-docs/src/05_resources/10_resources-interruption-secrets.ts`
|
|
153
|
+
- `ai-docs/src/06_persistence/10_mutation-boundaries.ts`
|
|
154
|
+
- `ai-docs/src/07_transport/10_wire-errors.ts`
|
|
155
|
+
- `ai-docs/src/08_style/10_visible-code-and-modules.ts`
|
|
156
|
+
- `ai-docs/src/09_testing/10_testing-and-review.ts`
|
|
157
|
+
- `ai-docs/src/10_boundaries/10_external-time-regex.ts`
|
|
158
|
+
- `ai-docs/src/fixtures/Auth.ts`
|
|
159
|
+
|
|
160
|
+
Also read canonical UPVE-1020, assigned issue and required empty thread scan,
|
|
161
|
+
the owner's parent decision thread, `specs/multica-workflow.md`, accepted
|
|
162
|
+
`specs/architecture-decisions.md`, `specs/core-contract.md`,
|
|
163
|
+
`specs/postgresql-backend.md`, `specs/d6-qualification.md`, current manifests,
|
|
164
|
+
TypeScript configuration, production schema/producer/transaction seams, retained
|
|
165
|
+
application-manager fixture, installed gate and actual installed Drizzle/native
|
|
166
|
+
driver/codecs/Effect SQL transaction implementations. The author worktree has no
|
|
167
|
+
repository-owned AGENTS.md. No auth repository or issue was written.
|