@stonyx/orm 0.2.1-beta.83 → 0.2.1-beta.84

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 (149) hide show
  1. package/dist/aggregates.d.ts +21 -0
  2. package/dist/aggregates.js +90 -0
  3. package/dist/attr.d.ts +2 -0
  4. package/dist/attr.js +22 -0
  5. package/dist/belongs-to.d.ts +11 -0
  6. package/dist/belongs-to.js +58 -0
  7. package/dist/cli.d.ts +22 -0
  8. package/dist/cli.js +148 -0
  9. package/dist/commands.d.ts +7 -0
  10. package/dist/commands.js +146 -0
  11. package/dist/db.d.ts +21 -0
  12. package/dist/db.js +174 -0
  13. package/dist/exports/db.d.ts +7 -0
  14. package/{src → dist}/exports/db.js +2 -4
  15. package/dist/has-many.d.ts +11 -0
  16. package/dist/has-many.js +57 -0
  17. package/dist/hooks.d.ts +47 -0
  18. package/dist/hooks.js +106 -0
  19. package/dist/index.d.ts +14 -0
  20. package/dist/index.js +34 -0
  21. package/dist/main.d.ts +46 -0
  22. package/dist/main.js +178 -0
  23. package/dist/manage-record.d.ts +13 -0
  24. package/dist/manage-record.js +113 -0
  25. package/dist/meta-request.d.ts +6 -0
  26. package/dist/meta-request.js +52 -0
  27. package/dist/migrate.d.ts +2 -0
  28. package/dist/migrate.js +57 -0
  29. package/dist/model-property.d.ts +9 -0
  30. package/dist/model-property.js +29 -0
  31. package/dist/model.d.ts +15 -0
  32. package/dist/model.js +18 -0
  33. package/dist/mysql/connection.d.ts +14 -0
  34. package/dist/mysql/connection.js +24 -0
  35. package/dist/mysql/migration-generator.d.ts +45 -0
  36. package/dist/mysql/migration-generator.js +245 -0
  37. package/dist/mysql/migration-runner.d.ts +12 -0
  38. package/dist/mysql/migration-runner.js +83 -0
  39. package/dist/mysql/mysql-db.d.ts +100 -0
  40. package/dist/mysql/mysql-db.js +411 -0
  41. package/dist/mysql/query-builder.d.ts +10 -0
  42. package/dist/mysql/query-builder.js +44 -0
  43. package/dist/mysql/schema-introspector.d.ts +19 -0
  44. package/dist/mysql/schema-introspector.js +286 -0
  45. package/dist/mysql/type-map.d.ts +21 -0
  46. package/dist/mysql/type-map.js +36 -0
  47. package/dist/orm-request.d.ts +38 -0
  48. package/dist/orm-request.js +453 -0
  49. package/dist/plural-registry.d.ts +4 -0
  50. package/{src → dist}/plural-registry.js +3 -6
  51. package/dist/postgres/connection.d.ts +15 -0
  52. package/dist/postgres/connection.js +30 -0
  53. package/dist/postgres/migration-generator.d.ts +45 -0
  54. package/dist/postgres/migration-generator.js +257 -0
  55. package/dist/postgres/migration-runner.d.ts +10 -0
  56. package/dist/postgres/migration-runner.js +82 -0
  57. package/dist/postgres/postgres-db.d.ts +119 -0
  58. package/dist/postgres/postgres-db.js +473 -0
  59. package/dist/postgres/query-builder.d.ts +27 -0
  60. package/dist/postgres/query-builder.js +98 -0
  61. package/dist/postgres/schema-introspector.d.ts +29 -0
  62. package/dist/postgres/schema-introspector.js +309 -0
  63. package/dist/postgres/type-map.d.ts +23 -0
  64. package/dist/postgres/type-map.js +53 -0
  65. package/dist/record.d.ts +75 -0
  66. package/dist/record.js +115 -0
  67. package/dist/relationships.d.ts +10 -0
  68. package/dist/relationships.js +35 -0
  69. package/dist/serializer.d.ts +17 -0
  70. package/dist/serializer.js +130 -0
  71. package/dist/setup-rest-server.d.ts +1 -0
  72. package/dist/setup-rest-server.js +54 -0
  73. package/dist/standalone-db.d.ts +58 -0
  74. package/dist/standalone-db.js +142 -0
  75. package/dist/store.d.ts +62 -0
  76. package/dist/store.js +271 -0
  77. package/dist/timescale/query-builder.d.ts +41 -0
  78. package/dist/timescale/query-builder.js +87 -0
  79. package/dist/timescale/timescale-db.d.ts +44 -0
  80. package/dist/timescale/timescale-db.js +81 -0
  81. package/dist/transforms.d.ts +2 -0
  82. package/dist/transforms.js +17 -0
  83. package/dist/types/orm-types.d.ts +142 -0
  84. package/dist/types/orm-types.js +1 -0
  85. package/dist/utils.d.ts +5 -0
  86. package/dist/utils.js +13 -0
  87. package/dist/view-resolver.d.ts +8 -0
  88. package/dist/view-resolver.js +165 -0
  89. package/dist/view.d.ts +11 -0
  90. package/dist/view.js +18 -0
  91. package/package.json +34 -11
  92. package/src/{aggregates.js → aggregates.ts} +27 -13
  93. package/src/{attr.js → attr.ts} +2 -2
  94. package/src/{belongs-to.js → belongs-to.ts} +36 -17
  95. package/src/{cli.js → cli.ts} +17 -11
  96. package/src/{commands.js → commands.ts} +179 -170
  97. package/src/{db.js → db.ts} +35 -26
  98. package/src/exports/db.ts +7 -0
  99. package/src/has-many.ts +91 -0
  100. package/src/{hooks.js → hooks.ts} +23 -27
  101. package/src/{index.js → index.ts} +4 -4
  102. package/src/{main.js → main.ts} +59 -34
  103. package/src/{manage-record.js → manage-record.ts} +41 -22
  104. package/src/{meta-request.js → meta-request.ts} +17 -14
  105. package/src/{migrate.js → migrate.ts} +9 -9
  106. package/src/{model-property.js → model-property.ts} +12 -6
  107. package/src/{model.js → model.ts} +5 -4
  108. package/src/mysql/{connection.js → connection.ts} +43 -28
  109. package/src/mysql/{migration-generator.js → migration-generator.ts} +332 -286
  110. package/src/mysql/{migration-runner.js → migration-runner.ts} +116 -110
  111. package/src/mysql/{mysql-db.js → mysql-db.ts} +533 -473
  112. package/src/mysql/{query-builder.js → query-builder.ts} +69 -64
  113. package/src/mysql/{schema-introspector.js → schema-introspector.ts} +355 -325
  114. package/src/mysql/{type-map.js → type-map.ts} +42 -37
  115. package/src/{orm-request.js → orm-request.ts} +165 -95
  116. package/src/plural-registry.ts +12 -0
  117. package/src/postgres/{connection.js → connection.ts} +14 -5
  118. package/src/postgres/{migration-generator.js → migration-generator.ts} +82 -38
  119. package/src/postgres/{migration-runner.js → migration-runner.ts} +11 -10
  120. package/src/postgres/{postgres-db.js → postgres-db.ts} +195 -114
  121. package/src/postgres/{query-builder.js → query-builder.ts} +27 -28
  122. package/src/postgres/{schema-introspector.js → schema-introspector.ts} +87 -58
  123. package/src/postgres/{type-map.js → type-map.ts} +10 -6
  124. package/src/{record.js → record.ts} +73 -34
  125. package/src/relationships.ts +48 -0
  126. package/src/{serializer.js → serializer.ts} +44 -36
  127. package/src/{setup-rest-server.js → setup-rest-server.ts} +18 -13
  128. package/src/{standalone-db.js → standalone-db.ts} +33 -24
  129. package/src/{store.js → store.ts} +90 -68
  130. package/src/timescale/{query-builder.js → query-builder.ts} +33 -38
  131. package/src/timescale/timescale-db.ts +107 -0
  132. package/src/transforms.ts +20 -0
  133. package/src/types/mysql2.d.ts +30 -0
  134. package/src/types/orm-types.ts +146 -0
  135. package/src/types/pg.d.ts +28 -0
  136. package/src/types/stonyx-cron.d.ts +5 -0
  137. package/src/types/stonyx-events.d.ts +4 -0
  138. package/src/types/stonyx-rest-server.d.ts +11 -0
  139. package/src/types/stonyx-utils.d.ts +33 -0
  140. package/src/types/stonyx.d.ts +21 -0
  141. package/src/utils.ts +16 -0
  142. package/src/{view-resolver.js → view-resolver.ts} +53 -28
  143. package/src/view.ts +22 -0
  144. package/src/has-many.js +0 -68
  145. package/src/relationships.js +0 -43
  146. package/src/timescale/timescale-db.js +0 -111
  147. package/src/transforms.js +0 -20
  148. package/src/utils.js +0 -12
  149. package/src/view.js +0 -21
@@ -0,0 +1,48 @@
1
+ import { relationships } from '@stonyx/orm';
2
+ import type { HasManyMap, BelongsToMap, GlobalMap, PendingMap, PendingBelongsToMap } from './types/orm-types.js';
3
+
4
+ // TODO: Refactor mapping to remove a level of iteration
5
+ export function getRelationships(type: string, sourceModel: string, targetModel: string, relationshipId?: string): Map<unknown, unknown> | undefined {
6
+ const allRelationships = relationships.get(type) as Map<string, Map<string, Map<unknown, unknown>>> | undefined;
7
+
8
+ // create relationship map for this type of it doesn't already exist
9
+ if (!allRelationships!.has(sourceModel)) allRelationships!.set(sourceModel, new Map());
10
+
11
+ const modelRelationship = allRelationships!.get(sourceModel)!;
12
+
13
+ if (!modelRelationship.has(targetModel)) modelRelationship.set(targetModel, new Map());
14
+
15
+ const relationship = modelRelationship.get(targetModel)!;
16
+
17
+ // TODO: Determine whether already having id should be handled differently
18
+ //if (relationship.has(relationshipId)) return;
19
+
20
+ return relationship;
21
+ }
22
+
23
+ export function getHasManyRelationships(sourceModel: string, targetModel: string): Map<unknown, unknown> | undefined {
24
+ return (relationships.get('hasMany') as HasManyMap | undefined)?.get(sourceModel)?.get(targetModel);
25
+ }
26
+
27
+ /** Typed accessors for the relationship registry */
28
+ export function getHasManyRegistry(): HasManyMap {
29
+ return relationships.get('hasMany') as HasManyMap;
30
+ }
31
+
32
+ export function getBelongsToRegistry(): BelongsToMap {
33
+ return relationships.get('belongsTo') as BelongsToMap;
34
+ }
35
+
36
+ export function getGlobalRegistry(): GlobalMap {
37
+ return relationships.get('global') as GlobalMap;
38
+ }
39
+
40
+ export function getPendingRegistry(): PendingMap {
41
+ return relationships.get('pending') as PendingMap;
42
+ }
43
+
44
+ export function getPendingBelongsToRegistry(): PendingBelongsToMap {
45
+ return relationships.get('pendingBelongsTo') as PendingBelongsToMap;
46
+ }
47
+
48
+ export const TYPES: string[] = ['global', 'hasMany', 'belongsTo', 'pending'];
@@ -1,46 +1,49 @@
1
1
  import config from 'stonyx/config';
2
2
  import { get, makeArray } from '@stonyx/utils/object';
3
+ import type { AggregateProperty } from './aggregates.js';
4
+ import type ModelProperty from './model-property.js';
3
5
 
4
6
  const RESERVED_KEYS = ['__name'];
5
7
 
6
- function searchQuery(query, array, key) {
7
- const result = makeArray(array).find(item => {
8
- for (const [ prop, value ] of Object.entries(query)) {
9
- if (item[prop] !== value) return false;
8
+ function searchQuery(query: Record<string, unknown>, array: unknown, key?: string): unknown {
9
+ const result = makeArray(array).find((item: unknown) => {
10
+ for (const [prop, value] of Object.entries(query)) {
11
+ if ((item as Record<string, unknown>)[prop] !== value) return false;
10
12
 
11
13
  return true;
12
14
  }
13
15
  });
14
16
 
15
17
  if (!result) return null;
16
- if (key) return result[key];
18
+ if (key) return (result as Record<string, unknown>)[key];
17
19
 
18
20
  return result;
19
21
  }
20
22
 
21
- function query(rawData, pathPrefix, subPath) {
23
+ function query(rawData: unknown, pathPrefix: string, subPath: unknown): unknown {
22
24
  if (!rawData) return null;
23
25
 
24
- const [ path, getter, pointer ] = makeArray(subPath);
26
+ const [path, getter, pointer] = makeArray(subPath) as [string, unknown, string | undefined];
25
27
  const fullPath = `${pathPrefix}${path}`;
26
28
  const value = get(rawData, fullPath);
27
29
 
28
30
  if (getter === undefined || getter === null) return value;
29
31
 
30
32
  try {
31
- switch(typeof getter) {
33
+ switch (typeof getter) {
32
34
  case 'object':
33
- return searchQuery(getter, value, pointer);
35
+ return searchQuery(getter as Record<string, unknown>, value, pointer);
34
36
 
35
37
  case 'function':
36
- return getter(value);
38
+ return (getter as (v: unknown) => unknown)(value);
37
39
 
38
- case 'number':
39
- const element = value[getter];
40
- return pointer ? element[pointer] : element;
40
+ case 'number': {
41
+ const element = (value as unknown[])[getter];
42
+ return pointer ? (element as Record<string, unknown>)[pointer] : element;
43
+ }
41
44
 
42
- default:
43
- return value[getter];
45
+ default:
46
+ return (value as Record<string, unknown>)[getter as string];
44
47
  }
45
48
  } catch (error) {
46
49
  if (config.debug) console.error(`Cannot parse value for ${fullPath}.`, { getter, query }, error);
@@ -48,10 +51,11 @@ function query(rawData, pathPrefix, subPath) {
48
51
  }
49
52
 
50
53
  export default class Serializer {
51
- map = {};
54
+ map: Record<string, unknown> = {};
52
55
  path = '';
56
+ model: Record<string, unknown>;
53
57
 
54
- constructor(model) {
58
+ constructor(model: Record<string, unknown>) {
55
59
  this.model = model;
56
60
  }
57
61
 
@@ -60,14 +64,16 @@ export default class Serializer {
60
64
  * the ModelProperty object, while setting parsed values to the record's
61
65
  * __data property, which represents the serialized version of the data
62
66
  */
63
- setProperties(rawData, record, options) {
67
+ setProperties(rawData: unknown, record: unknown, options: Record<string, unknown>): void {
64
68
  const { path, model } = this;
65
69
  const keys = Object.keys(model).filter(key => !RESERVED_KEYS.includes(key));
66
70
  const pathPrefix = path ? `${path}.` : '';
67
- const { __data:parsedData, __relationships:relatedRecords } = record;
71
+ const rec = record as Record<string, unknown>;
72
+ const parsedData = rec.__data as Record<string, unknown>;
73
+ const relatedRecords = rec.__relationships as Record<string, unknown>;
68
74
 
69
75
  for (const key of keys) {
70
- const subPath = options.serialize ? (this.map[key] || key) : key;
76
+ const subPath = options.serialize ? ((this.map as Record<string, unknown>)[key] || key) : key;
71
77
  const handler = model[key];
72
78
  const data = query(rawData, pathPrefix, subPath);
73
79
 
@@ -80,38 +86,40 @@ export default class Serializer {
80
86
  const handlerOptions = { ...options, _relationshipKey: key };
81
87
  const childRecord = handler(record, data, handlerOptions);
82
88
 
83
- record[key] = childRecord
89
+ rec[key] = childRecord;
84
90
  relatedRecords[key] = childRecord;
85
91
 
86
92
  continue;
87
93
  }
88
94
 
89
95
  // Aggregate property handling — use the rawData value, not the aggregate descriptor
90
- if (handler?.constructor?.name === 'AggregateProperty') {
96
+ if ((handler as AggregateProperty)?.__kind === 'AggregateProperty') {
91
97
  parsedData[key] = data;
92
- record[key] = data;
98
+ rec[key] = data;
93
99
  continue;
94
100
  }
95
101
 
96
102
  // Direct assignment handling
97
- if (handler?.constructor?.name !== 'ModelProperty') {
103
+ if ((handler as ModelProperty)?.__kind !== 'ModelProperty') {
98
104
  parsedData[key] = handler;
99
- record[key] = handler;
105
+ rec[key] = handler;
100
106
  continue;
101
107
  }
102
108
 
109
+ const prop = handler as { value: unknown; ignoreFirstTransform: boolean };
110
+
103
111
  Object.defineProperty(record, key, {
104
112
  enumerable: true,
105
113
  configurable: true,
106
- get: () => handler.value,
107
- set(newValue) {
108
- handler.ignoreFirstTransform = !options.transform;
109
- handler.value = newValue;
110
- parsedData[key] = handler.value;
114
+ get: () => prop.value,
115
+ set(newValue: unknown) {
116
+ prop.ignoreFirstTransform = !options.transform;
117
+ prop.value = newValue;
118
+ parsedData[key] = prop.value;
111
119
  }
112
120
  });
113
121
 
114
- record[key] = data;
122
+ rec[key] = data;
115
123
  }
116
124
 
117
125
  if (options.update) return;
@@ -120,25 +128,25 @@ export default class Serializer {
120
128
  for (const [key, getter] of getComputedProperties(this.model)) {
121
129
  Object.defineProperty(record, key, {
122
130
  enumerable: true,
123
- get: () => getter.call(record)
131
+ get: () => (getter as () => unknown).call(record)
124
132
  });
125
133
  }
126
134
 
127
- record.__serialized = true;
135
+ rec.__serialized = true;
128
136
  }
129
137
 
130
138
  /**
131
139
  * OVERRIDE: This hook allows for data manipulation prior to serialization logic
132
140
  */
133
- normalize(data) {
141
+ normalize(data: unknown): unknown {
134
142
  return data;
135
143
  }
136
144
  }
137
145
 
138
- export function getComputedProperties(classInstance) {
146
+ export function getComputedProperties(classInstance: Record<string, unknown>): [string, PropertyDescriptor['get']][] {
139
147
  const proto = Object.getPrototypeOf(classInstance);
140
148
  if (!proto || proto === Object.prototype) return [];
141
-
149
+
142
150
  return Object.entries(Object.getOwnPropertyDescriptors(proto))
143
151
  .filter(([key, descriptor]) => key !== 'constructor' && descriptor.get)
144
152
  .map(([key, descriptor]) => [key, descriptor.get]);
@@ -8,32 +8,37 @@ import { dbKey } from './db.js';
8
8
  import { getPluralName } from './plural-registry.js';
9
9
  import log from 'stonyx/log';
10
10
 
11
- export default async function(route, accessPath, metaRoute) {
12
- let accessFiles = {};
13
-
11
+ interface AccessInstance {
12
+ models: string[] | '*';
13
+ access: (request: unknown) => unknown;
14
+ }
15
+
16
+ export default async function(route: string, accessPath: string, metaRoute: boolean): Promise<void> {
17
+ let accessFiles: Record<string, (request: unknown) => unknown> | null = {};
18
+
14
19
  try {
15
- await forEachFileImport(accessPath, accessClass => {
16
- const accessInstance = new accessClass();
20
+ await forEachFileImport(accessPath, (accessClass: unknown) => {
21
+ const accessInstance = new (accessClass as new () => AccessInstance)();
17
22
  const { models } = accessInstance;
18
23
 
19
- if (!models) throw new Error(`Access class "${accessClass.name}" must define a "models" list`);
24
+ if (!models) throw new Error(`Access class "${(accessClass as { name: string }).name}" must define a "models" list`);
20
25
 
21
26
  if (models.length === 0) return; // No models to assign access to
22
- if (typeof accessInstance.access !== 'function') throw new Error(`Access class "${accessClass.name}" must declare an "access" method`);
27
+ if (typeof accessInstance.access !== 'function') throw new Error(`Access class "${(accessClass as { name: string }).name}" must declare an "access" method`);
23
28
 
24
29
  const availableModels = Array.from(store.data.keys());
25
30
 
26
31
  for (const model of models === '*' ? availableModels : models) {
27
32
  if (model === dbKey) continue;
28
33
  if (!store.data.has(model)) throw new Error(`Unable to define access for Invalid Model "${model}". Model does not exist`);
29
- if (accessFiles[model]) throw new Error(`Access for model "${model}" has already been defined by another access class.`);
34
+ if (accessFiles![model]) throw new Error(`Access for model "${model}" has already been defined by another access class.`);
30
35
 
31
- accessFiles[model] = accessInstance.access;
36
+ accessFiles![model] = accessInstance.access;
32
37
  }
33
38
  });
34
39
  } catch (error) {
35
- log.error(error.message);
36
- log.warn('You must define a valid access configuration file in order to access ORM generated REST endpoints.');
40
+ log.error!(error instanceof Error ? error.message : String(error));
41
+ log.warn!('You must define a valid access configuration file in order to access ORM generated REST endpoints.');
37
42
  }
38
43
 
39
44
  await waitForModule('rest-server');
@@ -42,7 +47,7 @@ export default async function(route, accessPath, metaRoute) {
42
47
  const name = route === '/' ? 'index' : (route[0] === '/' ? route.slice(1) : route);
43
48
 
44
49
  // Configure endpoints for models and views with access configuration
45
- for (const [model, access] of Object.entries(accessFiles)) {
50
+ for (const [model, access] of Object.entries(accessFiles!)) {
46
51
  const pluralizedModel = getPluralName(model);
47
52
  const modelName = name === 'index' ? pluralizedModel : `${name}/${pluralizedModel}`;
48
53
  RestServer.instance.mountRoute(OrmRequest, { name: modelName, options: { model, access } });
@@ -50,7 +55,7 @@ export default async function(route, accessPath, metaRoute) {
50
55
 
51
56
  // Mount the meta route when metaRoute config is enabled
52
57
  if (metaRoute) {
53
- log.warn('SECURITY RISK! - Meta route is enabled via metaRoute config. This feature is intended for development purposes only!');
58
+ log.warn!('SECURITY RISK! - Meta route is enabled via metaRoute config. This feature is intended for development purposes only!');
54
59
 
55
60
  RestServer.instance.mountRoute(MetaRequest, { name });
56
61
  }
@@ -9,14 +9,23 @@
9
9
  import fs from 'fs/promises';
10
10
  import path from 'path';
11
11
 
12
+ interface StandaloneDBOptions {
13
+ dbPath?: string;
14
+ mode?: 'file' | 'directory';
15
+ directory?: string;
16
+ }
17
+
18
+ interface DBRecord {
19
+ id: string | number;
20
+ [key: string]: unknown;
21
+ }
22
+
12
23
  export default class StandaloneDB {
13
- /**
14
- * @param {Object} options
15
- * @param {string} options.dbPath - Path to db.json (file mode) or parent of db dir (directory mode)
16
- * @param {string} [options.mode='directory'] - 'file' or 'directory'
17
- * @param {string} [options.directory='db'] - Directory name when mode is 'directory'
18
- */
19
- constructor(options = {}) {
24
+ readonly mode: 'file' | 'directory';
25
+ readonly dbPath: string;
26
+ readonly directory: string;
27
+
28
+ constructor(options: StandaloneDBOptions = {}) {
20
29
  this.mode = options.mode || 'directory';
21
30
  this.dbPath = options.dbPath || 'db.json';
22
31
  this.directory = options.directory || 'db';
@@ -25,7 +34,7 @@ export default class StandaloneDB {
25
34
  /**
26
35
  * Resolve the directory path for directory mode.
27
36
  */
28
- getDirPath() {
37
+ getDirPath(): string {
29
38
  const dbDir = path.dirname(path.resolve(this.dbPath));
30
39
  return path.join(dbDir, this.directory);
31
40
  }
@@ -34,7 +43,7 @@ export default class StandaloneDB {
34
43
  * List available collections by inspecting either the db.json keys
35
44
  * or the files in the db directory.
36
45
  */
37
- async getCollections() {
46
+ async getCollections(): Promise<string[]> {
38
47
  if (this.mode === 'directory') {
39
48
  const dirPath = this.getDirPath();
40
49
 
@@ -48,9 +57,9 @@ export default class StandaloneDB {
48
57
  }
49
58
  }
50
59
 
51
- // File mode read db.json and return its top-level keys
60
+ // File mode -- read db.json and return its top-level keys
52
61
  try {
53
- const data = await this._readJSON(this.dbPath);
62
+ const data = await this._readJSON(this.dbPath) as Record<string, unknown>;
54
63
  return Object.keys(data).filter(key => Array.isArray(data[key]));
55
64
  } catch {
56
65
  return [];
@@ -60,20 +69,20 @@ export default class StandaloneDB {
60
69
  /**
61
70
  * Read all records for a collection.
62
71
  */
63
- async readCollection(collection) {
72
+ async readCollection(collection: string): Promise<DBRecord[]> {
64
73
  if (this.mode === 'directory') {
65
74
  const filePath = path.join(this.getDirPath(), `${collection}.json`);
66
- return this._readJSON(filePath);
75
+ return this._readJSON(filePath) as Promise<DBRecord[]>;
67
76
  }
68
77
 
69
- const data = await this._readJSON(this.dbPath);
78
+ const data = await this._readJSON(this.dbPath) as Record<string, DBRecord[]>;
70
79
  return data[collection] || [];
71
80
  }
72
81
 
73
82
  /**
74
83
  * Write all records for a collection.
75
84
  */
76
- async writeCollection(collection, records) {
85
+ async writeCollection(collection: string, records: DBRecord[]): Promise<void> {
77
86
  if (this.mode === 'directory') {
78
87
  const dirPath = this.getDirPath();
79
88
  await fs.mkdir(dirPath, { recursive: true });
@@ -83,11 +92,11 @@ export default class StandaloneDB {
83
92
  return;
84
93
  }
85
94
 
86
- // File mode read full db, update collection, write back
87
- let data;
95
+ // File mode -- read full db, update collection, write back
96
+ let data: Record<string, unknown>;
88
97
 
89
98
  try {
90
- data = await this._readJSON(this.dbPath);
99
+ data = await this._readJSON(this.dbPath) as Record<string, unknown>;
91
100
  } catch {
92
101
  data = {};
93
102
  }
@@ -99,7 +108,7 @@ export default class StandaloneDB {
99
108
  /**
100
109
  * Get a single record by id.
101
110
  */
102
- async get(collection, id) {
111
+ async get(collection: string, id: string | number): Promise<DBRecord | null> {
103
112
  const records = await this.readCollection(collection);
104
113
  const numericId = Number(id);
105
114
 
@@ -111,14 +120,14 @@ export default class StandaloneDB {
111
120
  /**
112
121
  * List all records in a collection.
113
122
  */
114
- async list(collection) {
123
+ async list(collection: string): Promise<DBRecord[]> {
115
124
  return this.readCollection(collection);
116
125
  }
117
126
 
118
127
  /**
119
128
  * Create a new record. Auto-assigns an integer id if none provided.
120
129
  */
121
- async create(collection, data) {
130
+ async create(collection: string, data: DBRecord): Promise<DBRecord> {
122
131
  const records = await this.readCollection(collection);
123
132
 
124
133
  if (!data.id) {
@@ -145,7 +154,7 @@ export default class StandaloneDB {
145
154
  /**
146
155
  * Delete a record by id.
147
156
  */
148
- async delete(collection, id) {
157
+ async delete(collection: string, id: string | number): Promise<DBRecord> {
149
158
  const records = await this.readCollection(collection);
150
159
  const numericId = Number(id);
151
160
 
@@ -165,12 +174,12 @@ export default class StandaloneDB {
165
174
 
166
175
  // -- Private helpers --
167
176
 
168
- async _readJSON(filePath) {
177
+ private async _readJSON(filePath: string): Promise<unknown> {
169
178
  const content = await fs.readFile(filePath, 'utf-8');
170
179
  return JSON.parse(content);
171
180
  }
172
181
 
173
- async _writeJSON(filePath, data) {
182
+ private async _writeJSON(filePath: string, data: unknown): Promise<void> {
174
183
  await fs.writeFile(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
175
184
  }
176
185
  }