@cnstra/devtools-server-repository-redis 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  > **⚠️ Experimental**: API may change.
4
4
 
5
- Redis persistence for the [CNStra](https://cnstra.org) DevTools server. A drop-in, durable
5
+ Redis persistence for the [CNStra](https://cnstra.org) DevTools server. A drop-in, resumable
6
6
  replacement for `@cnstra/devtools-server-repository-in-memory`: same
7
- `ICNSDevToolsServerRepository` contract, backed by Redis so apps, topology, stimulations and
8
- per-hop history outlive a restart.
7
+ `ICNSDevToolsServerRepository` contract, backed by Redis so apps and topology snapshots outlive a
8
+ restart.
9
9
 
10
10
  ## Install
11
11
 
@@ -20,7 +20,7 @@ import Redis from 'ioredis';
20
20
  import { CNSDevToolsServer } from '@cnstra/devtools-server';
21
21
  import { CNSDevToolsServerRepositoryRedis } from '@cnstra/devtools-server-repository-redis';
22
22
 
23
- const redis = new Redis(process.env.REDIS_URL);
23
+ const redis = new Redis(process.env.REDIS_URL ?? 'redis://localhost:6379');
24
24
  const repo = new CNSDevToolsServerRepositoryRedis(redis);
25
25
 
26
26
  const server = new CNSDevToolsServer(repo);
@@ -28,21 +28,22 @@ const server = new CNSDevToolsServer(repo);
28
28
 
29
29
  The constructor accepts any [ioredis](https://github.com/redis/ioredis)-style client. `ioredis`
30
30
  is an optional peer dependency (the client is typed structurally). `node-redis` v4 uses
31
- different method names (`hSet`, `hGetAll`, …) and is **not** directly compatible.
31
+ camel-cased command names instead of the lowercase ioredis ones and is **not** directly compatible.
32
32
 
33
33
  ## Options
34
34
 
35
35
  ```ts
36
- new CNSDevToolsServerRepositoryRedis(redis, { keyPrefix: 'cns:' });
36
+ import Redis from 'ioredis';
37
+ import { CNSDevToolsServerRepositoryRedis } from '@cnstra/devtools-server-repository-redis';
38
+
39
+ new CNSDevToolsServerRepositoryRedis(new Redis(), { keyPrefix: 'cns:' });
37
40
  ```
38
41
 
39
42
  ## Data layout
40
43
 
41
- With the default `cns:` prefix: `cns:apps` / `cns:topology` / `cns:cnsToApp` (hashes),
42
- `cns:stim:{id}` (stimulation JSON), `cns:stims:{appId}` (zset scored by `startedAt` for
43
- ordering/paging and time-range filters), `cns:hops:{stimId}` (list), `cns:appToCns:{appId}`
44
- (set). `hasError` / `collateralId` / `neuronId` filters are applied in memory after the
45
- time-range zset scan — fine at DevTools scale.
44
+ With the default `cns:` prefix: `cns:apps` (hash, appId → app JSON), `cns:topology` (hash,
45
+ cnsId → snapshot JSON; the app of a CNS is read from its snapshot). Run history (Stimulation → Attempt → Task) is not kept here: it lives in the
46
+ `ICNSStimulationRepository` you pass as the second argument of `CNSDevToolsServer`.
46
47
 
47
48
  ## License
48
49
 
package/dist/index.cjs CHANGED
@@ -7,14 +7,6 @@ var CNSDevToolsServerRepositoryRedis = class {
7
7
  this.redis = redis;
8
8
  this.p = options.keyPrefix ?? "cns:";
9
9
  }
10
- // ─── Apps ───────────────────────────────────────────────────────────────
11
- async upsertApp(app) {
12
- await this.redis.hset(`${this.p}apps`, app.id, JSON.stringify(app));
13
- }
14
- async listApps() {
15
- const all = await this.redis.hgetall(`${this.p}apps`);
16
- return Object.values(all ?? {}).map((v) => JSON.parse(v));
17
- }
18
10
  // ─── Topology ───────────────────────────────────────────────────────────
19
11
  async saveTopology(snapshot) {
20
12
  await this.redis.hset(
@@ -34,17 +26,9 @@ var CNSDevToolsServerRepositoryRedis = class {
34
26
  (v) => JSON.parse(v)
35
27
  );
36
28
  }
37
- // ─── CNS ↔ App mapping ──────────────────────────────────────────────────
38
- async addCnsToApp(appId, cnsId) {
39
- await this.redis.hset(`${this.p}cnsToApp`, cnsId, appId);
40
- await this.redis.sadd(`${this.p}appToCns:${appId}`, cnsId);
41
- }
42
- async getCnsByApp(appId) {
43
- return this.redis.smembers(`${this.p}appToCns:${appId}`);
44
- }
45
29
  async findAppByCns(cnsId) {
46
- const appId = await this.redis.hget(`${this.p}cnsToApp`, cnsId);
47
- return appId ?? void 0;
30
+ const [snapshot] = await this.getTopology(cnsId);
31
+ return snapshot?.appId;
48
32
  }
49
33
  };
50
34
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ICNSDevToolsServerRepository,\n CNSDTOTopologySnapshot,\n} from '@cnstra/devtools-server';\nimport type { CNSDTOApp } from '@cnstra/devtools-dto';\n\n/**\n * Minimal structural view of an `ioredis` client — only the commands this\n * repository uses. A real `Redis` instance from `ioredis` satisfies it, so we\n * avoid a hard dependency on `ioredis` and its types.\n *\n * Note: this targets ioredis-style lowercase command methods. `node-redis` v4\n * uses different names (hSet, hGetAll, …) and is not directly compatible.\n */\nexport interface IRedisLike {\n hset(key: string, field: string, value: string): Promise<unknown>;\n hget(key: string, field: string): Promise<string | null>;\n hgetall(key: string): Promise<Record<string, string>>;\n sadd(key: string, member: string): Promise<unknown>;\n smembers(key: string): Promise<string[]>;\n}\n\nexport interface CNSDevToolsServerRepositoryRedisOptions {\n /** Key namespace prefix (default \"cns:\"). */\n keyPrefix?: string;\n}\n\nconst parse = <T>(s: string | null): T | null =>\n s == null ? null : (JSON.parse(s) as T);\n\n/**\n * Redis-backed repository for the CNStra DevTools server. Durable counterpart of\n * `@cnstra/devtools-server-repository-in-memory` with the same\n * `ICNSDevToolsServerRepository` contract.\n *\n * Data layout (with the default `cns:` prefix):\n * - `cns:apps` hash appId → app JSON\n * - `cns:topology` hash cnsId → snapshot JSON\n * - `cns:cnsToApp` hash cnsId → appId\n * - `cns:appToCns:{appId}` set cnsId\n */\nexport class CNSDevToolsServerRepositoryRedis\n implements ICNSDevToolsServerRepository\n{\n private readonly p: string;\n\n constructor(\n private readonly redis: IRedisLike,\n options: CNSDevToolsServerRepositoryRedisOptions = {}\n ) {\n this.p = options.keyPrefix ?? 'cns:';\n }\n\n // ─── Apps ───────────────────────────────────────────────────────────────\n\n async upsertApp(app: CNSDTOApp): Promise<void> {\n await this.redis.hset(`${this.p}apps`, app.id, JSON.stringify(app));\n }\n\n async listApps(): Promise<CNSDTOApp[]> {\n const all = await this.redis.hgetall(`${this.p}apps`);\n return Object.values(all ?? {}).map(v => JSON.parse(v) as CNSDTOApp);\n }\n\n // ─── Topology ───────────────────────────────────────────────────────────\n\n async saveTopology(snapshot: CNSDTOTopologySnapshot): Promise<void> {\n await this.redis.hset(\n `${this.p}topology`,\n snapshot.cnsId,\n JSON.stringify(snapshot)\n );\n }\n\n async getTopology(cnsId?: string): Promise<CNSDTOTopologySnapshot[]> {\n if (cnsId) {\n const raw = await this.redis.hget(`${this.p}topology`, cnsId);\n const snap = parse<CNSDTOTopologySnapshot>(raw);\n return snap ? [snap] : [];\n }\n const all = await this.redis.hgetall(`${this.p}topology`);\n return Object.values(all ?? {}).map(\n v => JSON.parse(v) as CNSDTOTopologySnapshot\n );\n }\n\n // ─── CNS ↔ App mapping ──────────────────────────────────────────────────\n\n async addCnsToApp(appId: string, cnsId: string): Promise<void> {\n await this.redis.hset(`${this.p}cnsToApp`, cnsId, appId);\n await this.redis.sadd(`${this.p}appToCns:${appId}`, cnsId);\n }\n\n async getCnsByApp(appId: string): Promise<string[]> {\n return this.redis.smembers(`${this.p}appToCns:${appId}`);\n }\n\n async findAppByCns(cnsId: string): Promise<string | undefined> {\n const appId = await this.redis.hget(`${this.p}cnsToApp`, cnsId);\n return appId ?? undefined;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAwBA,IAAM,KAAA,GAAQ,CAAI,CAAA,KACd,CAAA,IAAK,OAAO,IAAA,GAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAU7B,IAAM,mCAAN,MAEP;AAAA,EAGI,WAAA,CACqB,KAAA,EACjB,OAAA,GAAoD,EAAC,EACvD;AAFmB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAGjB,IAAA,IAAA,CAAK,CAAA,GAAI,QAAQ,SAAA,IAAa,MAAA;AAAA,EAClC;AAAA;AAAA,EAIA,MAAM,aAAa,QAAA,EAAkD;AACjE,IAAA,MAAM,KAAK,KAAA,CAAM,IAAA;AAAA,MACb,CAAA,EAAG,KAAK,CAAC,CAAA,QAAA,CAAA;AAAA,MACT,QAAA,CAAS,KAAA;AAAA,MACT,IAAA,CAAK,UAAU,QAAQ;AAAA,KAC3B;AAAA,EACJ;AAAA,EAEA,MAAM,YAAY,KAAA,EAAoD;AAClE,IAAA,IAAI,KAAA,EAAO;AACP,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,QAAA,CAAA,EAAY,KAAK,CAAA;AAC5D,MAAA,MAAM,IAAA,GAAO,MAA+B,GAAG,CAAA;AAC/C,MAAA,OAAO,IAAA,GAAO,CAAC,IAAI,CAAA,GAAI,EAAC;AAAA,IAC5B;AACA,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,QAAA,CAAU,CAAA;AACxD,IAAA,OAAO,MAAA,CAAO,MAAA,CAAO,GAAA,IAAO,EAAE,CAAA,CAAE,GAAA;AAAA,MAC5B,CAAA,CAAA,KAAK,IAAA,CAAK,KAAA,CAAM,CAAC;AAAA,KACrB;AAAA,EACJ;AAAA,EAEA,MAAM,aAAa,KAAA,EAA4C;AAC3D,IAAA,MAAM,CAAC,QAAQ,CAAA,GAAI,MAAM,IAAA,CAAK,YAAY,KAAK,CAAA;AAC/C,IAAA,OAAO,QAAA,EAAU,KAAA;AAAA,EACrB;AACJ","file":"index.cjs","sourcesContent":["import type {\n ICNSDevToolsServerRepository,\n TCNSTopologySnapshotDTO,\n} from '@cnstra/devtools-server';\n\n/**\n * Minimal structural view of an `ioredis` client — only the commands this\n * repository uses. A real `Redis` instance from `ioredis` satisfies it, so we\n * avoid a hard dependency on `ioredis` and its types.\n *\n * Note: this targets ioredis-style lowercase command methods. `node-redis` v4\n * uses different names (hSet, hGetAll, …) and is not directly compatible.\n */\nexport interface ICNSRedisLike {\n hset(key: string, field: string, value: string): Promise<unknown>;\n hget(key: string, field: string): Promise<string | null>;\n hgetall(key: string): Promise<Record<string, string>>;\n}\n\nexport interface ICNSDevToolsServerRepositoryRedisOptions {\n /** Key namespace prefix (default \"cns:\"). */\n keyPrefix?: string;\n}\n\nconst parse = <T>(s: string | null): T | null =>\n s == null ? null : (JSON.parse(s) as T);\n\n/**\n * Redis-backed repository for the CNStra DevTools server. Resumable counterpart of\n * `@cnstra/devtools-server-repository-in-memory` with the same\n * `ICNSDevToolsServerRepository` contract.\n *\n * Data layout (with the default `cns:` prefix):\n * - `cns:topology` hash cnsId → snapshot JSON\n */\nexport class CNSDevToolsServerRepositoryRedis\n implements ICNSDevToolsServerRepository\n{\n private readonly p: string;\n\n constructor(\n private readonly redis: ICNSRedisLike,\n options: ICNSDevToolsServerRepositoryRedisOptions = {}\n ) {\n this.p = options.keyPrefix ?? 'cns:';\n }\n\n // ─── Topology ───────────────────────────────────────────────────────────\n\n async saveTopology(snapshot: TCNSTopologySnapshotDTO): Promise<void> {\n await this.redis.hset(\n `${this.p}topology`,\n snapshot.cnsId,\n JSON.stringify(snapshot)\n );\n }\n\n async getTopology(cnsId?: string): Promise<TCNSTopologySnapshotDTO[]> {\n if (cnsId) {\n const raw = await this.redis.hget(`${this.p}topology`, cnsId);\n const snap = parse<TCNSTopologySnapshotDTO>(raw);\n return snap ? [snap] : [];\n }\n const all = await this.redis.hgetall(`${this.p}topology`);\n return Object.values(all ?? {}).map(\n v => JSON.parse(v) as TCNSTopologySnapshotDTO\n );\n }\n\n async findAppByCns(cnsId: string): Promise<string | undefined> {\n const [snapshot] = await this.getTopology(cnsId);\n return snapshot?.appId;\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { ICNSDevToolsServerRepository, CNSDTOTopologySnapshot } from '@cnstra/devtools-server';
2
- import type { CNSDTOApp } from '@cnstra/devtools-dto';
1
+ import type { ICNSDevToolsServerRepository, TCNSTopologySnapshotDTO } from '@cnstra/devtools-server';
3
2
  /**
4
3
  * Minimal structural view of an `ioredis` client — only the commands this
5
4
  * repository uses. A real `Redis` instance from `ioredis` satisfies it, so we
@@ -8,38 +7,29 @@ import type { CNSDTOApp } from '@cnstra/devtools-dto';
8
7
  * Note: this targets ioredis-style lowercase command methods. `node-redis` v4
9
8
  * uses different names (hSet, hGetAll, …) and is not directly compatible.
10
9
  */
11
- export interface IRedisLike {
10
+ export interface ICNSRedisLike {
12
11
  hset(key: string, field: string, value: string): Promise<unknown>;
13
12
  hget(key: string, field: string): Promise<string | null>;
14
13
  hgetall(key: string): Promise<Record<string, string>>;
15
- sadd(key: string, member: string): Promise<unknown>;
16
- smembers(key: string): Promise<string[]>;
17
14
  }
18
- export interface CNSDevToolsServerRepositoryRedisOptions {
15
+ export interface ICNSDevToolsServerRepositoryRedisOptions {
19
16
  /** Key namespace prefix (default "cns:"). */
20
17
  keyPrefix?: string;
21
18
  }
22
19
  /**
23
- * Redis-backed repository for the CNStra DevTools server. Durable counterpart of
20
+ * Redis-backed repository for the CNStra DevTools server. Resumable counterpart of
24
21
  * `@cnstra/devtools-server-repository-in-memory` with the same
25
22
  * `ICNSDevToolsServerRepository` contract.
26
23
  *
27
24
  * Data layout (with the default `cns:` prefix):
28
- * - `cns:apps` hash appId → app JSON
29
25
  * - `cns:topology` hash cnsId → snapshot JSON
30
- * - `cns:cnsToApp` hash cnsId → appId
31
- * - `cns:appToCns:{appId}` set cnsId
32
26
  */
33
27
  export declare class CNSDevToolsServerRepositoryRedis implements ICNSDevToolsServerRepository {
34
28
  private readonly redis;
35
29
  private readonly p;
36
- constructor(redis: IRedisLike, options?: CNSDevToolsServerRepositoryRedisOptions);
37
- upsertApp(app: CNSDTOApp): Promise<void>;
38
- listApps(): Promise<CNSDTOApp[]>;
39
- saveTopology(snapshot: CNSDTOTopologySnapshot): Promise<void>;
40
- getTopology(cnsId?: string): Promise<CNSDTOTopologySnapshot[]>;
41
- addCnsToApp(appId: string, cnsId: string): Promise<void>;
42
- getCnsByApp(appId: string): Promise<string[]>;
30
+ constructor(redis: ICNSRedisLike, options?: ICNSDevToolsServerRepositoryRedisOptions);
31
+ saveTopology(snapshot: TCNSTopologySnapshotDTO): Promise<void>;
32
+ getTopology(cnsId?: string): Promise<TCNSTopologySnapshotDTO[]>;
43
33
  findAppByCns(cnsId: string): Promise<string | undefined>;
44
34
  }
45
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,4BAA4B,EAC5B,sBAAsB,EACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,uCAAuC;IACpD,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;;;;;;GAUG;AACH,qBAAa,gCACT,YAAW,4BAA4B;IAKnC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAH1B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;gBAGN,KAAK,EAAE,UAAU,EAClC,OAAO,GAAE,uCAA4C;IAOnD,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,QAAQ,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAOhC,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7D,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAc9D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI7C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAIjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,4BAA4B,EAC5B,uBAAuB,EAC1B,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,wCAAwC;IACrD,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;;;GAOG;AACH,qBAAa,gCACT,YAAW,4BAA4B;IAKnC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAH1B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;gBAGN,KAAK,EAAE,aAAa,EACrC,OAAO,GAAE,wCAA6C;IAOpD,YAAY,CAAC,QAAQ,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAY/D,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAIjE"}
package/dist/index.js CHANGED
@@ -5,14 +5,6 @@ var CNSDevToolsServerRepositoryRedis = class {
5
5
  this.redis = redis;
6
6
  this.p = options.keyPrefix ?? "cns:";
7
7
  }
8
- // ─── Apps ───────────────────────────────────────────────────────────────
9
- async upsertApp(app) {
10
- await this.redis.hset(`${this.p}apps`, app.id, JSON.stringify(app));
11
- }
12
- async listApps() {
13
- const all = await this.redis.hgetall(`${this.p}apps`);
14
- return Object.values(all ?? {}).map((v) => JSON.parse(v));
15
- }
16
8
  // ─── Topology ───────────────────────────────────────────────────────────
17
9
  async saveTopology(snapshot) {
18
10
  await this.redis.hset(
@@ -32,17 +24,9 @@ var CNSDevToolsServerRepositoryRedis = class {
32
24
  (v) => JSON.parse(v)
33
25
  );
34
26
  }
35
- // ─── CNS ↔ App mapping ──────────────────────────────────────────────────
36
- async addCnsToApp(appId, cnsId) {
37
- await this.redis.hset(`${this.p}cnsToApp`, cnsId, appId);
38
- await this.redis.sadd(`${this.p}appToCns:${appId}`, cnsId);
39
- }
40
- async getCnsByApp(appId) {
41
- return this.redis.smembers(`${this.p}appToCns:${appId}`);
42
- }
43
27
  async findAppByCns(cnsId) {
44
- const appId = await this.redis.hget(`${this.p}cnsToApp`, cnsId);
45
- return appId ?? void 0;
28
+ const [snapshot] = await this.getTopology(cnsId);
29
+ return snapshot?.appId;
46
30
  }
47
31
  };
48
32
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ICNSDevToolsServerRepository,\n CNSDTOTopologySnapshot,\n} from '@cnstra/devtools-server';\nimport type { CNSDTOApp } from '@cnstra/devtools-dto';\n\n/**\n * Minimal structural view of an `ioredis` client — only the commands this\n * repository uses. A real `Redis` instance from `ioredis` satisfies it, so we\n * avoid a hard dependency on `ioredis` and its types.\n *\n * Note: this targets ioredis-style lowercase command methods. `node-redis` v4\n * uses different names (hSet, hGetAll, …) and is not directly compatible.\n */\nexport interface IRedisLike {\n hset(key: string, field: string, value: string): Promise<unknown>;\n hget(key: string, field: string): Promise<string | null>;\n hgetall(key: string): Promise<Record<string, string>>;\n sadd(key: string, member: string): Promise<unknown>;\n smembers(key: string): Promise<string[]>;\n}\n\nexport interface CNSDevToolsServerRepositoryRedisOptions {\n /** Key namespace prefix (default \"cns:\"). */\n keyPrefix?: string;\n}\n\nconst parse = <T>(s: string | null): T | null =>\n s == null ? null : (JSON.parse(s) as T);\n\n/**\n * Redis-backed repository for the CNStra DevTools server. Durable counterpart of\n * `@cnstra/devtools-server-repository-in-memory` with the same\n * `ICNSDevToolsServerRepository` contract.\n *\n * Data layout (with the default `cns:` prefix):\n * - `cns:apps` hash appId → app JSON\n * - `cns:topology` hash cnsId → snapshot JSON\n * - `cns:cnsToApp` hash cnsId → appId\n * - `cns:appToCns:{appId}` set cnsId\n */\nexport class CNSDevToolsServerRepositoryRedis\n implements ICNSDevToolsServerRepository\n{\n private readonly p: string;\n\n constructor(\n private readonly redis: IRedisLike,\n options: CNSDevToolsServerRepositoryRedisOptions = {}\n ) {\n this.p = options.keyPrefix ?? 'cns:';\n }\n\n // ─── Apps ───────────────────────────────────────────────────────────────\n\n async upsertApp(app: CNSDTOApp): Promise<void> {\n await this.redis.hset(`${this.p}apps`, app.id, JSON.stringify(app));\n }\n\n async listApps(): Promise<CNSDTOApp[]> {\n const all = await this.redis.hgetall(`${this.p}apps`);\n return Object.values(all ?? {}).map(v => JSON.parse(v) as CNSDTOApp);\n }\n\n // ─── Topology ───────────────────────────────────────────────────────────\n\n async saveTopology(snapshot: CNSDTOTopologySnapshot): Promise<void> {\n await this.redis.hset(\n `${this.p}topology`,\n snapshot.cnsId,\n JSON.stringify(snapshot)\n );\n }\n\n async getTopology(cnsId?: string): Promise<CNSDTOTopologySnapshot[]> {\n if (cnsId) {\n const raw = await this.redis.hget(`${this.p}topology`, cnsId);\n const snap = parse<CNSDTOTopologySnapshot>(raw);\n return snap ? [snap] : [];\n }\n const all = await this.redis.hgetall(`${this.p}topology`);\n return Object.values(all ?? {}).map(\n v => JSON.parse(v) as CNSDTOTopologySnapshot\n );\n }\n\n // ─── CNS ↔ App mapping ──────────────────────────────────────────────────\n\n async addCnsToApp(appId: string, cnsId: string): Promise<void> {\n await this.redis.hset(`${this.p}cnsToApp`, cnsId, appId);\n await this.redis.sadd(`${this.p}appToCns:${appId}`, cnsId);\n }\n\n async getCnsByApp(appId: string): Promise<string[]> {\n return this.redis.smembers(`${this.p}appToCns:${appId}`);\n }\n\n async findAppByCns(cnsId: string): Promise<string | undefined> {\n const appId = await this.redis.hget(`${this.p}cnsToApp`, cnsId);\n return appId ?? undefined;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAwBA,IAAM,KAAA,GAAQ,CAAI,CAAA,KACd,CAAA,IAAK,OAAO,IAAA,GAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAU7B,IAAM,mCAAN,MAEP;AAAA,EAGI,WAAA,CACqB,KAAA,EACjB,OAAA,GAAoD,EAAC,EACvD;AAFmB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAGjB,IAAA,IAAA,CAAK,CAAA,GAAI,QAAQ,SAAA,IAAa,MAAA;AAAA,EAClC;AAAA;AAAA,EAIA,MAAM,aAAa,QAAA,EAAkD;AACjE,IAAA,MAAM,KAAK,KAAA,CAAM,IAAA;AAAA,MACb,CAAA,EAAG,KAAK,CAAC,CAAA,QAAA,CAAA;AAAA,MACT,QAAA,CAAS,KAAA;AAAA,MACT,IAAA,CAAK,UAAU,QAAQ;AAAA,KAC3B;AAAA,EACJ;AAAA,EAEA,MAAM,YAAY,KAAA,EAAoD;AAClE,IAAA,IAAI,KAAA,EAAO;AACP,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,QAAA,CAAA,EAAY,KAAK,CAAA;AAC5D,MAAA,MAAM,IAAA,GAAO,MAA+B,GAAG,CAAA;AAC/C,MAAA,OAAO,IAAA,GAAO,CAAC,IAAI,CAAA,GAAI,EAAC;AAAA,IAC5B;AACA,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,QAAA,CAAU,CAAA;AACxD,IAAA,OAAO,MAAA,CAAO,MAAA,CAAO,GAAA,IAAO,EAAE,CAAA,CAAE,GAAA;AAAA,MAC5B,CAAA,CAAA,KAAK,IAAA,CAAK,KAAA,CAAM,CAAC;AAAA,KACrB;AAAA,EACJ;AAAA,EAEA,MAAM,aAAa,KAAA,EAA4C;AAC3D,IAAA,MAAM,CAAC,QAAQ,CAAA,GAAI,MAAM,IAAA,CAAK,YAAY,KAAK,CAAA;AAC/C,IAAA,OAAO,QAAA,EAAU,KAAA;AAAA,EACrB;AACJ","file":"index.js","sourcesContent":["import type {\n ICNSDevToolsServerRepository,\n TCNSTopologySnapshotDTO,\n} from '@cnstra/devtools-server';\n\n/**\n * Minimal structural view of an `ioredis` client — only the commands this\n * repository uses. A real `Redis` instance from `ioredis` satisfies it, so we\n * avoid a hard dependency on `ioredis` and its types.\n *\n * Note: this targets ioredis-style lowercase command methods. `node-redis` v4\n * uses different names (hSet, hGetAll, …) and is not directly compatible.\n */\nexport interface ICNSRedisLike {\n hset(key: string, field: string, value: string): Promise<unknown>;\n hget(key: string, field: string): Promise<string | null>;\n hgetall(key: string): Promise<Record<string, string>>;\n}\n\nexport interface ICNSDevToolsServerRepositoryRedisOptions {\n /** Key namespace prefix (default \"cns:\"). */\n keyPrefix?: string;\n}\n\nconst parse = <T>(s: string | null): T | null =>\n s == null ? null : (JSON.parse(s) as T);\n\n/**\n * Redis-backed repository for the CNStra DevTools server. Resumable counterpart of\n * `@cnstra/devtools-server-repository-in-memory` with the same\n * `ICNSDevToolsServerRepository` contract.\n *\n * Data layout (with the default `cns:` prefix):\n * - `cns:topology` hash cnsId → snapshot JSON\n */\nexport class CNSDevToolsServerRepositoryRedis\n implements ICNSDevToolsServerRepository\n{\n private readonly p: string;\n\n constructor(\n private readonly redis: ICNSRedisLike,\n options: ICNSDevToolsServerRepositoryRedisOptions = {}\n ) {\n this.p = options.keyPrefix ?? 'cns:';\n }\n\n // ─── Topology ───────────────────────────────────────────────────────────\n\n async saveTopology(snapshot: TCNSTopologySnapshotDTO): Promise<void> {\n await this.redis.hset(\n `${this.p}topology`,\n snapshot.cnsId,\n JSON.stringify(snapshot)\n );\n }\n\n async getTopology(cnsId?: string): Promise<TCNSTopologySnapshotDTO[]> {\n if (cnsId) {\n const raw = await this.redis.hget(`${this.p}topology`, cnsId);\n const snap = parse<TCNSTopologySnapshotDTO>(raw);\n return snap ? [snap] : [];\n }\n const all = await this.redis.hgetall(`${this.p}topology`);\n return Object.values(all ?? {}).map(\n v => JSON.parse(v) as TCNSTopologySnapshotDTO\n );\n }\n\n async findAppByCns(cnsId: string): Promise<string | undefined> {\n const [snapshot] = await this.getTopology(cnsId);\n return snapshot?.appId;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@cnstra/devtools-server-repository-redis",
3
- "version": "0.2.0",
4
- "description": "Redis repository for CNStra DevTools server (durable stimulations & hops)",
3
+ "version": "0.3.1",
4
+ "description": "Redis repository for CNStra DevTools server (resumable stimulations & hops)",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/abaikov/cnstra.git",
8
+ "directory": "packages/devtools-server-repository-redis"
9
+ },
5
10
  "keywords": [
6
11
  "cnstra",
7
12
  "devtools",
@@ -22,6 +27,7 @@
22
27
  }
23
28
  },
24
29
  "scripts": {
30
+ "prepublishOnly": "node ../../scripts/check-drift.mjs --gate",
25
31
  "build": "tsup && tsc --emitDeclarationOnly",
26
32
  "build:types": "tsc --emitDeclarationOnly",
27
33
  "test": "jest",
@@ -31,14 +37,14 @@
31
37
  "dist"
32
38
  ],
33
39
  "dependencies": {
34
- "@cnstra/devtools-dto": "^2.0.0"
40
+ "@cnstra/devtools-dto": "^3.0.0"
35
41
  },
36
42
  "peerDependencies": {
37
- "@cnstra/devtools-server": "^2.0.0",
43
+ "@cnstra/devtools-server": "^3.0.0",
38
44
  "ioredis": ">=5"
39
45
  },
40
46
  "devDependencies": {
41
- "@cnstra/devtools-server": "^2.0.0",
47
+ "@cnstra/devtools-server": "^3.0.0",
42
48
  "@types/jest": "^29.5.5",
43
49
  "ioredis-mock": "^8.9.0",
44
50
  "jest": "^29.7.0",