@outfitter/state 0.2.3 → 0.2.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.
package/README.md CHANGED
@@ -42,14 +42,14 @@ if (cursorResult.isOk()) {
42
42
 
43
43
  Cursors are intentionally **opaque** to consumers. They are immutable, frozen objects that encapsulate pagination state.
44
44
 
45
- | Property | Type | Description |
46
- |----------|------|-------------|
47
- | `id` | `string` | Unique identifier for storage lookup |
48
- | `position` | `number` | Current offset in the result set |
49
- | `metadata` | `Record<string, unknown>` | Optional user-defined context |
50
- | `ttl` | `number` | Time-to-live in milliseconds (optional) |
51
- | `expiresAt` | `number` | Computed Unix timestamp for expiry (if TTL set) |
52
- | `createdAt` | `number` | Unix timestamp when cursor was created |
45
+ | Property | Type | Description |
46
+ | ----------- | ------------------------- | ----------------------------------------------- |
47
+ | `id` | `string` | Unique identifier for storage lookup |
48
+ | `position` | `number` | Current offset in the result set |
49
+ | `metadata` | `Record<string, unknown>` | Optional user-defined context |
50
+ | `ttl` | `number` | Time-to-live in milliseconds (optional) |
51
+ | `expiresAt` | `number` | Computed Unix timestamp for expiry (if TTL set) |
52
+ | `createdAt` | `number` | Unix timestamp when cursor was created |
53
53
 
54
54
  ### Why Opaque?
55
55
 
@@ -95,7 +95,11 @@ if (result.isOk()) {
95
95
  ### Example: Paginated Handler
96
96
 
97
97
  ```typescript
98
- import { createCursor, createCursorStore, advanceCursor } from "@outfitter/state";
98
+ import {
99
+ createCursor,
100
+ createCursorStore,
101
+ advanceCursor,
102
+ } from "@outfitter/state";
99
103
  import { Result } from "@outfitter/contracts";
100
104
 
101
105
  const store = createCursorStore();
@@ -150,11 +154,11 @@ const issuesStore = createScopedStore(store, "linear:issues");
150
154
  const prsStore = createScopedStore(store, "github:prs");
151
155
 
152
156
  // Cursors are isolated - same ID won't conflict
153
- issuesStore.set(issueCursor); // Stored as "linear:issues:cursor-id"
154
- prsStore.set(prCursor); // Stored as "github:prs:cursor-id"
157
+ issuesStore.set(issueCursor); // Stored as "linear:issues:cursor-id"
158
+ prsStore.set(prCursor); // Stored as "github:prs:cursor-id"
155
159
 
156
160
  // Each scope manages its own cursors
157
- issuesStore.clear(); // Only clears issue cursors
161
+ issuesStore.clear(); // Only clears issue cursors
158
162
  ```
159
163
 
160
164
  ### Nested Scopes
@@ -167,8 +171,8 @@ const githubStore = createScopedStore(store, "github");
167
171
  const issuesStore = createScopedStore(githubStore, "issues");
168
172
  const prsStore = createScopedStore(githubStore, "prs");
169
173
 
170
- issuesStore.getScope(); // "github:issues"
171
- prsStore.getScope(); // "github:prs"
174
+ issuesStore.getScope(); // "github:issues"
175
+ prsStore.getScope(); // "github:prs"
172
176
  ```
173
177
 
174
178
  ### Scoped Store Behavior
@@ -183,15 +187,15 @@ if (cursor.isOk()) {
183
187
  scoped.set(cursor.value);
184
188
 
185
189
  // Underlying store has prefixed ID
186
- store.list(); // ["my-scope:abc123"]
190
+ store.list(); // ["my-scope:abc123"]
187
191
 
188
192
  // Scoped store returns clean ID
189
- scoped.list(); // ["abc123"]
193
+ scoped.list(); // ["abc123"]
190
194
 
191
195
  // Get returns cursor with clean ID
192
196
  const result = scoped.get("abc123");
193
197
  if (result.isOk()) {
194
- result.value.id; // "abc123" (not "my-scope:abc123")
198
+ result.value.id; // "abc123" (not "my-scope:abc123")
195
199
  }
196
200
  }
197
201
  ```
@@ -257,7 +261,7 @@ const result = createCursor({
257
261
 
258
262
  if (result.isOk()) {
259
263
  const cursor = result.value;
260
- cursor.ttl; // 3600000
264
+ cursor.ttl; // 3600000
261
265
  cursor.expiresAt; // Unix timestamp (e.g., 1706000000000)
262
266
  }
263
267
  ```
@@ -289,7 +293,7 @@ Cursors created without a TTL never expire:
289
293
  ```typescript
290
294
  const result = createCursor({ position: 0 });
291
295
  if (result.isOk()) {
292
- result.value.ttl; // undefined
296
+ result.value.ttl; // undefined
293
297
  result.value.expiresAt; // undefined
294
298
  isExpired(result.value); // always false
295
299
  }
@@ -299,24 +303,24 @@ if (result.isOk()) {
299
303
 
300
304
  ### Functions
301
305
 
302
- | Function | Description |
303
- |----------|-------------|
304
- | `createCursor(options)` | Create a new immutable pagination cursor |
306
+ | Function | Description |
307
+ | --------------------------------- | ----------------------------------------- |
308
+ | `createCursor(options)` | Create a new immutable pagination cursor |
305
309
  | `advanceCursor(cursor, position)` | Create a new cursor with updated position |
306
- | `isExpired(cursor)` | Check if a cursor has expired |
307
- | `createCursorStore()` | Create an in-memory cursor store |
308
- | `createPersistentStore(options)` | Create a disk-backed cursor store |
309
- | `createScopedStore(store, scope)` | Create a namespace-isolated cursor store |
310
+ | `isExpired(cursor)` | Check if a cursor has expired |
311
+ | `createCursorStore()` | Create an in-memory cursor store |
312
+ | `createPersistentStore(options)` | Create a disk-backed cursor store |
313
+ | `createScopedStore(store, scope)` | Create a namespace-isolated cursor store |
310
314
 
311
315
  ### Interfaces
312
316
 
313
- | Interface | Description |
314
- |-----------|-------------|
315
- | `Cursor` | Immutable pagination cursor |
316
- | `CreateCursorOptions` | Options for `createCursor()` |
317
- | `CursorStore` | Base interface for cursor stores |
318
- | `ScopedStore` | Cursor store with namespace isolation |
319
- | `PersistentStore` | Cursor store with disk persistence |
317
+ | Interface | Description |
318
+ | ------------------------ | ------------------------------------- |
319
+ | `Cursor` | Immutable pagination cursor |
320
+ | `CreateCursorOptions` | Options for `createCursor()` |
321
+ | `CursorStore` | Base interface for cursor stores |
322
+ | `ScopedStore` | Cursor store with namespace isolation |
323
+ | `PersistentStore` | Cursor store with disk persistence |
320
324
  | `PersistentStoreOptions` | Options for `createPersistentStore()` |
321
325
 
322
326
  ## Error Handling
@@ -339,7 +343,7 @@ if (getResult.isErr()) {
339
343
  // NotFoundError: Cursor not found: nonexistent
340
344
  console.error(getResult.error.message);
341
345
  console.log(getResult.error.resourceType); // "cursor"
342
- console.log(getResult.error.resourceId); // "nonexistent"
346
+ console.log(getResult.error.resourceId); // "nonexistent"
343
347
  }
344
348
  ```
345
349
 
package/dist/index.d.ts CHANGED
@@ -378,53 +378,6 @@ declare function decodeCursor(encoded: string): Result<Cursor, InstanceType<type
378
378
  * ```
379
379
  */
380
380
  declare function createCursorStore(): CursorStore & ScopedStore;
381
- /**
382
- * Create a persistent cursor store that saves to disk.
383
- *
384
- * The store loads existing cursors from the file on initialization.
385
- * Changes are kept in memory until `flush()` is called. Uses atomic
386
- * writes (temp file + rename) to prevent corruption.
387
- *
388
- * If the file is corrupted or invalid JSON, the store starts empty
389
- * rather than throwing an error.
390
- *
391
- * @param options - Persistence options including the file path
392
- * @returns Promise resolving to a PersistentStore
393
- *
394
- * @example
395
- * ```typescript
396
- * // Create persistent store
397
- * const store = await createPersistentStore({
398
- * path: "/home/user/.config/myapp/cursors.json",
399
- * });
400
- *
401
- * // Use like any cursor store
402
- * const cursor = createCursor({ position: 0 });
403
- * if (cursor.isOk()) {
404
- * store.set(cursor.value);
405
- * }
406
- *
407
- * // Flush to disk (call before process exit)
408
- * await store.flush();
409
- *
410
- * // Cleanup when done
411
- * store.dispose();
412
- * ```
413
- *
414
- * @example
415
- * ```typescript
416
- * // Combine with scoped stores for organized persistence
417
- * const persistent = await createPersistentStore({
418
- * path: "~/.config/myapp/cursors.json",
419
- * });
420
- *
421
- * const issuesCursors = createScopedStore(persistent, "issues");
422
- * const prsCursors = createScopedStore(persistent, "prs");
423
- *
424
- * // All scopes share the same persistence file
425
- * await persistent.flush();
426
- * ```
427
- */
428
381
  declare function createPersistentStore(options: PersistentStoreOptions): Promise<PersistentStore>;
429
382
  /**
430
383
  * Create a scoped cursor store with namespace isolation.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { createRequire } from "node:module";
2
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
1
+ // @bun
2
+ var __require = import.meta.require;
3
3
 
4
- // src/index.ts
5
- import { existsSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
6
- import { dirname } from "node:path";
4
+ // packages/state/src/index.ts
5
+ import { existsSync, mkdirSync, renameSync, writeFileSync } from "fs";
6
+ import { dirname } from "path";
7
7
  import {
8
8
  NotFoundError,
9
9
  Result,
@@ -213,6 +213,7 @@ function createCursorStore() {
213
213
  }
214
214
  };
215
215
  }
216
+ var dispose = () => {};
216
217
  async function createPersistentStore(options) {
217
218
  const { path: storagePath } = options;
218
219
  const cursors = new Map;
@@ -242,13 +243,12 @@ async function createPersistentStore(options) {
242
243
  renameSync(tempPath, storagePath);
243
244
  } catch (error) {
244
245
  try {
245
- const { unlinkSync } = await import("node:fs");
246
+ const { unlinkSync } = await import("fs");
246
247
  unlinkSync(tempPath);
247
248
  } catch {}
248
249
  throw error;
249
250
  }
250
251
  };
251
- const dispose = () => {};
252
252
  return {
253
253
  set(cursor) {
254
254
  cursors.set(cursor.id, cursor);
package/package.json CHANGED
@@ -1,11 +1,25 @@
1
1
  {
2
2
  "name": "@outfitter/state",
3
+ "version": "0.2.4",
3
4
  "description": "Pagination cursor persistence and state management for Outfitter",
4
- "version": "0.2.3",
5
- "type": "module",
5
+ "keywords": [
6
+ "cursor",
7
+ "outfitter",
8
+ "pagination",
9
+ "state",
10
+ "typescript"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/outfitter-dev/outfitter.git",
16
+ "directory": "packages/state"
17
+ },
6
18
  "files": [
7
19
  "dist"
8
20
  ],
21
+ "type": "module",
22
+ "sideEffects": false,
9
23
  "module": "./dist/index.js",
10
24
  "types": "./dist/index.d.ts",
11
25
  "exports": {
@@ -17,38 +31,24 @@
17
31
  },
18
32
  "./package.json": "./package.json"
19
33
  },
20
- "sideEffects": false,
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
21
37
  "scripts": {
22
- "build": "bunup --filter @outfitter/state",
23
- "lint": "biome lint ./src",
24
- "lint:fix": "biome lint --write ./src",
38
+ "build": "cd ../.. && bunup --filter @outfitter/state",
39
+ "lint": "oxlint ./src",
40
+ "lint:fix": "oxlint --fix ./src",
25
41
  "test": "bun test",
26
42
  "typecheck": "tsc --noEmit",
27
43
  "clean": "rm -rf dist",
28
44
  "prepublishOnly": "bun ../../scripts/check-publish-manifest.ts"
29
45
  },
30
46
  "dependencies": {
31
- "@outfitter/contracts": "0.4.1",
32
- "@outfitter/types": "0.2.3"
47
+ "@outfitter/contracts": "0.4.2",
48
+ "@outfitter/types": "0.2.4"
33
49
  },
34
50
  "devDependencies": {
35
- "@types/bun": "latest",
36
- "typescript": "^5.8.0"
37
- },
38
- "keywords": [
39
- "outfitter",
40
- "state",
41
- "pagination",
42
- "cursor",
43
- "typescript"
44
- ],
45
- "license": "MIT",
46
- "repository": {
47
- "type": "git",
48
- "url": "https://github.com/outfitter-dev/outfitter.git",
49
- "directory": "packages/state"
50
- },
51
- "publishConfig": {
52
- "access": "public"
51
+ "@types/bun": "^1.3.9",
52
+ "typescript": "^5.9.3"
53
53
  }
54
54
  }