@f0rbit/corpus 0.2.0 → 0.2.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../backend/base.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EAGZ,WAAW,EAGZ,MAAM,UAAU,CAAC;AAIlB,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACzE,GAAG,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,aAAa,CAAC,YAAY,CAAC,CAAC;IACxD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACtD,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD,CAAC;AAEF,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;AAEzC,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,IAAI,GACT,cAAc,CAoEhB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CA0C/E"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../backend/base.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,YAAY,EAGZ,WAAW,EAGZ,MAAM,UAAU,CAAC;AAKlB,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACzE,GAAG,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,aAAa,CAAC,YAAY,CAAC,CAAC;IACxD,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACtD,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD,CAAC;AAEF,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;AAEzC,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,IAAI,GACT,cAAc,CAoEhB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CA0C/E"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { ok, err } from "../types";
6
6
  import { to_bytes, filter_snapshots } from "../utils";
7
+ import { first, to_fallback } from "../result";
7
8
  export function create_metadata_client(storage, emit) {
8
9
  return {
9
10
  async get(store_id, version) {
@@ -67,7 +68,7 @@ export function create_data_client(storage, emit) {
67
68
  const bytes = await storage.get(data_key);
68
69
  emit({
69
70
  type: "data_get",
70
- store_id: data_key.split("/")[0] ?? data_key,
71
+ store_id: to_fallback(first(data_key.split("/")), data_key),
71
72
  version: data_key,
72
73
  found: !!bytes,
73
74
  });
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../backend/cloudflare.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAiE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMrH,KAAK,UAAU,GAAG;IAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,CAAC;AACxD,KAAK,QAAQ,GAAG;IACf,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QAAC,WAAW,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACpH,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,EAAE,EAAE,UAAU,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,QAAQ,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AA6IF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAmOlF"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../backend/cloudflare.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAiE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOrH,KAAK,UAAU,GAAG;IAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,CAAC;AACxD,KAAK,QAAQ,GAAG;IACf,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QAAC,WAAW,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACpH,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,EAAE,EAAE,UAAU,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,QAAQ,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AA6IF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAmOlF"}
@@ -6,6 +6,7 @@ import { eq, and, desc, lt, gt, like, sql, inArray } from "drizzle-orm";
6
6
  import { drizzle } from "drizzle-orm/d1";
7
7
  import { create_emitter, parse_snapshot_meta } from "../utils";
8
8
  import { ok, err } from "../types";
9
+ import { first, to_nullable, to_fallback } from "../result";
9
10
  import { corpus_snapshots } from "../schema";
10
11
  import { corpus_observations, create_observations_client } from "../observations";
11
12
  function create_cloudflare_storage(db) {
@@ -30,7 +31,7 @@ function create_cloudflare_storage(db) {
30
31
  .from(corpus_observations)
31
32
  .where(eq(corpus_observations.id, id))
32
33
  .limit(1);
33
- return ok(rows[0] ?? null);
34
+ return ok(to_nullable(first(rows)));
34
35
  }
35
36
  catch (cause) {
36
37
  return err({
@@ -185,7 +186,7 @@ export function create_cloudflare_backend(config) {
185
186
  .from(corpus_snapshots)
186
187
  .where(and(eq(corpus_snapshots.store_id, store_id), eq(corpus_snapshots.version, version)))
187
188
  .limit(1);
188
- const row = rows[0];
189
+ const row = to_nullable(first(rows));
189
190
  emit({ type: "meta_get", store_id, version, found: !!row });
190
191
  if (!row) {
191
192
  return err({ kind: "not_found", store_id, version });
@@ -279,7 +280,7 @@ export function create_cloudflare_backend(config) {
279
280
  async get_latest(store_id) {
280
281
  try {
281
282
  const rows = await db.select().from(corpus_snapshots).where(eq(corpus_snapshots.store_id, store_id)).orderBy(desc(corpus_snapshots.created_at)).limit(1);
282
- const row = rows[0];
283
+ const row = to_nullable(first(rows));
283
284
  if (!row) {
284
285
  return err({ kind: "not_found", store_id, version: "latest" });
285
286
  }
@@ -311,7 +312,7 @@ export function create_cloudflare_backend(config) {
311
312
  .from(corpus_snapshots)
312
313
  .where(and(eq(corpus_snapshots.store_id, store_id), eq(corpus_snapshots.content_hash, content_hash)))
313
314
  .limit(1);
314
- const row = rows[0];
315
+ const row = to_nullable(first(rows));
315
316
  return row ? snapshot_row_to_meta(row) : null;
316
317
  }
317
318
  catch {
@@ -323,7 +324,7 @@ export function create_cloudflare_backend(config) {
323
324
  async get(data_key) {
324
325
  try {
325
326
  const object = await r2.get(data_key);
326
- emit({ type: "data_get", store_id: data_key.split("/")[0] ?? data_key, version: data_key, found: !!object });
327
+ emit({ type: "data_get", store_id: to_fallback(first(data_key.split("/")), data_key), version: data_key, found: !!object });
327
328
  if (!object) {
328
329
  return err({ kind: "not_found", store_id: data_key, version: "" });
329
330
  }
@@ -1 +1 @@
1
- {"version":3,"file":"layered.d.ts","sourceRoot":"","sources":["../../backend/layered.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiG,MAAM,UAAU,CAAA;AAItI,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAClC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAkJ9E"}
1
+ {"version":3,"file":"layered.d.ts","sourceRoot":"","sources":["../../backend/layered.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiG,MAAM,UAAU,CAAA;AAKtI,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAClC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAoJ9E"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { ok, err } from '../types';
6
6
  import { to_bytes } from '../utils';
7
+ import { first, to_nullable } from '../result';
7
8
  /**
8
9
  * Creates a layered backend that combines multiple backends with read/write separation.
9
10
  * @category Backends
@@ -71,7 +72,10 @@ export function create_layered_backend(options) {
71
72
  if (read.length === 0)
72
73
  return;
73
74
  if (list_strategy === 'first') {
74
- yield* read[0].metadata.list(store_id, opts);
75
+ const first_backend = to_nullable(first(read));
76
+ if (!first_backend)
77
+ return;
78
+ yield* first_backend.metadata.list(store_id, opts);
75
79
  return;
76
80
  }
77
81
  const seen = new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../observations/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAGnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACpC,eAAe,CAKjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAK/D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAsC9F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAS3G;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,GAAG,OAAO,CAO9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAE7E"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../observations/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAInD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACpC,eAAe,CAKjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAK/D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAyBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAsC9F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAS3G;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,GAAG,OAAO,CAO9E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAE7E"}
@@ -3,6 +3,7 @@
3
3
  * @description Utility functions for working with SnapshotPointers and observations.
4
4
  */
5
5
  import { ok, err } from '../types';
6
+ import { last, to_nullable } from '../result';
6
7
  /**
7
8
  * Creates a SnapshotPointer to a location in a snapshot.
8
9
  *
@@ -96,8 +97,10 @@ export function key_to_pointer(key) {
96
97
  const pointer = { store_id, version };
97
98
  if (rest.length === 0)
98
99
  return pointer;
99
- const last = rest[rest.length - 1];
100
- const span_match = /^(\d+)-(\d+)$/.exec(last);
100
+ const last_part = to_nullable(last(rest));
101
+ if (!last_part)
102
+ return pointer;
103
+ const span_match = /^(\d+)-(\d+)$/.exec(last_part);
101
104
  if (span_match) {
102
105
  const [, start_str, end_str] = span_match;
103
106
  pointer.span = { start: parseInt(start_str, 10), end: parseInt(end_str, 10) };
package/dist/result.d.ts CHANGED
@@ -276,5 +276,86 @@ export declare const fallback_on: <T, E>(result: Result<T, E>, predicate: (error
276
276
  * ```
277
277
  */
278
278
  export declare const format_error: (e: unknown) => string;
279
+ /**
280
+ * Recursively makes all properties of T optional.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * type Config = { api: { url: string; timeout: number } }
285
+ * type PartialConfig = DeepPartial<Config>
286
+ * // { api?: { url?: string; timeout?: number } }
287
+ * ```
288
+ */
289
+ export type DeepPartial<T> = T extends object ? {
290
+ [P in keyof T]?: DeepPartial<T[P]>;
291
+ } : T;
292
+ /**
293
+ * Deep merge two objects, with overrides taking precedence.
294
+ * Only merges plain objects; arrays and null are replaced entirely.
295
+ *
296
+ * @param base - The base object to merge into
297
+ * @param overrides - Partial object with values to override
298
+ * @returns A new object with merged values
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * const config = merge_deep(
303
+ * { api: { url: 'http://localhost', timeout: 5000 }, debug: false },
304
+ * { api: { timeout: 10000 } }
305
+ * )
306
+ * // { api: { url: 'http://localhost', timeout: 10000 }, debug: false }
307
+ * ```
308
+ */
309
+ export declare const merge_deep: <T extends Record<string, unknown>>(base: T, overrides: DeepPartial<T>) => T;
310
+ /**
311
+ * Safely access an array element by index, returning a Result.
312
+ * Unlike bracket notation, this never returns undefined for out-of-bounds access.
313
+ *
314
+ * @param array - The array to access
315
+ * @param index - The index to retrieve (must be non-negative)
316
+ * @returns Result containing the element or an index_out_of_bounds error
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * const items = ['a', 'b', 'c']
321
+ * const second = at(items, 1) // { ok: true, value: 'b' }
322
+ * const tenth = at(items, 10) // { ok: false, error: { kind: 'index_out_of_bounds', index: 10, length: 3 } }
323
+ * ```
324
+ */
325
+ export declare const at: <T>(array: readonly T[], index: number) => Result<T, {
326
+ kind: "index_out_of_bounds";
327
+ index: number;
328
+ length: number;
329
+ }>;
330
+ /**
331
+ * Safely get the first element of an array.
332
+ *
333
+ * @param array - The array to access
334
+ * @returns Result containing the first element or an empty_array error
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * const head = first([1, 2, 3]) // { ok: true, value: 1 }
339
+ * const empty = first([]) // { ok: false, error: { kind: 'empty_array' } }
340
+ * ```
341
+ */
342
+ export declare const first: <T>(array: readonly T[]) => Result<T, {
343
+ kind: "empty_array";
344
+ }>;
345
+ /**
346
+ * Safely get the last element of an array.
347
+ *
348
+ * @param array - The array to access
349
+ * @returns Result containing the last element or an empty_array error
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * const tail = last([1, 2, 3]) // { ok: true, value: 3 }
354
+ * const empty = last([]) // { ok: false, error: { kind: 'empty_array' } }
355
+ * ```
356
+ */
357
+ export declare const last: <T>(array: readonly T[]) => Result<T, {
358
+ kind: "empty_array";
359
+ }>;
279
360
  export {};
280
361
  //# sourceMappingURL=result.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../result.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../result.ts"],"names":[],"mappings":"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"}
package/dist/result.js CHANGED
@@ -317,3 +317,94 @@ export const fallback_on = (result, predicate, fallback) => {
317
317
  * ```
318
318
  */
319
319
  export const format_error = (e) => (e instanceof Error ? e.message : String(e));
320
+ /**
321
+ * Deep merge two objects, with overrides taking precedence.
322
+ * Only merges plain objects; arrays and null are replaced entirely.
323
+ *
324
+ * @param base - The base object to merge into
325
+ * @param overrides - Partial object with values to override
326
+ * @returns A new object with merged values
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * const config = merge_deep(
331
+ * { api: { url: 'http://localhost', timeout: 5000 }, debug: false },
332
+ * { api: { timeout: 10000 } }
333
+ * )
334
+ * // { api: { url: 'http://localhost', timeout: 10000 }, debug: false }
335
+ * ```
336
+ */
337
+ export const merge_deep = (base, overrides) => {
338
+ const result = { ...base };
339
+ for (const key in overrides) {
340
+ const value = overrides[key];
341
+ if (value !== undefined && typeof value === "object" && !Array.isArray(value) && value !== null) {
342
+ result[key] = merge_deep(result[key], value);
343
+ }
344
+ else if (value !== undefined) {
345
+ result[key] = value;
346
+ }
347
+ }
348
+ return result;
349
+ };
350
+ /**
351
+ * Safely access an array element by index, returning a Result.
352
+ * Unlike bracket notation, this never returns undefined for out-of-bounds access.
353
+ *
354
+ * @param array - The array to access
355
+ * @param index - The index to retrieve (must be non-negative)
356
+ * @returns Result containing the element or an index_out_of_bounds error
357
+ *
358
+ * @example
359
+ * ```ts
360
+ * const items = ['a', 'b', 'c']
361
+ * const second = at(items, 1) // { ok: true, value: 'b' }
362
+ * const tenth = at(items, 10) // { ok: false, error: { kind: 'index_out_of_bounds', index: 10, length: 3 } }
363
+ * ```
364
+ */
365
+ export const at = (array, index) => {
366
+ if (index < 0 || index >= array.length) {
367
+ return err({ kind: "index_out_of_bounds", index, length: array.length });
368
+ }
369
+ const element = array[index];
370
+ if (element === undefined) {
371
+ return err({ kind: "index_out_of_bounds", index, length: array.length });
372
+ }
373
+ return ok(element);
374
+ };
375
+ /**
376
+ * Safely get the first element of an array.
377
+ *
378
+ * @param array - The array to access
379
+ * @returns Result containing the first element or an empty_array error
380
+ *
381
+ * @example
382
+ * ```ts
383
+ * const head = first([1, 2, 3]) // { ok: true, value: 1 }
384
+ * const empty = first([]) // { ok: false, error: { kind: 'empty_array' } }
385
+ * ```
386
+ */
387
+ export const first = (array) => {
388
+ if (array.length === 0) {
389
+ return err({ kind: "empty_array" });
390
+ }
391
+ return ok(array[0]);
392
+ };
393
+ /**
394
+ * Safely get the last element of an array.
395
+ *
396
+ * @param array - The array to access
397
+ * @returns Result containing the last element or an empty_array error
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * const tail = last([1, 2, 3]) // { ok: true, value: 3 }
402
+ * const empty = last([]) // { ok: false, error: { kind: 'empty_array' } }
403
+ * ```
404
+ */
405
+ export const last = (array) => {
406
+ if (array.length === 0) {
407
+ return err({ kind: "empty_array" });
408
+ }
409
+ return ok(array[array.length - 1]);
410
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f0rbit/corpus",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A functional snapshotting library for TypeScript with versioned data storage, lineage tracking, and multiple backend support",
5
5
  "module": "dist/index.js",
6
6
  "main": "dist/index.js",