@rljson/io 0.0.21 → 0.0.22

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/dist/io-mem.d.ts CHANGED
@@ -6,7 +6,7 @@ import { Io } from './io.ts';
6
6
  */
7
7
  export declare class IoMem implements Io {
8
8
  constructor();
9
- static example: () => IoMem;
9
+ static example: () => Promise<IoMem>;
10
10
  isReady(): Promise<void>;
11
11
  dump(): Promise<Rljson>;
12
12
  dumpTable(request: {
@@ -18,20 +18,21 @@ export declare class IoMem implements Io {
18
18
  [column: string]: JsonValue;
19
19
  };
20
20
  }): Promise<Rljson>;
21
+ rowCount(table: string): Promise<number>;
21
22
  write(request: {
22
23
  data: Rljson;
23
24
  }): Promise<void>;
24
- createTable(request: {
25
+ createOrExtendTable(request: {
25
26
  tableCfg: TableCfg;
26
27
  }): Promise<void>;
27
28
  tableCfgs(): Promise<Rljson>;
28
- allTableNames(): Promise<string[]>;
29
+ private _ioTools;
29
30
  private _isReady;
30
31
  private _mem;
31
32
  private _ioInit;
32
33
  private _init;
33
34
  private _initTableCfgs;
34
- private _createTable;
35
+ private _createOrExtendTable;
35
36
  private _dump;
36
37
  private _dumpTable;
37
38
  private _write;
@@ -0,0 +1,40 @@
1
+ import { TableCfg, TableKey } from '@rljson/rljson';
2
+ import { Io } from './io.ts';
3
+ /**
4
+ * Provides utility functions for the Io interface.
5
+ */
6
+ export declare class IoTools {
7
+ readonly io: Io;
8
+ /**
9
+ * Constructor
10
+ * @param io The Io interface to use
11
+ */
12
+ constructor(io: Io);
13
+ /**
14
+ * Example object for test purposes
15
+ * @returns An instance of io tools
16
+ */
17
+ static example: () => Promise<IoTools>;
18
+ /**
19
+ * Returns a list with all table names
20
+ */
21
+ allTableNames(): Promise<string[]>;
22
+ /**
23
+ * Returns the configuration of a given table
24
+ */
25
+ tableCfg(table: TableKey): Promise<TableCfg>;
26
+ /**
27
+ * Returns the configuration of a given table or null if it does not exist.
28
+
29
+ */
30
+ tableCfgOrNull(table: TableKey): Promise<TableCfg | null>;
31
+ /**
32
+ * Returns a list of all column names of a given table
33
+ */
34
+ allColumnKeys(table: TableKey): Promise<string[]>;
35
+ /**
36
+ * Throws when a table update is not compatible with the current table
37
+ * configuration.
38
+ */
39
+ throwWhenTableIsNotCompatible(update: TableCfg): Promise<void>;
40
+ }
package/dist/io.d.ts CHANGED
@@ -13,21 +13,19 @@ export interface Io {
13
13
  table: string;
14
14
  }): Promise<Rljson>;
15
15
  /**
16
- * Creates a table with a given config
17
- *
18
- * The config must be already in the database
16
+ * Creates a table with a given config.
17
+ * If the table already exists, new columns are added to the existing table.
18
+ * If the table does not exist, it is created with the given config.
19
+ * If the table exists and columns are removed, an error is thrown.
20
+ * If the table exists and the column type is changed, an error is thrown.
19
21
  */
20
- createTable(request: {
22
+ createOrExtendTable(request: {
21
23
  tableCfg: TableCfg;
22
24
  }): Promise<void>;
23
25
  /**
24
26
  * Returns a json structure returning current table configurations
25
27
  */
26
28
  tableCfgs(): Promise<Rljson>;
27
- /**
28
- * Returns an rljson with all available tables without data
29
- */
30
- allTableNames(): Promise<string[]>;
31
29
  /** Writes Rljson data into the database */
32
30
  write(request: {
33
31
  data: Rljson;
@@ -39,5 +37,7 @@ export interface Io {
39
37
  [column: string]: JsonValue;
40
38
  };
41
39
  }): Promise<Rljson>;
40
+ /** Returns the number of rows in the given table */
41
+ rowCount(table: string): Promise<number>;
42
42
  }
43
43
  export declare const exampleIo = "Checkout @rljson/io-mem for an example implementation";
package/dist/io.js CHANGED
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { hip, hsh } from "@rljson/hash";
5
5
  import { IsReady } from "@rljson/is-ready";
6
- import { copy, equals } from "@rljson/json";
6
+ import { jsonValueTypes, copy, equals } from "@rljson/json";
7
7
  // @license
8
8
  class IoInit {
9
9
  constructor(io) {
@@ -15,15 +15,15 @@ class IoInit {
15
15
  isHead: false,
16
16
  isRoot: false,
17
17
  isShared: true,
18
- columns: {
19
- table: { type: "string" },
20
- predecessor: { type: "string" },
21
- successor: { type: "string" },
22
- timestamp: { type: "number" },
23
- id: { type: "string" }
24
- }
18
+ columns: [
19
+ { key: "table", type: "string" },
20
+ { key: "predecessor", type: "string" },
21
+ { key: "successor", type: "string" },
22
+ { key: "timestamp", type: "number" },
23
+ { key: "id", type: "string" }
24
+ ]
25
25
  };
26
- await this.io.createTable({ tableCfg });
26
+ await this.io.createOrExtendTable({ tableCfg });
27
27
  });
28
28
  this.io = io;
29
29
  }
@@ -35,15 +35,117 @@ class IoInit {
35
35
  isHead: false,
36
36
  isRoot: false,
37
37
  isShared: true,
38
- columns: {
39
- key: { type: "string" },
40
- type: { type: "string" }
41
- }
38
+ columns: [
39
+ { key: "key", type: "string" },
40
+ { key: "type", type: "string" }
41
+ ]
42
42
  });
43
43
  return tableCfg;
44
44
  }
45
45
  }
46
46
  // @license
47
+ const _IoTools = class _IoTools {
48
+ /**
49
+ * Constructor
50
+ * @param io The Io interface to use
51
+ */
52
+ constructor(io) {
53
+ this.io = io;
54
+ }
55
+ /**
56
+ * Returns a list with all table names
57
+ */
58
+ async allTableNames() {
59
+ const result = (await this.io.tableCfgs()).tableCfgs._data.map(
60
+ (e) => e.key
61
+ );
62
+ return result;
63
+ }
64
+ /**
65
+ * Returns the configuration of a given table
66
+ */
67
+ async tableCfg(table) {
68
+ const tableCfg = await this.tableCfgOrNull(table);
69
+ if (!tableCfg) {
70
+ throw new Error(`Table "${table}" not found`);
71
+ }
72
+ return tableCfg;
73
+ }
74
+ /**
75
+ * Returns the configuration of a given table or null if it does not exist.
76
+
77
+ */
78
+ async tableCfgOrNull(table) {
79
+ const tableCfgs = await this.io.tableCfgs();
80
+ const tableCfg = tableCfgs.tableCfgs._data.find((e) => e.key === table);
81
+ return tableCfg ?? null;
82
+ }
83
+ /**
84
+ * Returns a list of all column names of a given table
85
+ */
86
+ async allColumnKeys(table) {
87
+ const tableCfg = await this.tableCfg(table);
88
+ const result = tableCfg.columns.map((column) => column.key);
89
+ return result;
90
+ }
91
+ /**
92
+ * Throws when a table update is not compatible with the current table
93
+ * configuration.
94
+ */
95
+ async throwWhenTableIsNotCompatible(update) {
96
+ const prefix = `Invalid update of table able "${update.key}"`;
97
+ for (const column of update.columns) {
98
+ if (!jsonValueTypes.includes(column.type)) {
99
+ throw new Error(
100
+ `${prefix}: Column "${column.key}" has an unsupported type "${column.type}"`
101
+ );
102
+ }
103
+ }
104
+ const existing = await this.tableCfgOrNull(update.key);
105
+ if (existing) {
106
+ if (existing.columns.length > update.columns.length) {
107
+ const deletedColumnKeys = existing.columns.map((column) => column.key).filter(
108
+ (key) => !update.columns.some((column) => column.key === key)
109
+ );
110
+ if (deletedColumnKeys.length > 0) {
111
+ const deletedColumns = deletedColumnKeys.join(", ");
112
+ throw new Error(
113
+ `${prefix}: Columns must not be deleted. Deleted columns: ${deletedColumns}}`
114
+ );
115
+ }
116
+ }
117
+ for (let i = 0; i < existing.columns.length; i++) {
118
+ const before = existing.columns[i].key;
119
+ const after = update.columns[i].key;
120
+ if (before !== after) {
121
+ throw new Error(
122
+ `${prefix}: Column keys must not change! Column "${before}" was renamed into "${after}".`
123
+ );
124
+ }
125
+ }
126
+ for (let i = 0; i < existing.columns.length; i++) {
127
+ const column = existing.columns[i].key;
128
+ const before = existing.columns[i].type;
129
+ const after = update.columns[i].type;
130
+ if (before !== after) {
131
+ throw new Error(
132
+ `${prefix}: Column types must not change! Type of column "${column}" was changed from "${before}" to ${after}.`
133
+ );
134
+ }
135
+ }
136
+ }
137
+ }
138
+ };
139
+ /**
140
+ * Example object for test purposes
141
+ * @returns An instance of io tools
142
+ */
143
+ __publicField(_IoTools, "example", async () => {
144
+ const io = await IoMem.example();
145
+ return new _IoTools(io);
146
+ });
147
+ let IoTools = _IoTools;
148
+ // @license
47
149
  const _IoMem = class _IoMem {
48
150
  // ...........................................................................
49
151
  // Constructor & example
@@ -51,6 +153,7 @@ const _IoMem = class _IoMem {
51
153
  // ######################
52
154
  // Private
53
155
  // ######################
156
+ __publicField(this, "_ioTools");
54
157
  __publicField(this, "_isReady", new IsReady());
55
158
  __publicField(this, "_mem", hip({}));
56
159
  __publicField(this, "_ioInit");
@@ -84,6 +187,13 @@ const _IoMem = class _IoMem {
84
187
  readRows(request) {
85
188
  return this._readRows(request);
86
189
  }
190
+ async rowCount(table) {
191
+ const tableData = this._mem[table];
192
+ if (!tableData) {
193
+ throw new Error(`Table "${table}" not found`);
194
+ }
195
+ return Promise.resolve(tableData._data.length);
196
+ }
87
197
  // ...........................................................................
88
198
  // Write
89
199
  write(request) {
@@ -91,8 +201,8 @@ const _IoMem = class _IoMem {
91
201
  }
92
202
  // ...........................................................................
93
203
  // Table management
94
- createTable(request) {
95
- return this._createTable(request);
204
+ createOrExtendTable(request) {
205
+ return this._createOrExtendTable(request);
96
206
  }
97
207
  async tableCfgs() {
98
208
  const tables = this._mem.tableCfgs._data;
@@ -113,25 +223,19 @@ const _IoMem = class _IoMem {
113
223
  }
114
224
  });
115
225
  }
116
- async allTableNames() {
117
- const tables = Object.keys(this._mem).filter((key) => !key.startsWith("_"));
118
- return tables;
119
- }
120
226
  // ...........................................................................
121
227
  async _init() {
228
+ this._ioTools = new IoTools(this);
122
229
  this._ioInit = new IoInit(this);
123
230
  this._initTableCfgs();
124
231
  await this._ioInit.initRevisionsTable();
125
232
  this._isReady.resolve();
126
233
  }
127
234
  // ...........................................................................
128
- async _createTable(request) {
235
+ async _createOrExtendTable(request) {
129
236
  var _a;
130
- const { key, type } = request.tableCfg;
131
- const existing = this._mem[key];
132
- if (existing) {
133
- throw new Error(`Table ${key} already exists`);
134
- }
237
+ await this._ioTools.throwWhenTableIsNotCompatible(request.tableCfg);
238
+ const { type, key } = request.tableCfg;
135
239
  const newConfig = hsh(request.tableCfg);
136
240
  const existingConfig = this._mem.tableCfgs._data.find(
137
241
  (cfg) => cfg._hash === newConfig._hash
@@ -139,9 +243,10 @@ const _IoMem = class _IoMem {
139
243
  if (!existingConfig) {
140
244
  this._mem.tableCfgs._data.push(newConfig);
141
245
  this._mem.tableCfgs._hash = "";
142
- const updateExistingHashes = false;
143
- const throwOnWrongHashes = false;
144
- hip(this._mem.tableCfgs, { updateExistingHashes, throwOnWrongHashes });
246
+ hip(this._mem.tableCfgs, {
247
+ updateExistingHashes: false,
248
+ throwOnWrongHashes: false
249
+ });
145
250
  }
146
251
  const table = {
147
252
  _data: [],
@@ -217,7 +322,7 @@ const _IoMem = class _IoMem {
217
322
  return result;
218
323
  }
219
324
  };
220
- __publicField(_IoMem, "example", () => {
325
+ __publicField(_IoMem, "example", async () => {
221
326
  return new _IoMem();
222
327
  });
223
328
  let IoMem = _IoMem;
@@ -17,11 +17,15 @@ export const example = async () => {
17
17
  const rowWithHash = hsh(row);
18
18
 
19
19
  // Create a table config first
20
- const tableCfg = hip({
20
+ const tableCfg = hip<TableCfg>({
21
21
  key: 'tableA',
22
22
  type: 'ingredients',
23
- columns: {},
24
- } as TableCfg);
23
+ columns: [],
24
+ version: 1,
25
+ isHead: true,
26
+ isRoot: true,
27
+ isShared: false,
28
+ });
25
29
 
26
30
  await ioMem.write({
27
31
  data: {
@@ -33,7 +37,7 @@ export const example = async () => {
33
37
  });
34
38
 
35
39
  // Create a table first
36
- await ioMem.createTable({ tableCfg: tableCfg });
40
+ await ioMem.createOrExtendTable({ tableCfg: tableCfg });
37
41
 
38
42
  // Write data into the table
39
43
  await ioMem.write({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rljson/io",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "packageManager": "pnpm@10.6.2",
5
5
  "description": "Low level interface for reading and writing RLJSON data",
6
6
  "homepage": "https://github.com/rljson/io",
@@ -29,24 +29,24 @@
29
29
  "updateGoldens": "cross-env UPDATE_GOLDENS=true pnpm test"
30
30
  },
31
31
  "devDependencies": {
32
- "@types/node": "^22.14.1",
33
- "@typescript-eslint/eslint-plugin": "^8.30.1",
34
- "@typescript-eslint/parser": "^8.30.1",
35
- "@vitest/coverage-v8": "^3.1.1",
32
+ "@types/node": "^22.15.2",
33
+ "@typescript-eslint/eslint-plugin": "^8.31.0",
34
+ "@typescript-eslint/parser": "^8.31.0",
35
+ "@vitest/coverage-v8": "^3.1.2",
36
36
  "cross-env": "^7.0.3",
37
- "eslint": "^9.24.0",
38
- "eslint-plugin-jsdoc": "^50.6.9",
37
+ "eslint": "^9.25.1",
38
+ "eslint-plugin-jsdoc": "^50.6.11",
39
39
  "eslint-plugin-tsdoc": "^0.4.0",
40
40
  "globals": "^16.0.0",
41
41
  "jsdoc": "^4.0.4",
42
42
  "read-pkg": "^9.0.1",
43
43
  "typescript": "~5.8.3",
44
- "typescript-eslint": "^8.30.1",
45
- "vite": "^6.2.6",
46
- "vite-node": "^3.1.1",
44
+ "typescript-eslint": "^8.31.0",
45
+ "vite": "^6.3.3",
46
+ "vite-node": "^3.1.2",
47
47
  "vite-plugin-dts": "^4.5.3",
48
48
  "vite-tsconfig-paths": "^5.1.4",
49
- "vitest": "^3.1.1",
49
+ "vitest": "^3.1.2",
50
50
  "vitest-dom": "^0.1.1"
51
51
  },
52
52
  "pnpm": {
@@ -59,7 +59,7 @@
59
59
  "@rljson/hash": "^0.0.13",
60
60
  "@rljson/is-ready": "^0.0.17",
61
61
  "@rljson/json": "^0.0.18",
62
- "@rljson/rljson": "^0.0.38",
63
- "@rljson/validate": "^0.0.9"
62
+ "@rljson/rljson": "^0.0.40",
63
+ "@rljson/validate": "^0.0.10"
64
64
  }
65
65
  }