@arkstack/cache 0.5.3

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.
@@ -0,0 +1,19 @@
1
+ import { t as Cache } from "../CacheManager-Do82q7KO.js";
2
+ import { Command } from "@h3ravel/musket";
3
+ //#region src/commands/CacheClearCommand.ts
4
+ /**
5
+ * Flush all entries from a cache store.
6
+ */
7
+ var CacheClearCommand = class extends Command {
8
+ signature = `cache:clear
9
+ {--store= : The cache store to flush. Defaults to the configured default store.}
10
+ `;
11
+ description = "Flush the application cache.";
12
+ async handle() {
13
+ const store = this.option("store");
14
+ await Cache.store(store).flush();
15
+ this.info(`Cache store [${store ?? "default"}] cleared successfully.`);
16
+ }
17
+ };
18
+ //#endregion
19
+ export { CacheClearCommand };
@@ -0,0 +1,526 @@
1
+ import { DotPath, DotPathValue } from "@arkstack/common";
2
+
3
+ //#region src/Contracts/Store.d.ts
4
+ /**
5
+ * The low level contract every cache store driver must satisfy.
6
+ *
7
+ * Stores are intentionally thin: they only deal in raw key/value persistence
8
+ * and expiration. The higher level convenience API (`remember`, `add`, `pull`,
9
+ * default value resolution, etc.) lives in {@link Repository}, which wraps a
10
+ * store. This keeps drivers simple and behaviour consistent across backends.
11
+ */
12
+ declare abstract class Store {
13
+ /**
14
+ * Retrieve the value for the given key, or `null` when the key is missing
15
+ * or has expired.
16
+ *
17
+ * @param key
18
+ */
19
+ abstract get<T = unknown>(key: string): Promise<T | null>;
20
+ /**
21
+ * Store a value for the given key.
22
+ *
23
+ * @param key The cache key.
24
+ * @param value The value to store (will be serialized by the driver).
25
+ * @param seconds Lifetime in seconds, or `null` to store forever.
26
+ * @returns Whether the value was stored.
27
+ */
28
+ abstract put(key: string, value: unknown, seconds?: number | null): Promise<boolean>;
29
+ /**
30
+ * Store a value that never expires.
31
+ *
32
+ * @param key
33
+ * @param value
34
+ */
35
+ abstract forever(key: string, value: unknown): Promise<boolean>;
36
+ /**
37
+ * Increment a numeric value, creating it (starting at 0) when absent.
38
+ *
39
+ * @returns The new value, or `false` when the existing value is not numeric.
40
+ *
41
+ * @param key
42
+ * @param value
43
+ */
44
+ abstract increment(key: string, value?: number): Promise<number | false>;
45
+ /**
46
+ * Decrement a numeric value, creating it (starting at 0) when absent.
47
+ *
48
+ * @returns The new value, or `false` when the existing value is not numeric.
49
+ *
50
+ * @param key
51
+ * @param value
52
+ */
53
+ abstract decrement(key: string, value?: number): Promise<number | false>;
54
+ /**
55
+ * Remove a single key from the store.
56
+ *
57
+ * @param key
58
+ * @param value
59
+ */
60
+ abstract forget(key: string): Promise<boolean>;
61
+ /**
62
+ * Remove every entry owned by this store.
63
+ */
64
+ abstract flush(): Promise<boolean>;
65
+ /**
66
+ * The key prefix applied to every entry by this store.
67
+ */
68
+ abstract getPrefix(): string;
69
+ }
70
+ //#endregion
71
+ //#region src/types.d.ts
72
+ /**
73
+ * A value, or a function (sync or async) that resolves to a value.
74
+ *
75
+ * Used for cache defaults and the `remember` callbacks.
76
+ */
77
+ type Resolvable<T> = T | (() => T | Promise<T>);
78
+ /**
79
+ * Time to live for a cache entry.
80
+ *
81
+ * - `number` number of seconds the entry should live.
82
+ * - `Date` an absolute expiry time.
83
+ * - `null` store forever (no expiration).
84
+ */
85
+ type CacheTtl = number | Date | null;
86
+ /**
87
+ * Built in cache store driver names.
88
+ */
89
+ type CacheDriverName = 'memory' | 'array' | 'file' | 'redis' | 'database';
90
+ interface FilePayload {
91
+ value: unknown;
92
+ /** Absolute expiry in epoch milliseconds, or `null` for forever. */
93
+ expiresAt: number | null;
94
+ }
95
+ interface MemoryStoreConfig {
96
+ driver: 'memory' | 'array';
97
+ }
98
+ interface FileStoreConfig {
99
+ driver: 'file';
100
+ /** Absolute path to the directory cache files are written to. */
101
+ path: string;
102
+ }
103
+ interface RedisStoreConfig {
104
+ driver: 'redis';
105
+ /** A pre built ioredis connection string, e.g. `redis://localhost:6379`. */
106
+ url?: string;
107
+ host?: string;
108
+ port?: number;
109
+ password?: string;
110
+ db?: number;
111
+ /** Optional per store key prefix applied on top of the global prefix. */
112
+ prefix?: string;
113
+ }
114
+ interface DatabaseStoreConfig {
115
+ driver: 'database';
116
+ /** The table the cache entries are stored in. */
117
+ table: string;
118
+ /** Optional named database connection. */
119
+ connection?: string;
120
+ }
121
+ /**
122
+ * Apps may augment this registry to register custom store driver configs.
123
+ */
124
+ interface CustomCacheStoreRegistry {}
125
+ type CacheStoreConfig = MemoryStoreConfig | FileStoreConfig | RedisStoreConfig | DatabaseStoreConfig | ({
126
+ driver: string;
127
+ } & Record<string, unknown>);
128
+ interface CacheConfig {
129
+ /**
130
+ * The default cache store used when no store is explicitly requested.
131
+ */
132
+ default: string;
133
+ /**
134
+ * A string prepended to every cache key to avoid collisions between
135
+ * applications sharing the same backing store.
136
+ */
137
+ prefix: string;
138
+ /**
139
+ * The configured cache stores. Each entry is keyed by the name used with
140
+ * `Cache.store('<name>')`.
141
+ */
142
+ stores: Record<string, CacheStoreConfig> & CustomCacheStoreRegistry;
143
+ }
144
+ /**
145
+ * Signature for a custom store factory registered via `Cache.extend`.
146
+ */
147
+ type CacheStoreFactory = (config: CacheStoreConfig) => Store;
148
+ interface MemoryEntry {
149
+ value: unknown;
150
+ /** Absolute expiry in epoch milliseconds, or `null` for forever. */
151
+ expiresAt: number | null;
152
+ }
153
+ interface CacheRow {
154
+ key: string;
155
+ value: string;
156
+ /** Absolute expiry in epoch seconds, or `null` for forever. */
157
+ expiration: number | null;
158
+ }
159
+ /**
160
+ * Structural type for the `DB` class exported by `@arkstack/database`, declared
161
+ * locally so the cache package does not depend on it at build time (it is an
162
+ * optional peer dependency).
163
+ */
164
+ interface DatabaseFacade {
165
+ table(table: string): {
166
+ where(where: Record<string, unknown>): {
167
+ first(): Promise<CacheRow | null>;
168
+ delete(): Promise<unknown>;
169
+ };
170
+ updateOrInsert(attributes: Record<string, unknown>, values: Record<string, unknown>): Promise<boolean>;
171
+ delete(): Promise<unknown>;
172
+ };
173
+ }
174
+ /**
175
+ * Minimal structural type for the slice of the ioredis client we use. Declared
176
+ * locally so the package does not need ioredis types at build time (it is an
177
+ * optional peer dependency).
178
+ */
179
+ interface RedisClient {
180
+ get(key: string): Promise<string | null>;
181
+ set(key: string, value: string): Promise<unknown>;
182
+ set(key: string, value: string, mode: 'EX', seconds: number): Promise<unknown>;
183
+ del(...keys: string[]): Promise<number>;
184
+ incrby(key: string, value: number): Promise<number>;
185
+ decrby(key: string, value: number): Promise<number>;
186
+ scan(cursor: string, match: 'MATCH', pattern: string, count: 'COUNT', n: number): Promise<[string, string[]]>;
187
+ quit(): Promise<unknown>;
188
+ }
189
+ //#endregion
190
+ //#region src/Repository.d.ts
191
+ /**
192
+ * Normalize a {@link CacheTtl} into a whole number of seconds, or `null` for a
193
+ * forever entry. A non positive duration is treated as already expired and
194
+ * returns `0`, signalling the caller to skip writing.
195
+ */
196
+ declare const ttlToSeconds: (ttl: CacheTtl) => number | null;
197
+ /**
198
+ * A high level wrapper around a {@link Store} that provides the developer facing
199
+ * cache API. A repository is what `Cache.store()` returns.
200
+ */
201
+ declare class Repository {
202
+ protected readonly store: Store;
203
+ constructor(store: Store);
204
+ /**
205
+ * Get the underlying store driver.
206
+ */
207
+ getStore(): Store;
208
+ /**
209
+ * Determine whether an item exists in the cache and has not expired.
210
+ *
211
+ * @param key
212
+ * @returns
213
+ */
214
+ has(key: string): Promise<boolean>;
215
+ /**
216
+ * Determine whether an item is missing from the cache.
217
+ *
218
+ * @param key
219
+ * @returns
220
+ */
221
+ missing(key: string): Promise<boolean>;
222
+ /**
223
+ * Retrieve an item from the cache, falling back to `defaultValue` (which may
224
+ * be a callback) when the item is absent.
225
+ *
226
+ * @param key
227
+ * @returns
228
+ */
229
+ get<T = unknown>(key: string, defaultValue?: Resolvable<T | null>): Promise<T | null>;
230
+ /**
231
+ * Retrieve an item and delete it from the cache in a single call.
232
+ *
233
+ * @param key
234
+ * @param defaultValue
235
+ * @returns
236
+ */
237
+ pull<T = unknown>(key: string, defaultValue?: Resolvable<T | null>): Promise<T | null>;
238
+ /**
239
+ * Store an item in the cache. A `null`/omitted ttl stores the item forever;
240
+ * a non positive ttl is a no-op that also forgets any existing entry.
241
+ *
242
+ * @param key
243
+ * @param defaultValue
244
+ * @returns
245
+ */
246
+ put(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
247
+ /**
248
+ * Alias for {@link put}.
249
+ *
250
+ * @param key
251
+ * @param defaultValue
252
+ * @returns
253
+ */
254
+ set(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
255
+ /**
256
+ * Store an item only if it is not already present. Returns `false` when the
257
+ * key already exists.
258
+ *
259
+ * @param key
260
+ * @param defaultValue
261
+ * @returns
262
+ */
263
+ add(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
264
+ /**
265
+ * Store an item that never expires.
266
+ *
267
+ * @param key
268
+ * @param defaultValue
269
+ * @returns
270
+ */
271
+ forever(key: string, value: unknown): Promise<boolean>;
272
+ /**
273
+ * Increment a stored numeric value.
274
+ *
275
+ * @param key
276
+ * @param defaultValue
277
+ * @returns
278
+ */
279
+ increment(key: string, value?: number): Promise<number | false>;
280
+ /**
281
+ * Decrement a stored numeric value.
282
+ *
283
+ * @param key
284
+ * @param defaultValue
285
+ * @returns
286
+ */
287
+ decrement(key: string, value?: number): Promise<number | false>;
288
+ /**
289
+ * Get an item from the cache, or execute the callback, store its result for
290
+ * the given ttl, and return it.
291
+ *
292
+ * @param key
293
+ * @param defaultValue
294
+ * @returns
295
+ */
296
+ remember<T>(key: string, ttl: CacheTtl, callback: () => T | Promise<T>): Promise<T>;
297
+ /**
298
+ * Get an item from the cache, or execute the callback and store its result
299
+ * forever.
300
+ *
301
+ * @param key
302
+ * @param defaultValue
303
+ * @returns
304
+ */
305
+ rememberForever<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
306
+ /**
307
+ * Alias for {@link rememberForever}.
308
+ *
309
+ * @param key
310
+ * @param callback
311
+ * @returns
312
+ */
313
+ sear<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
314
+ /**
315
+ * Remove an item from the cache.
316
+ *
317
+ * @param key
318
+ * @param callback
319
+ * @returns
320
+ */
321
+ forget(key: string): Promise<boolean>;
322
+ /**
323
+ * Alias for {@link forget}.
324
+ *
325
+ * @param key
326
+ * @param callback
327
+ * @returns
328
+ */
329
+ delete(key: string): Promise<boolean>;
330
+ /**
331
+ * Remove every item from the cache store.
332
+ *
333
+ * @param key
334
+ * @param callback
335
+ * @returns
336
+ */
337
+ flush(): Promise<boolean>;
338
+ /**
339
+ * Alias for {@link flush}.
340
+ *
341
+ * @param key
342
+ * @param callback
343
+ * @returns
344
+ */
345
+ clear(): Promise<boolean>;
346
+ }
347
+ //#endregion
348
+ //#region src/CacheManager.d.ts
349
+ /**
350
+ * The cache manager and primary entry point of `@arkstack/cache`.
351
+ *
352
+ * Resolves named cache stores from configuration, memoizes the resulting
353
+ * repositories, and exposes static convenience methods that proxy the default
354
+ * store, e.g.:
355
+ *
356
+ * ```ts
357
+ * await Cache.put('user:1', user, 60)
358
+ * await Cache.store('redis').remember('stats', 300, computeStats)
359
+ * ```
360
+ */
361
+ declare class Cache {
362
+ private static repositories;
363
+ private static customDrivers;
364
+ /**
365
+ * Resolve a cache repository for the given store name (or the default store
366
+ * when omitted). Repositories are memoized per name.
367
+ */
368
+ static store(name?: string): Repository;
369
+ /**
370
+ * Alias for {@link store}.
371
+ */
372
+ static driver(name?: string): Repository;
373
+ /**
374
+ * Register a custom store driver factory, used when a store's `driver` does
375
+ * not match a built in one.
376
+ */
377
+ static extend(driver: string, factory: CacheStoreFactory): typeof Cache;
378
+ /**
379
+ * Clear the memoized repositories. Mainly useful between tests or after the
380
+ * configuration changes at runtime.
381
+ */
382
+ static clearResolved(): void;
383
+ /**
384
+ * Build a repository for a configured store from scratch.
385
+ */
386
+ private static resolve;
387
+ /**
388
+ * Instantiate the concrete {@link Store} for a store config.
389
+ */
390
+ private static createStore;
391
+ static has(key: string): Promise<boolean>;
392
+ static missing(key: string): Promise<boolean>;
393
+ static get<T = unknown>(key: string, defaultValue?: Resolvable<T | null>): Promise<T | null>;
394
+ static pull<T = unknown>(key: string, defaultValue?: Resolvable<T | null>): Promise<T | null>;
395
+ static put(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
396
+ static set(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
397
+ static add(key: string, value: unknown, ttl?: CacheTtl): Promise<boolean>;
398
+ static forever(key: string, value: unknown): Promise<boolean>;
399
+ static increment(key: string, value?: number): Promise<number | false>;
400
+ static decrement(key: string, value?: number): Promise<number | false>;
401
+ static remember<T>(key: string, ttl: CacheTtl, callback: () => T | Promise<T>): Promise<T>;
402
+ static rememberForever<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
403
+ static forget(key: string): Promise<boolean>;
404
+ static delete(key: string): Promise<boolean>;
405
+ static flush(): Promise<boolean>;
406
+ static clear(): Promise<boolean>;
407
+ }
408
+ //#endregion
409
+ //#region src/config.d.ts
410
+ /**
411
+ * Read a value from the `cache` configuration namespace with a fallback.
412
+ *
413
+ * Mirrors the framework `config()` helper but is scoped to the cache config and
414
+ * never throws when the config file is missing, returning the default instead.
415
+ *
416
+ * @param key Dot path within the cache config.
417
+ * @param defaultValue Value returned when the key is not set.
418
+ */
419
+ declare const configure: <T extends DotPath<CacheConfig>>(key: T, defaultValue: unknown) => DotPathValue<CacheConfig, T>;
420
+ //#endregion
421
+ //#region src/drivers/MemoryStore.d.ts
422
+ /**
423
+ * An in-process cache store backed by a `Map`.
424
+ *
425
+ * The backing map is shared across every `MemoryStore` instance that uses the
426
+ * same prefix, so resolving the store twice within a process sees the same data
427
+ * (matching how the other backends behave). Great for development and tests; it
428
+ * is not shared across processes.
429
+ */
430
+ declare class MemoryStore extends Store {
431
+ private readonly prefix;
432
+ private static stores;
433
+ private readonly entries;
434
+ constructor(prefix?: string);
435
+ getPrefix(): string;
436
+ get<T = unknown>(key: string): Promise<T | null>;
437
+ put(key: string, value: unknown, seconds?: number | null): Promise<boolean>;
438
+ forever(key: string, value: unknown): Promise<boolean>;
439
+ increment(key: string, value?: number): Promise<number | false>;
440
+ decrement(key: string, value?: number): Promise<number | false>;
441
+ forget(key: string): Promise<boolean>;
442
+ flush(): Promise<boolean>;
443
+ }
444
+ //#endregion
445
+ //#region src/drivers/FileStore.d.ts
446
+ /**
447
+ * A cache store that persists each entry as a JSON file on disk.
448
+ *
449
+ * Keys are hashed to produce safe, fixed length file names. Expired files are
450
+ * removed lazily on read and ignored otherwise.
451
+ */
452
+ declare class FileStore extends Store {
453
+ private readonly directory;
454
+ private readonly prefix;
455
+ constructor(directory: string, prefix?: string);
456
+ getPrefix(): string;
457
+ private pathFor;
458
+ private ensureDirectory;
459
+ get<T = unknown>(key: string): Promise<T | null>;
460
+ put(key: string, value: unknown, seconds?: number | null): Promise<boolean>;
461
+ forever(key: string, value: unknown): Promise<boolean>;
462
+ increment(key: string, value?: number): Promise<number | false>;
463
+ decrement(key: string, value?: number): Promise<number | false>;
464
+ forget(key: string): Promise<boolean>;
465
+ flush(): Promise<boolean>;
466
+ }
467
+ //#endregion
468
+ //#region src/drivers/RedisStore.d.ts
469
+ /**
470
+ * A Redis backed cache store using an ioredis compatible client.
471
+ *
472
+ * Values are JSON serialized. Numeric helpers use native `INCRBY`/`DECRBY` so
473
+ * counters stay atomic across processes. `ioredis` is an optional peer
474
+ * dependency; it is imported lazily so applications that never use the redis
475
+ * store don't need it installed.
476
+ */
477
+ declare class RedisStore extends Store {
478
+ private readonly redisConfig;
479
+ private readonly prefix;
480
+ private client?;
481
+ constructor(redisConfig: RedisStoreConfig, prefix?: string);
482
+ getPrefix(): string;
483
+ private prefixed;
484
+ private connection;
485
+ /**
486
+ * Disconnect the underlying client. Useful for tests and graceful shutdown.
487
+ */
488
+ disconnect(): Promise<void>;
489
+ get<T = unknown>(key: string): Promise<T | null>;
490
+ put(key: string, value: unknown, seconds?: number | null): Promise<boolean>;
491
+ forever(key: string, value: unknown): Promise<boolean>;
492
+ increment(key: string, value?: number): Promise<number | false>;
493
+ decrement(key: string, value?: number): Promise<number | false>;
494
+ forget(key: string): Promise<boolean>;
495
+ flush(): Promise<boolean>;
496
+ }
497
+ //#endregion
498
+ //#region src/drivers/DatabaseStore.d.ts
499
+ /**
500
+ * A cache store that persists entries in a relational table via
501
+ * `@arkstack/database`.
502
+ *
503
+ * The table is expected to have `key` (string, primary), `value` (text), and
504
+ * `expiration` (nullable integer epoch seconds) columns. Run
505
+ * `ark publish --tag cache-migrations` to add the migration that creates it.
506
+ * `@arkstack/database` is an optional peer dependency and is imported lazily.
507
+ */
508
+ declare class DatabaseStore extends Store {
509
+ private readonly databaseConfig;
510
+ private readonly prefix;
511
+ private db?;
512
+ constructor(databaseConfig: DatabaseStoreConfig, prefix?: string);
513
+ getPrefix(): string;
514
+ private prefixed;
515
+ private get table();
516
+ private database;
517
+ get<T = unknown>(key: string): Promise<T | null>;
518
+ put(key: string, value: unknown, seconds?: number | null): Promise<boolean>;
519
+ forever(key: string, value: unknown): Promise<boolean>;
520
+ increment(key: string, value?: number): Promise<number | false>;
521
+ decrement(key: string, value?: number): Promise<number | false>;
522
+ forget(key: string): Promise<boolean>;
523
+ flush(): Promise<boolean>;
524
+ }
525
+ //#endregion
526
+ export { Cache, CacheConfig, CacheDriverName, CacheRow, CacheStoreConfig, CacheStoreFactory, CacheTtl, CustomCacheStoreRegistry, DatabaseFacade, DatabaseStore, DatabaseStoreConfig, FilePayload, FileStore, FileStoreConfig, MemoryEntry, MemoryStore, MemoryStoreConfig, RedisClient, RedisStore, RedisStoreConfig, Repository, Resolvable, Store, configure, ttlToSeconds };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as RedisStore, c as DatabaseStore, i as ttlToSeconds, l as Store, n as configure, o as MemoryStore, r as Repository, s as FileStore, t as Cache } from "./CacheManager-Do82q7KO.js";
2
+ export { Cache, DatabaseStore, FileStore, MemoryStore, RedisStore, Repository, Store, configure, ttlToSeconds };
@@ -0,0 +1 @@
1
+ export { };
package/dist/setup.js ADDED
@@ -0,0 +1,21 @@
1
+ import { dirname, join } from "node:path";
2
+ import { Publisher } from "@arkstack/common";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/setup.ts
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ /**
7
+ * Register the artifacts `@arkstack/cache` publishes into the application.
8
+ *
9
+ * Run `ark publish --package @arkstack/cache` (or `--tag cache-migrations`) to
10
+ * copy the migration for the `database` cache store into the app.
11
+ */
12
+ Publisher.publishes({
13
+ package: "@arkstack/cache",
14
+ tag: "cache-migrations",
15
+ entries: [{
16
+ from: join(root, "stubs/migrations/20260601000000_create_cache_table.ts.stub"),
17
+ to: "src/database/migrations/20260601000000_create_cache_table.ts"
18
+ }]
19
+ });
20
+ //#endregion
21
+ export {};
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@arkstack/cache",
3
+ "version": "0.5.3",
4
+ "type": "module",
5
+ "description": "Cache module for Arkstack, providing a unified, driver based caching layer for the framework.",
6
+ "homepage": "https://arkstack.toneflix.net/guide/cache",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/arkstack-hq/arkstack.git",
10
+ "directory": "packages/cache"
11
+ },
12
+ "keywords": [
13
+ "cache",
14
+ "caching",
15
+ "memory",
16
+ "file",
17
+ "redis",
18
+ "database",
19
+ "store",
20
+ "arkstack"
21
+ ],
22
+ "files": [
23
+ "dist",
24
+ "stubs"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "exports": {
30
+ ".": "./dist/index.js",
31
+ "./commands/CacheClearCommand": "./dist/commands/CacheClearCommand.js",
32
+ "./setup": "./dist/setup.js",
33
+ "./package.json": "./package.json"
34
+ },
35
+ "dependencies": {
36
+ "@arkstack/common": "^0.5.3"
37
+ },
38
+ "peerDependencies": {
39
+ "@h3ravel/musket": "^2.2.1",
40
+ "ioredis": "^5.4.1",
41
+ "@arkstack/contract": "^0.5.3",
42
+ "@arkstack/database": "^0.5.3"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "@arkstack/database": {
46
+ "optional": true
47
+ },
48
+ "ioredis": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "scripts": {
53
+ "build": "tsdown",
54
+ "test": "vitest",
55
+ "version:patch": "pnpm version patch"
56
+ }
57
+ }
@@ -0,0 +1,21 @@
1
+ import { Migration, SchemaBuilder } from 'arkormx'
2
+
3
+ /**
4
+ * Backing table for the `database` cache store (`@arkstack/cache`).
5
+ *
6
+ * Columns: `key` (string primary), `value` (text), `expiration` (nullable
7
+ * epoch seconds). Adjust the table name to match `cache.stores.database.table`.
8
+ */
9
+ export default class CreateCacheTableMigration extends Migration {
10
+ public async up (schema: SchemaBuilder): Promise<void> {
11
+ schema.createTable('cache', (table) => {
12
+ table.string('key').primary()
13
+ table.text('value')
14
+ table.integer('expiration').nullable()
15
+ })
16
+ }
17
+
18
+ public async down (schema: SchemaBuilder): Promise<void> {
19
+ schema.dropTable('cache')
20
+ }
21
+ }