@f0rbit/corpus 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/backend/cloudflare.d.ts +40 -0
  2. package/dist/backend/cloudflare.d.ts.map +1 -1
  3. package/dist/backend/cloudflare.js +40 -0
  4. package/dist/backend/file.d.ts +35 -0
  5. package/dist/backend/file.d.ts.map +1 -1
  6. package/dist/backend/file.js +35 -0
  7. package/dist/backend/layered.d.ts +38 -0
  8. package/dist/backend/layered.d.ts.map +1 -1
  9. package/dist/backend/layered.js +38 -0
  10. package/dist/backend/memory.d.ts +30 -0
  11. package/dist/backend/memory.d.ts.map +1 -1
  12. package/dist/backend/memory.js +30 -0
  13. package/dist/backends.d.ts +11 -0
  14. package/dist/backends.d.ts.map +1 -0
  15. package/dist/backends.js +9 -0
  16. package/dist/cloudflare.d.ts +2 -5
  17. package/dist/cloudflare.d.ts.map +1 -1
  18. package/dist/cloudflare.js +2 -5
  19. package/dist/codecs.d.ts +8 -0
  20. package/dist/codecs.d.ts.map +1 -0
  21. package/dist/codecs.js +6 -0
  22. package/dist/core.d.ts +9 -0
  23. package/dist/core.d.ts.map +1 -0
  24. package/dist/core.js +7 -0
  25. package/dist/corpus.d.ts +68 -1
  26. package/dist/corpus.d.ts.map +1 -1
  27. package/dist/corpus.js +194 -1
  28. package/dist/index.d.ts +3 -6
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2 -5
  31. package/dist/schema.d.ts +27 -0
  32. package/dist/schema.d.ts.map +1 -1
  33. package/dist/schema.js +27 -0
  34. package/dist/sst.d.ts +38 -0
  35. package/dist/sst.d.ts.map +1 -1
  36. package/dist/sst.js +38 -0
  37. package/dist/types.d.ts +229 -1
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +91 -2
  40. package/dist/utils.d.ts +133 -0
  41. package/dist/utils.d.ts.map +1 -0
  42. package/dist/utils.js +174 -0
  43. package/package.json +5 -2
package/dist/utils.js ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @module Utilities
3
+ * @description Utility functions for hashing, versioning, and codecs.
4
+ */
5
+ /**
6
+ * Computes the SHA-256 hash of binary data.
7
+ * @category Utilities
8
+ * @group Hashing
9
+ *
10
+ * Returns a lowercase hexadecimal string (64 characters).
11
+ * Used internally for content-addressable storage and deduplication.
12
+ *
13
+ * @param data - The binary data to hash
14
+ * @returns A lowercase hex string of the SHA-256 hash
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const data = new TextEncoder().encode('Hello, world!')
19
+ * const hash = await compute_hash(data)
20
+ * // => '315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3'
21
+ * ```
22
+ */
23
+ export async function compute_hash(data) {
24
+ const hash_buffer = await crypto.subtle.digest('SHA-256', data);
25
+ const hash_array = new Uint8Array(hash_buffer);
26
+ return Array.from(hash_array).map(b => b.toString(16).padStart(2, '0')).join('');
27
+ }
28
+ let last_timestamp = 0;
29
+ let sequence = 0;
30
+ /**
31
+ * Generates a unique, time-sortable version string.
32
+ *
33
+ * Format: base64url-encoded timestamp, with optional `.N` suffix when multiple
34
+ * versions are generated within the same millisecond.
35
+ *
36
+ * Versions sort lexicographically in chronological order, making them suitable
37
+ * for use as database keys where ordering matters.
38
+ *
39
+ * @category Utilities
40
+ * @group Versioning
41
+ * @returns A unique version string like `AZJx4vM` or `AZJx4vM.1`
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const v1 = generate_version() // => 'AZJx4vM'
46
+ * const v2 = generate_version() // => 'AZJx4vM.1' (same millisecond)
47
+ * const v3 = generate_version() // => 'AZJx4vN' (next millisecond)
48
+ *
49
+ * // Versions sort chronologically
50
+ * [v3, v1, v2].sort() // => [v1, v2, v3]
51
+ * ```
52
+ */
53
+ export function generate_version() {
54
+ const now = Date.now();
55
+ if (now === last_timestamp) {
56
+ sequence++;
57
+ }
58
+ else {
59
+ last_timestamp = now;
60
+ sequence = 0;
61
+ }
62
+ // base64url encode the timestamp (no padding, url-safe)
63
+ const timestamp_bytes = new Uint8Array(8);
64
+ const view = new DataView(timestamp_bytes.buffer);
65
+ view.setBigUint64(0, BigInt(now), false); // big-endian for lexicographic sorting
66
+ // trim leading zeros for compactness
67
+ let start = 0;
68
+ while (start < 7 && timestamp_bytes[start] === 0)
69
+ start++;
70
+ const trimmed = timestamp_bytes.slice(start);
71
+ const base64 = btoa(String.fromCharCode(...trimmed))
72
+ .replace(/\+/g, '-')
73
+ .replace(/\//g, '_')
74
+ .replace(/=/g, '');
75
+ return sequence > 0 ? `${base64}.${sequence}` : base64;
76
+ }
77
+ /**
78
+ * Creates a JSON codec with schema validation.
79
+ *
80
+ * Data is serialized to JSON on encode and validated against the schema on decode.
81
+ * Works with both Zod 3.x and 4.x (uses structural typing, not Zod imports).
82
+ *
83
+ * Note: Validation only happens on decode. Invalid data passed to encode will
84
+ * serialize but may fail validation when decoded later.
85
+ *
86
+ * @category Codecs
87
+ * @group Codec Factories
88
+ * @param schema - A Zod schema (or any object with a `parse` method)
89
+ * @returns A Codec for JSON serialization with validation
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { z } from 'zod'
94
+ *
95
+ * const UserSchema = z.object({
96
+ * id: z.string().uuid(),
97
+ * name: z.string(),
98
+ * createdAt: z.coerce.date()
99
+ * })
100
+ *
101
+ * const codec = json_codec(UserSchema)
102
+ * const users = define_store('users', codec)
103
+ *
104
+ * // Decoding validates and transforms data
105
+ * const bytes = codec.encode({ id: '...', name: 'Alice', createdAt: '2024-01-01' })
106
+ * const user = codec.decode(bytes) // createdAt is now a Date object
107
+ * ```
108
+ */
109
+ export function json_codec(schema) {
110
+ return {
111
+ content_type: "application/json",
112
+ encode: (value) => new TextEncoder().encode(JSON.stringify(value)),
113
+ decode: (bytes) => schema.parse(JSON.parse(new TextDecoder().decode(bytes))),
114
+ };
115
+ }
116
+ /**
117
+ * Creates a plain text codec using UTF-8 encoding.
118
+ *
119
+ * No validation is performed - any string can be encoded and any valid
120
+ * UTF-8 bytes can be decoded.
121
+ *
122
+ * @category Codecs
123
+ * @group Codec Factories
124
+ * @returns A Codec for plain text strings
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const notes = define_store('notes', text_codec())
129
+ *
130
+ * await corpus.stores.notes.put('Meeting notes for 2024-01-15...')
131
+ *
132
+ * const result = await corpus.stores.notes.get_latest()
133
+ * if (result.ok) {
134
+ * console.log(result.value.data) // string
135
+ * }
136
+ * ```
137
+ */
138
+ export function text_codec() {
139
+ return {
140
+ content_type: "text/plain",
141
+ encode: (value) => new TextEncoder().encode(value),
142
+ decode: (bytes) => new TextDecoder().decode(bytes),
143
+ };
144
+ }
145
+ /**
146
+ * Creates a pass-through codec for raw binary data.
147
+ *
148
+ * No transformation is performed - bytes are stored and retrieved as-is.
149
+ * Use for images, PDFs, pre-serialized data, or any binary content.
150
+ *
151
+ * @category Codecs
152
+ * @group Codec Factories
153
+ * @returns A Codec for raw binary data
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * const images = define_store('images', binary_codec())
158
+ *
159
+ * // Store an image
160
+ * const imageData = await fetch('photo.png').then(r => r.arrayBuffer())
161
+ * await corpus.stores.images.put(new Uint8Array(imageData))
162
+ *
163
+ * // Store pre-serialized protobuf
164
+ * const protoBytes = MyMessage.encode(message).finish()
165
+ * await corpus.stores.images.put(protoBytes)
166
+ * ```
167
+ */
168
+ export function binary_codec() {
169
+ return {
170
+ content_type: "application/octet-stream",
171
+ encode: (value) => value,
172
+ decode: (bytes) => bytes,
173
+ };
174
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f0rbit/corpus",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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",
@@ -31,7 +31,10 @@
31
31
  "build": "tsc -p tsconfig.build.json",
32
32
  "test": "bun test",
33
33
  "typecheck": "tsc --noEmit",
34
- "prepublishOnly": "npm run build"
34
+ "prepublishOnly": "npm run build",
35
+ "docs:dev": "cd docs && bun run dev",
36
+ "docs:build": "cd docs && bun run build",
37
+ "docs:preview": "cd docs && bun run preview"
35
38
  },
36
39
  "repository": {
37
40
  "type": "git",