@effected/jsonl 0.7.1-dogfood-20260927171341 → 0.9.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/Envelope.js CHANGED
@@ -87,6 +87,23 @@ const completeResult = (events, line, frame) => {
87
87
  * defined **in terms of** the sync ones via `Effect.fromResult`, so the two can
88
88
  * never drift into two implementations of the same rule.
89
89
  *
90
+ * @example
91
+ * ```ts
92
+ * import { Envelope, JsonlEvent, Line } from "@effected/jsonl";
93
+ * import { Result, Schema } from "effect";
94
+ *
95
+ * const events = [JsonlEvent.make("started", { data: Schema.Struct({ id: Schema.String }) })] as const;
96
+ *
97
+ * declare const sourceText: string;
98
+ *
99
+ * for (const line of Line.split(sourceText)) {
100
+ * const decoded = Envelope.decodeResult(events, line);
101
+ * if (Result.isSuccess(decoded) && decoded.success.event === "started") {
102
+ * decoded.success.data.id; // typed from the registered payload schema
103
+ * }
104
+ * }
105
+ * ```
106
+ *
90
107
  * @public
91
108
  */
92
109
  const Envelope = {
@@ -98,6 +115,9 @@ const Envelope = {
98
115
  * filtered out never pays for its payload at all.
99
116
  *
100
117
  * @param line - A slice from `Line.split`.
118
+ * @returns The frame with its source `line` attached, or `MalformedLine`
119
+ * when the line is not JSON, or `InvalidData` when it is JSON that is not
120
+ * an envelope frame.
101
121
  */
102
122
  frameResult: (line) => {
103
123
  const parsed = Line.parseResult(line);
@@ -157,6 +177,10 @@ const Envelope = {
157
177
  *
158
178
  * Same contract as `Line.parseAll` one layer down: a line that is not a
159
179
  * legal envelope is a `Result` in the array, never a gap in it.
180
+ *
181
+ * @param events - The registry to validate against.
182
+ * @param text - JSONL source text.
183
+ * @returns One `Result` per non-blank line, in file order.
160
184
  */
161
185
  decodeAllResult: (events, text) => {
162
186
  const results = [];
@@ -207,6 +231,10 @@ const Envelope = {
207
231
  * @param events - The registry to validate against.
208
232
  * @param envelope - The event tag, its payload, the service-assigned
209
233
  * timestamp, and an optional scope.
234
+ * @returns The encoded line, or `UnknownEvent` for a tag the registry does
235
+ * not define, `InvalidData` when the payload does not satisfy its schema,
236
+ * or `UnserializableData` when the encoded payload cannot be turned into
237
+ * JSON (a bigint or a reference cycle).
210
238
  */
211
239
  encodeResult: (events, envelope) => {
212
240
  const empty = new LineSlice({
@@ -251,14 +279,18 @@ const Envelope = {
251
279
  return Result.succeed(`${text}\n`);
252
280
  },
253
281
  /**
254
- * {@link (Envelope:variable).decodeResult} in the `Effect` channel.
282
+ * `Envelope.decodeResult` in the `Effect` channel.
255
283
  *
256
284
  * Prefer the sync form in a hook or any caller that has no runtime; this
257
- * exists for programs that already are one.
285
+ * exists for programs that already are one. Fails with `MalformedLine`,
286
+ * `InvalidData` or `UnknownEvent`; requires nothing.
258
287
  */
259
288
  decode: (events, line) => Effect.fromResult(Envelope.decodeResult(events, line)),
260
289
  /**
261
- * {@link (Envelope:variable).encodeResult} in the `Effect` channel.
290
+ * `Envelope.encodeResult` in the `Effect` channel.
291
+ *
292
+ * Fails with `UnknownEvent`, `InvalidData` or `UnserializableData`; requires
293
+ * nothing.
262
294
  */
263
295
  encode: (events, envelope) => Effect.fromResult(Envelope.encodeResult(events, envelope))
264
296
  };
package/Journal.js CHANGED
@@ -120,7 +120,7 @@ const makeEngine = (events, config, fs) => Effect.gen(function* () {
120
120
  const latest = yield* SubscriptionRef.make(Option.none());
121
121
  /** Refusal state. Read before the permit so a late append fails fast. */
122
122
  let closed = false;
123
- /** Logical bytes decoded so far — the resume cursor the watcher (P5) advances. */
123
+ /** Logical bytes decoded so far — the resume cursor the watcher advances. */
124
124
  let consumed = 0;
125
125
  const exists = () => fs.exists(config.path);
126
126
  const requireFile = Effect.gen(function* () {
@@ -446,6 +446,9 @@ const makeEngine = (events, config, fs) => Effect.gen(function* () {
446
446
  * one layer graph with each one's operations typed by *its* registry. This
447
447
  * mirrors `ConfigFile.Service<Self, A>()(id)`.
448
448
  *
449
+ * Pass the registry as `options.events`; the returned class exposes it as
450
+ * `events` and builds the scoped layer with `.layer(config)`.
451
+ *
449
452
  * @example
450
453
  * ```ts
451
454
  * import { Journal, JsonlEvent } from "@effected/jsonl";
@@ -453,9 +456,9 @@ const makeEngine = (events, config, fs) => Effect.gen(function* () {
453
456
  *
454
457
  * const events = [JsonlEvent.make("started", { data: Schema.Void })] as const;
455
458
  *
456
- * class MailJournal extends Journal.Service<MailJournal>()("dogfood/MailJournal", { events }) {}
459
+ * class MailJournal extends Journal.Service<MailJournal>()("app/MailJournal", { events }) {}
457
460
  *
458
- * export const layer = MailJournal.layer({ path: ".claude/dogfood/silk.jsonl" });
461
+ * export const layer = MailJournal.layer({ path: ".app/mail.jsonl" });
459
462
  * ```
460
463
  *
461
464
  * @public
package/JsonlError.js CHANGED
@@ -3,15 +3,6 @@ import { Option, Schema } from "effect";
3
3
 
4
4
  //#region src/JsonlError.ts
5
5
  /**
6
- * The `@effected/jsonl` error taxonomy.
7
- *
8
- * Every tag names a distinct recovery a caller would actually make, and every
9
- * cause is carried structurally rather than stringified. Core's `PlatformError`
10
- * passes through untranslated rather than being wrapped.
11
- *
12
- * @since 0.1.0
13
- */
14
- /**
15
6
  * `Schema.SchemaError` as a schema of itself.
16
7
  *
17
8
  * A schema issue tree is a live object graph, not something with a wire form,
package/Line.js CHANGED
@@ -5,16 +5,6 @@ import { Option, Result, Schema } from "effect";
5
5
 
6
6
  //#region src/Line.ts
7
7
  /**
8
- * The pure, synchronous line layer of a JSONL journal.
9
- *
10
- * Nothing here touches `FileSystem`, builds an `Effect`, or needs a runtime —
11
- * a `PreToolUse` hook script reads the current state of a journal with one
12
- * call. That is a contract, not a convenience: a hook that had to construct an
13
- * Effect runtime to read one line would not adopt this package.
14
- *
15
- * @since 0.1.0
16
- */
17
- /**
18
8
  * A line that parsed as JSON, paired with the slice it came from.
19
9
  *
20
10
  * `value` is deliberately `unknown`: this layer knows JSON, not envelopes.
package/LineSlice.js CHANGED
@@ -2,11 +2,6 @@ import { Schema } from "effect";
2
2
 
3
3
  //#region src/LineSlice.ts
4
4
  /**
5
- * One candidate line of a JSONL journal, located in the source by byte.
6
- *
7
- * @since 0.1.0
8
- */
9
- /**
10
5
  * A single candidate line: its text, and where it lives in the source **in
11
6
  * bytes**.
12
7
  *
package/README.md CHANGED
@@ -7,10 +7,10 @@
7
7
 
8
8
  Append-only, schema-validated JSONL journals as a definable Effect service. The subject is not the format — one JSON value per line is a two-sentence specification and needs no library — but the file as a live object: a journal that only ever grows, whose current state is its last valid line, whose tail may be torn mid-append, and which several processes read while one writes. Every line is an envelope (`at`, `event`, an optional `scope`, and a `data` payload validated by the schema you registered for that event), so a journal stays greppable, `jq`-able and diffable in a pull request while the reads that matter stay typed.
9
9
 
10
- > **Pre-release.** This package is part of the `@effected/*` kit, in pre-`1.0.0`
11
- > development against a single pinned Effect v4 prerelease. Packages graduate to
12
- > `1.0.0` once Effect `4.0.0` ships. To hold your own `effect` versions at
13
- > exactly the ones the kit is built and tested against, install
10
+ > **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
11
+ > Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
12
+ > makes a kit `1.0.0` possible, not automatic. To keep your `effect` and
13
+ > `@effect/*` versions on the line the kit is built and tested against, install
14
14
  > [`@effected/pnpm-plugin-effect`](https://www.npmjs.com/package/@effected/pnpm-plugin-effect).
15
15
  >
16
16
  > **Stability: unstable.** This package's API surface is not yet considered
package/index.d.ts CHANGED
@@ -490,6 +490,23 @@ type EnvelopeWithTag<R extends JsonlEvent.Registry, T extends string> = Extract<
490
490
  * defined **in terms of** the sync ones via `Effect.fromResult`, so the two can
491
491
  * never drift into two implementations of the same rule.
492
492
  *
493
+ * @example
494
+ * ```ts
495
+ * import { Envelope, JsonlEvent, Line } from "@effected/jsonl";
496
+ * import { Result, Schema } from "effect";
497
+ *
498
+ * const events = [JsonlEvent.make("started", { data: Schema.Struct({ id: Schema.String }) })] as const;
499
+ *
500
+ * declare const sourceText: string;
501
+ *
502
+ * for (const line of Line.split(sourceText)) {
503
+ * const decoded = Envelope.decodeResult(events, line);
504
+ * if (Result.isSuccess(decoded) && decoded.success.event === "started") {
505
+ * decoded.success.data.id; // typed from the registered payload schema
506
+ * }
507
+ * }
508
+ * ```
509
+ *
493
510
  * @public
494
511
  */
495
512
  export declare const Envelope: {
@@ -501,6 +518,9 @@ export declare const Envelope: {
501
518
  * filtered out never pays for its payload at all.
502
519
  *
503
520
  * @param line - A slice from `Line.split`.
521
+ * @returns The frame with its source `line` attached, or `MalformedLine`
522
+ * when the line is not JSON, or `InvalidData` when it is JSON that is not
523
+ * an envelope frame.
504
524
  */
505
525
  readonly frameResult: (line: LineSlice) => Result.Result<typeof EnvelopeFrame.Type & {
506
526
  readonly line: LineSlice;
@@ -540,6 +560,10 @@ export declare const Envelope: {
540
560
  *
541
561
  * Same contract as `Line.parseAll` one layer down: a line that is not a
542
562
  * legal envelope is a `Result` in the array, never a gap in it.
563
+ *
564
+ * @param events - The registry to validate against.
565
+ * @param text - JSONL source text.
566
+ * @returns One `Result` per non-blank line, in file order.
543
567
  */
544
568
  readonly decodeAllResult: <const R extends JsonlEvent.Registry>(events: R, text: string) => ReadonlyArray<Result.Result<EnvelopeUnion<R>, MalformedLine | InvalidData | UnknownEvent>>;
545
569
  /**
@@ -577,6 +601,10 @@ export declare const Envelope: {
577
601
  * @param events - The registry to validate against.
578
602
  * @param envelope - The event tag, its payload, the service-assigned
579
603
  * timestamp, and an optional scope.
604
+ * @returns The encoded line, or `UnknownEvent` for a tag the registry does
605
+ * not define, `InvalidData` when the payload does not satisfy its schema,
606
+ * or `UnserializableData` when the encoded payload cannot be turned into
607
+ * JSON (a bigint or a reference cycle).
580
608
  */
581
609
  readonly encodeResult: <const R extends JsonlEvent.Registry, const T extends JsonlEvent.Tag<R>>(events: R, envelope: {
582
610
  readonly event: T;
@@ -585,14 +613,18 @@ export declare const Envelope: {
585
613
  readonly scope?: string | undefined;
586
614
  }) => Result.Result<string, UnknownEvent | InvalidData | UnserializableData>;
587
615
  /**
588
- * {@link (Envelope:variable).decodeResult} in the `Effect` channel.
616
+ * `Envelope.decodeResult` in the `Effect` channel.
589
617
  *
590
618
  * Prefer the sync form in a hook or any caller that has no runtime; this
591
- * exists for programs that already are one.
619
+ * exists for programs that already are one. Fails with `MalformedLine`,
620
+ * `InvalidData` or `UnknownEvent`; requires nothing.
592
621
  */
593
622
  readonly decode: <const R extends JsonlEvent.Registry>(events: R, line: LineSlice) => Effect.Effect<EnvelopeUnion<R>, MalformedLine | InvalidData | UnknownEvent>;
594
623
  /**
595
- * {@link (Envelope:variable).encodeResult} in the `Effect` channel.
624
+ * `Envelope.encodeResult` in the `Effect` channel.
625
+ *
626
+ * Fails with `UnknownEvent`, `InvalidData` or `UnserializableData`; requires
627
+ * nothing.
596
628
  */
597
629
  readonly encode: <const R extends JsonlEvent.Registry, const T extends JsonlEvent.Tag<R>>(events: R, envelope: {
598
630
  readonly event: T;
@@ -781,8 +813,8 @@ interface JournalShape<R extends JsonlEvent.Registry> {
781
813
  * Elements are `Take` chunks and end-of-stream is a published `Exit`, which
782
814
  * is what lets quiescence and graceful shutdown arrive at a subscriber as a
783
815
  * normal stream end. Never shut this down to signal completion —
784
- * `PubSub.shutdown` *interrupts* subscribers, which is the tear the design
785
- * forbids.
816
+ * `PubSub.shutdown` *interrupts* subscribers, which tears an in-flight
817
+ * stream instead of ending it.
786
818
  *
787
819
  * @internal
788
820
  */
@@ -871,6 +903,9 @@ interface JournalClass<Self, Id extends string, R extends JsonlEvent.Registry> e
871
903
  * one layer graph with each one's operations typed by *its* registry. This
872
904
  * mirrors `ConfigFile.Service<Self, A>()(id)`.
873
905
  *
906
+ * Pass the registry as `options.events`; the returned class exposes it as
907
+ * `events` and builds the scoped layer with `.layer(config)`.
908
+ *
874
909
  * @example
875
910
  * ```ts
876
911
  * import { Journal, JsonlEvent } from "@effected/jsonl";
@@ -878,9 +913,9 @@ interface JournalClass<Self, Id extends string, R extends JsonlEvent.Registry> e
878
913
  *
879
914
  * const events = [JsonlEvent.make("started", { data: Schema.Void })] as const;
880
915
  *
881
- * class MailJournal extends Journal.Service<MailJournal>()("dogfood/MailJournal", { events }) {}
916
+ * class MailJournal extends Journal.Service<MailJournal>()("app/MailJournal", { events }) {}
882
917
  *
883
- * export const layer = MailJournal.layer({ path: ".claude/dogfood/silk.jsonl" });
918
+ * export const layer = MailJournal.layer({ path: ".app/mail.jsonl" });
884
919
  * ```
885
920
  *
886
921
  * @public
package/internal/merge.js CHANGED
@@ -1,14 +1,5 @@
1
1
  //#region src/internal/merge.ts
2
2
  /**
3
- * Pollution-safe shallow merge for `appendPatch`.
4
- *
5
- * Ported from `@effected/config-file`'s `internal/deepMerge.ts` recipe, minus
6
- * the recursion: `appendPatch` is a **shallow** merge by decision, so a nested
7
- * object in the patch replaces the one beneath it rather than merging into it.
8
- *
9
- * @internal
10
- */
11
- /**
12
3
  * Keys that must never be copied from either side.
13
4
  *
14
5
  * `__proto__` reaches `Object.prototype`'s inherited accessor; `constructor`
package/internal/utf8.js CHANGED
@@ -1,14 +1,5 @@
1
1
  //#region src/internal/utf8.ts
2
2
  /**
3
- * UTF-8 byte accounting.
4
- *
5
- * A zero-dependency leaf so that both the line splitter and the error taxonomy
6
- * can measure bytes without either importing the other — the same cycle
7
- * firewall the other pure-tier packages use.
8
- *
9
- * @internal
10
- */
11
- /**
12
3
  * The UTF-8 byte length of a JavaScript string.
13
4
  *
14
5
  * Journal offsets are **bytes**, because that is what `FileSystem.stream`'s
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/jsonl",
3
- "version": "0.7.1-dogfood-20260927171341",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "Append-only, schema-validated JSONL journals as a definable Effect service.",
6
6
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "./package.json": "./package.json"
38
38
  },
39
39
  "peerDependencies": {
40
- "effect": "4.0.0-rc.117"
40
+ "effect": "^4.0.0"
41
41
  },
42
42
  "engines": {
43
43
  "node": ">=24.11.0"