@loom-framework/core 0.1.0-alpha.8 → 0.1.0-alpha.81

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 (178) hide show
  1. package/dist/adapter-base.d.ts +29 -0
  2. package/dist/adapter-base.d.ts.map +1 -0
  3. package/dist/adapter-base.js +62 -0
  4. package/dist/adapter-base.js.map +1 -0
  5. package/dist/adapter-factory.d.ts +8 -0
  6. package/dist/adapter-factory.d.ts.map +1 -0
  7. package/dist/adapter-factory.js +25 -0
  8. package/dist/adapter-factory.js.map +1 -0
  9. package/dist/adapter-filesystem.d.ts +6 -11
  10. package/dist/adapter-filesystem.d.ts.map +1 -1
  11. package/dist/adapter-filesystem.js +56 -41
  12. package/dist/adapter-filesystem.js.map +1 -1
  13. package/dist/adapter-sqlite.d.ts +6 -23
  14. package/dist/adapter-sqlite.d.ts.map +1 -1
  15. package/dist/adapter-sqlite.js +65 -50
  16. package/dist/adapter-sqlite.js.map +1 -1
  17. package/dist/backend/ai/button-resolver.d.ts +18 -0
  18. package/dist/backend/ai/button-resolver.d.ts.map +1 -0
  19. package/dist/backend/ai/button-resolver.js +58 -0
  20. package/dist/backend/ai/button-resolver.js.map +1 -0
  21. package/dist/backend/ai/engine.d.ts +52 -0
  22. package/dist/backend/ai/engine.d.ts.map +1 -0
  23. package/dist/backend/ai/engine.js +186 -0
  24. package/dist/backend/ai/engine.js.map +1 -0
  25. package/dist/backend/ai/index.d.ts +11 -0
  26. package/dist/backend/ai/index.d.ts.map +1 -0
  27. package/dist/backend/ai/index.js +8 -0
  28. package/dist/backend/ai/index.js.map +1 -0
  29. package/dist/backend/ai/output-parser.d.ts +29 -0
  30. package/dist/backend/ai/output-parser.d.ts.map +1 -0
  31. package/dist/backend/ai/output-parser.js +247 -0
  32. package/dist/backend/ai/output-parser.js.map +1 -0
  33. package/dist/backend/ai/session-manager.d.ts +103 -0
  34. package/dist/backend/ai/session-manager.d.ts.map +1 -0
  35. package/dist/backend/ai/session-manager.js +298 -0
  36. package/dist/backend/ai/session-manager.js.map +1 -0
  37. package/dist/backend/index.d.ts +61 -0
  38. package/dist/backend/index.d.ts.map +1 -0
  39. package/dist/backend/index.js +161 -0
  40. package/dist/backend/index.js.map +1 -0
  41. package/dist/backend/observe/index.d.ts +6 -0
  42. package/dist/backend/observe/index.d.ts.map +1 -0
  43. package/dist/backend/observe/index.js +5 -0
  44. package/dist/backend/observe/index.js.map +1 -0
  45. package/dist/backend/observe/logger.d.ts +28 -0
  46. package/dist/backend/observe/logger.d.ts.map +1 -0
  47. package/dist/backend/observe/logger.js +80 -0
  48. package/dist/backend/observe/logger.js.map +1 -0
  49. package/dist/backend/observe/types.d.ts +26 -0
  50. package/dist/backend/observe/types.d.ts.map +1 -0
  51. package/dist/backend/observe/types.js +7 -0
  52. package/dist/backend/observe/types.js.map +1 -0
  53. package/dist/backend/routes/chat.d.ts +31 -0
  54. package/dist/backend/routes/chat.d.ts.map +1 -0
  55. package/dist/backend/routes/chat.js +426 -0
  56. package/dist/backend/routes/chat.js.map +1 -0
  57. package/dist/backend/routes/data.d.ts +13 -0
  58. package/dist/backend/routes/data.d.ts.map +1 -0
  59. package/dist/backend/routes/data.js +134 -0
  60. package/dist/backend/routes/data.js.map +1 -0
  61. package/dist/backend/routes/health.d.ts +7 -0
  62. package/dist/backend/routes/health.d.ts.map +1 -0
  63. package/dist/backend/routes/health.js +15 -0
  64. package/dist/backend/routes/health.js.map +1 -0
  65. package/dist/backend/routes/index.d.ts +11 -0
  66. package/dist/backend/routes/index.d.ts.map +1 -0
  67. package/dist/backend/routes/index.js +9 -0
  68. package/dist/backend/routes/index.js.map +1 -0
  69. package/dist/backend/routes/skills.d.ts +16 -0
  70. package/dist/backend/routes/skills.d.ts.map +1 -0
  71. package/dist/backend/routes/skills.js +590 -0
  72. package/dist/backend/routes/skills.js.map +1 -0
  73. package/dist/backend/routes/upload.d.ts +24 -0
  74. package/dist/backend/routes/upload.d.ts.map +1 -0
  75. package/dist/backend/routes/upload.js +67 -0
  76. package/dist/backend/routes/upload.js.map +1 -0
  77. package/dist/bin.d.ts +8 -0
  78. package/dist/bin.d.ts.map +1 -0
  79. package/dist/bin.js +12 -0
  80. package/dist/bin.js.map +1 -0
  81. package/dist/capability-generator.d.ts +21 -6
  82. package/dist/capability-generator.d.ts.map +1 -1
  83. package/dist/capability-generator.js +88 -261
  84. package/dist/capability-generator.js.map +1 -1
  85. package/dist/cli/commands/build.d.ts +11 -0
  86. package/dist/cli/commands/build.d.ts.map +1 -0
  87. package/dist/cli/commands/build.js +170 -0
  88. package/dist/cli/commands/build.js.map +1 -0
  89. package/dist/cli/commands/data.d.ts +12 -0
  90. package/dist/cli/commands/data.d.ts.map +1 -0
  91. package/dist/cli/commands/data.js +158 -0
  92. package/dist/cli/commands/data.js.map +1 -0
  93. package/dist/cli/commands/dev.d.ts +9 -0
  94. package/dist/cli/commands/dev.d.ts.map +1 -0
  95. package/dist/cli/commands/dev.js +114 -0
  96. package/dist/cli/commands/dev.js.map +1 -0
  97. package/dist/cli/commands/generate-capabilities.d.ts +8 -0
  98. package/dist/cli/commands/generate-capabilities.d.ts.map +1 -0
  99. package/dist/cli/commands/generate-capabilities.js +40 -0
  100. package/dist/cli/commands/generate-capabilities.js.map +1 -0
  101. package/dist/cli/commands/generate-cli-command.d.ts +8 -0
  102. package/dist/cli/commands/generate-cli-command.d.ts.map +1 -0
  103. package/dist/cli/commands/generate-cli-command.js +64 -0
  104. package/dist/cli/commands/generate-cli-command.js.map +1 -0
  105. package/dist/cli/commands/generate-dashboard.d.ts +9 -0
  106. package/dist/cli/commands/generate-dashboard.d.ts.map +1 -0
  107. package/dist/cli/commands/generate-dashboard.js +452 -0
  108. package/dist/cli/commands/generate-dashboard.js.map +1 -0
  109. package/dist/cli/commands/generate-page.d.ts +9 -0
  110. package/dist/cli/commands/generate-page.d.ts.map +1 -0
  111. package/dist/cli/commands/generate-page.js +518 -0
  112. package/dist/cli/commands/generate-page.js.map +1 -0
  113. package/dist/cli/commands/generate-skill.d.ts +8 -0
  114. package/dist/cli/commands/generate-skill.d.ts.map +1 -0
  115. package/dist/cli/commands/generate-skill.js +75 -0
  116. package/dist/cli/commands/generate-skill.js.map +1 -0
  117. package/dist/cli/commands/generate.d.ts +6 -0
  118. package/dist/cli/commands/generate.d.ts.map +1 -0
  119. package/dist/cli/commands/generate.js +19 -0
  120. package/dist/cli/commands/generate.js.map +1 -0
  121. package/dist/cli/commands/init.d.ts +8 -0
  122. package/dist/cli/commands/init.d.ts.map +1 -0
  123. package/dist/cli/commands/init.js +539 -0
  124. package/dist/cli/commands/init.js.map +1 -0
  125. package/dist/cli/commands/observe.d.ts +9 -0
  126. package/dist/cli/commands/observe.d.ts.map +1 -0
  127. package/dist/cli/commands/observe.js +142 -0
  128. package/dist/cli/commands/observe.js.map +1 -0
  129. package/dist/cli/commands/skill.d.ts +9 -0
  130. package/dist/cli/commands/skill.d.ts.map +1 -0
  131. package/dist/cli/commands/skill.js +186 -0
  132. package/dist/cli/commands/skill.js.map +1 -0
  133. package/dist/cli/helpers/app-tsx-wiring.d.ts +17 -0
  134. package/dist/cli/helpers/app-tsx-wiring.d.ts.map +1 -0
  135. package/dist/cli/helpers/app-tsx-wiring.js +132 -0
  136. package/dist/cli/helpers/app-tsx-wiring.js.map +1 -0
  137. package/dist/cli/helpers/duration.d.ts +5 -0
  138. package/dist/cli/helpers/duration.d.ts.map +1 -0
  139. package/dist/cli/helpers/duration.js +19 -0
  140. package/dist/cli/helpers/duration.js.map +1 -0
  141. package/dist/cli/helpers/field-template.d.ts +10 -0
  142. package/dist/cli/helpers/field-template.d.ts.map +1 -0
  143. package/dist/cli/helpers/field-template.js +100 -0
  144. package/dist/cli/helpers/field-template.js.map +1 -0
  145. package/dist/cli/helpers/naming.d.ts +12 -0
  146. package/dist/cli/helpers/naming.d.ts.map +1 -0
  147. package/dist/cli/helpers/naming.js +25 -0
  148. package/dist/cli/helpers/naming.js.map +1 -0
  149. package/dist/cli/index.d.ts +9 -0
  150. package/dist/cli/index.d.ts.map +1 -0
  151. package/dist/cli/index.js +33 -0
  152. package/dist/cli/index.js.map +1 -0
  153. package/dist/cli/utils.d.ts +10 -0
  154. package/dist/cli/utils.d.ts.map +1 -0
  155. package/dist/cli/utils.js +31 -0
  156. package/dist/cli/utils.js.map +1 -0
  157. package/dist/config.d.ts +118 -42
  158. package/dist/config.d.ts.map +1 -1
  159. package/dist/config.js +59 -10
  160. package/dist/config.js.map +1 -1
  161. package/dist/index.d.ts +7 -2
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +7 -1
  164. package/dist/index.js.map +1 -1
  165. package/dist/server-bin.d.ts +12 -0
  166. package/dist/server-bin.d.ts.map +1 -0
  167. package/dist/server-bin.js +75 -0
  168. package/dist/server-bin.js.map +1 -0
  169. package/dist/types.d.ts +71 -20
  170. package/dist/types.d.ts.map +1 -1
  171. package/package.json +25 -10
  172. package/templates/app-skill/SKILL.md +27 -0
  173. package/templates/app-skill/references/data-semantics.md +44 -0
  174. package/templates/app-skill/references/models.md +31 -0
  175. package/templates/loom-skill/SKILL.md +153 -0
  176. package/templates/loom-skill/references/README.md +128 -0
  177. package/templates/loom-skill/references/dashboard.md +161 -0
  178. package/templates/loom-skill/references/data-model.md +78 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * BaseAdapter - Shared logic for DataAdapter implementations
3
+ *
4
+ * Extracts common validation, ID generation, and schema management
5
+ * used by both FileSystemAdapter and SQLiteAdapter.
6
+ */
7
+ import type { DataAdapter, DataRecord, ModelSchema, LoomConfig } from './types.js';
8
+ export declare abstract class BaseAdapter implements DataAdapter {
9
+ abstract readonly name: string;
10
+ protected schemas: Map<string, ModelSchema>;
11
+ protected config: LoomConfig;
12
+ constructor(config: LoomConfig);
13
+ abstract initialize(): Promise<void>;
14
+ abstract healthCheck(): Promise<boolean>;
15
+ abstract read(model: string, options?: import('./types.js').ReadOptions): Promise<DataRecord | DataRecord[] | null>;
16
+ abstract write(model: string, data: DataRecord): Promise<DataRecord>;
17
+ abstract update(model: string, id: string, data: DataRecord): Promise<DataRecord>;
18
+ abstract delete(model: string, id: string): Promise<void>;
19
+ getSchema(model: string): Promise<ModelSchema | undefined>;
20
+ listModels(): Promise<string[]>;
21
+ protected requireModel(model: string): void;
22
+ protected generateId(): string;
23
+ /**
24
+ * Validate required fields and apply defaults on a record.
25
+ * Returns the validated record with defaults applied.
26
+ */
27
+ protected validateAndApplyDefaults(model: string, record: DataRecord, skipIdValidation?: boolean): DataRecord;
28
+ }
29
+ //# sourceMappingURL=adapter-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-base.d.ts","sourceRoot":"","sources":["../src/adapter-base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnF,8BAAsB,WAAY,YAAW,WAAW;IACtD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAa;IACxD,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;gBAEjB,MAAM,EAAE,UAAU;IAO9B,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IACpC,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IACxC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,YAAY,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC;IACnH,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACpE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACjF,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAI1D,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMrC,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ3C,SAAS,CAAC,UAAU,IAAI,MAAM;IAM9B;;;OAGG;IACH,SAAS,CAAC,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,UAAQ,GAAG,UAAU;CA2B5G"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * BaseAdapter - Shared logic for DataAdapter implementations
3
+ *
4
+ * Extracts common validation, ID generation, and schema management
5
+ * used by both FileSystemAdapter and SQLiteAdapter.
6
+ */
7
+ export class BaseAdapter {
8
+ schemas = new Map();
9
+ config;
10
+ constructor(config) {
11
+ this.config = config;
12
+ for (const model of config.data.models) {
13
+ this.schemas.set(model.name, model);
14
+ }
15
+ }
16
+ async getSchema(model) {
17
+ return this.schemas.get(model);
18
+ }
19
+ async listModels() {
20
+ return Array.from(this.schemas.keys());
21
+ }
22
+ // ── Shared Helpers ──
23
+ requireModel(model) {
24
+ if (!this.schemas.has(model)) {
25
+ throw new Error(`Unknown model: ${model}. Available: ${Array.from(this.schemas.keys()).join(', ')}`);
26
+ }
27
+ }
28
+ generateId() {
29
+ const timestamp = Date.now().toString(36);
30
+ const random = Math.random().toString(36).substring(2, 10);
31
+ return `rec_${timestamp}_${random}`;
32
+ }
33
+ /**
34
+ * Validate required fields and apply defaults on a record.
35
+ * Returns the validated record with defaults applied.
36
+ */
37
+ validateAndApplyDefaults(model, record, skipIdValidation = false) {
38
+ const schema = this.schemas.get(model);
39
+ // Apply default values for missing fields
40
+ for (const field of schema.fields) {
41
+ if (record[field.name] === undefined && field.default !== undefined) {
42
+ record[field.name] = field.default;
43
+ }
44
+ }
45
+ // Validate required fields
46
+ for (const field of schema.fields) {
47
+ if (skipIdValidation && field.name === 'id')
48
+ continue;
49
+ if (field.required && record[field.name] === undefined) {
50
+ throw new Error(`Missing required field: ${field.name}`);
51
+ }
52
+ }
53
+ // Validate enum constraints
54
+ for (const field of schema.fields) {
55
+ if (field.enum && record[field.name] !== undefined && !field.enum.includes(record[field.name])) {
56
+ throw new Error(`Invalid value for field '${field.name}': ${record[field.name]}. Allowed values: ${field.enum.join(', ')}`);
57
+ }
58
+ }
59
+ return record;
60
+ }
61
+ }
62
+ //# sourceMappingURL=adapter-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-base.js","sourceRoot":"","sources":["../src/adapter-base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Adapter Factory
3
+ *
4
+ * Creates the appropriate DataAdapter based on LoomConfig.data.defaultAdapter.
5
+ */
6
+ import type { LoomConfig, DataAdapter } from './types.js';
7
+ export declare function createAdapter(config: LoomConfig, projectRoot: string): DataAdapter;
8
+ //# sourceMappingURL=adapter-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../src/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,CAgBlF"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Adapter Factory
3
+ *
4
+ * Creates the appropriate DataAdapter based on LoomConfig.data.defaultAdapter.
5
+ */
6
+ import path from 'path';
7
+ import { FileSystemAdapter } from './adapter-filesystem.js';
8
+ import { SQLiteAdapter } from './adapter-sqlite.js';
9
+ export function createAdapter(config, projectRoot) {
10
+ const adapterType = config.data.defaultAdapter || 'filesystem';
11
+ switch (adapterType) {
12
+ case 'sqlite':
13
+ return new SQLiteAdapter({
14
+ dbPath: path.join(projectRoot, 'data', config.data.sqlite?.filename ?? 'loom.db'),
15
+ config,
16
+ });
17
+ case 'filesystem':
18
+ default:
19
+ return new FileSystemAdapter({
20
+ dataDir: path.join(projectRoot, 'data'),
21
+ config,
22
+ });
23
+ }
24
+ }
25
+ //# sourceMappingURL=adapter-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-factory.js","sourceRoot":"","sources":["../src/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,UAAU,aAAa,CAAC,MAAkB,EAAE,WAAmB;IACnE,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,IAAI,YAAY,CAAC;IAE/D,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC;gBACvB,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,IAAI,SAAS,CAAC;gBACjF,MAAM;aACP,CAAC,CAAC;QACL,KAAK,YAAY,CAAC;QAClB;YACE,OAAO,IAAI,iBAAiB,CAAC;gBAC3B,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC;gBACvC,MAAM;aACP,CAAC,CAAC;IACP,CAAC;AACH,CAAC"}
@@ -4,29 +4,24 @@
4
4
  * Stores data as JSON files, organized by model.
5
5
  * Default adapter for development and small deployments.
6
6
  */
7
- import type { DataAdapter, ModelSchema, ReadOptions, LoomConfig } from './types.js';
7
+ import type { DataRecord, ReadOptions, LoomConfig } from './types.js';
8
+ import { BaseAdapter } from './adapter-base.js';
8
9
  export interface FileSystemAdapterOptions {
9
10
  dataDir: string;
10
11
  config: LoomConfig;
11
12
  }
12
- export declare class FileSystemAdapter implements DataAdapter {
13
+ export declare class FileSystemAdapter extends BaseAdapter {
13
14
  readonly name = "filesystem";
14
15
  private dataDir;
15
- private config;
16
- private schemas;
17
16
  constructor(options: FileSystemAdapterOptions);
18
17
  initialize(): Promise<void>;
19
18
  healthCheck(): Promise<boolean>;
20
- read(model: string, options?: ReadOptions): Promise<unknown>;
21
- write(model: string, data: Record<string, unknown>): Promise<unknown>;
22
- update(model: string, id: string, data: Record<string, unknown>): Promise<unknown>;
19
+ read(model: string, options?: ReadOptions): Promise<DataRecord | DataRecord[] | null>;
20
+ write(model: string, data: DataRecord): Promise<DataRecord>;
21
+ update(model: string, id: string, data: DataRecord): Promise<DataRecord>;
23
22
  delete(model: string, id: string): Promise<void>;
24
- getSchema(model: string): Promise<ModelSchema | undefined>;
25
- listModels(): Promise<string[]>;
26
- private requireModel;
27
23
  private getModelDir;
28
24
  private getRecordPath;
29
25
  private matchesFilter;
30
- private generateId;
31
26
  }
32
27
  //# sourceMappingURL=adapter-filesystem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-filesystem.d.ts","sourceRoot":"","sources":["../src/adapter-filesystem.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAuC;gBAE1C,OAAO,EAAE,wBAAwB;IAUvC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAmC5D,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBrE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBlF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAI1D,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMrC,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;CAMnB"}
1
+ {"version":3,"file":"adapter-filesystem.d.ts","sourceRoot":"","sources":["../src/adapter-filesystem.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,wBAAwB;IAKvC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC;IA6DrF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAc3D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAmBxE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAatD,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;CAoBtB"}
@@ -6,18 +6,13 @@
6
6
  */
7
7
  import { promises as fs } from 'fs';
8
8
  import path from 'path';
9
- export class FileSystemAdapter {
9
+ import { BaseAdapter } from './adapter-base.js';
10
+ export class FileSystemAdapter extends BaseAdapter {
10
11
  name = 'filesystem';
11
12
  dataDir;
12
- config;
13
- schemas = new Map();
14
13
  constructor(options) {
14
+ super(options.config);
15
15
  this.dataDir = options.dataDir;
16
- this.config = options.config;
17
- // Index model schemas
18
- for (const model of options.config.data.models) {
19
- this.schemas.set(model.name, model);
20
- }
21
16
  }
22
17
  async initialize() {
23
18
  await fs.mkdir(this.dataDir, { recursive: true });
@@ -57,28 +52,49 @@ export class FileSystemAdapter {
57
52
  if (!file.endsWith('.json'))
58
53
  continue;
59
54
  const content = await fs.readFile(path.join(modelDir, file), 'utf-8');
60
- const record = JSON.parse(content);
61
- // Apply filter if provided
62
- if (options?.filter && !this.matchesFilter(record, options.filter)) {
63
- continue;
55
+ const parsed = JSON.parse(content);
56
+ // Handle both single records and arrays (e.g. init.json seed data)
57
+ const items = Array.isArray(parsed) ? parsed : [parsed];
58
+ for (const record of items) {
59
+ // Apply filter if provided
60
+ if (options?.filter && !this.matchesFilter(record, options.filter)) {
61
+ continue;
62
+ }
63
+ records.push(record);
64
64
  }
65
- records.push(record);
66
65
  }
67
- return records;
66
+ // Apply sort if provided
67
+ if (options?.sort) {
68
+ const { field, order } = options.sort;
69
+ records.sort((a, b) => {
70
+ const va = a[field];
71
+ const vb = b[field];
72
+ if (va == null && vb == null)
73
+ return 0;
74
+ if (va == null)
75
+ return 1;
76
+ if (vb == null)
77
+ return -1;
78
+ let cmp;
79
+ if (typeof va === 'number' && typeof vb === 'number') {
80
+ cmp = va - vb;
81
+ }
82
+ else {
83
+ cmp = String(va).localeCompare(String(vb));
84
+ }
85
+ return order === 'desc' ? -cmp : cmp;
86
+ });
87
+ }
88
+ // Apply offset and limit
89
+ const start = options?.offset || 0;
90
+ const end = options?.limit ? start + options.limit : undefined;
91
+ return records.slice(start, end);
68
92
  }
69
93
  async write(model, data) {
70
94
  this.requireModel(model);
71
- const schema = this.schemas.get(model);
72
95
  const id = data.id || this.generateId();
73
- // Build the full record first (with auto-generated id)
74
96
  const record = { id, ...data };
75
- // Validate required fields (check against the full record, not the raw input,
76
- // so auto-generated fields like id are not falsely flagged as missing)
77
- for (const field of schema.fields) {
78
- if (field.required && record[field.name] === undefined) {
79
- throw new Error(`Missing required field: ${field.name}`);
80
- }
81
- }
97
+ this.validateAndApplyDefaults(model, record);
82
98
  const filePath = this.getRecordPath(model, id);
83
99
  await fs.mkdir(path.dirname(filePath), { recursive: true });
84
100
  await fs.writeFile(filePath, JSON.stringify(record, null, 2), 'utf-8');
@@ -109,18 +125,7 @@ export class FileSystemAdapter {
109
125
  throw new Error(`Record not found: ${model}/${id}`);
110
126
  }
111
127
  }
112
- async getSchema(model) {
113
- return this.schemas.get(model);
114
- }
115
- async listModels() {
116
- return Array.from(this.schemas.keys());
117
- }
118
128
  // ── Private Helpers ──
119
- requireModel(model) {
120
- if (!this.schemas.has(model)) {
121
- throw new Error(`Unknown model: ${model}. Available: ${Array.from(this.schemas.keys()).join(', ')}`);
122
- }
123
- }
124
129
  getModelDir(model) {
125
130
  const schema = this.schemas.get(model);
126
131
  const adapterConfig = schema.adapters?.filesystem;
@@ -131,13 +136,23 @@ export class FileSystemAdapter {
131
136
  return path.join(this.getModelDir(model), `${id}.json`);
132
137
  }
133
138
  matchesFilter(record, filter) {
134
- return Object.entries(filter).every(([key, value]) => record[key] === value);
135
- }
136
- generateId() {
137
- // Simple ID generation without dynamic import
138
- const timestamp = Date.now().toString(36);
139
- const random = Math.random().toString(36).substring(2, 10);
140
- return `rec_${timestamp}_${random}`;
139
+ return Object.entries(filter).every(([key, value]) => {
140
+ const recordVal = record[key];
141
+ // Range filter: { __range: [start, end] }
142
+ if (value && typeof value === 'object' && !Array.isArray(value) && '__range' in value) {
143
+ const range = value.__range;
144
+ if (!range?.[0] || !range?.[1])
145
+ return true;
146
+ if (recordVal == null)
147
+ return false;
148
+ return recordVal >= range[0] && recordVal <= range[1];
149
+ }
150
+ // Array filter: match any value in the array
151
+ if (Array.isArray(value)) {
152
+ return value.includes(recordVal);
153
+ }
154
+ return recordVal === value;
155
+ });
141
156
  }
142
157
  }
143
158
  //# sourceMappingURL=adapter-filesystem.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-filesystem.js","sourceRoot":"","sources":["../src/adapter-filesystem.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter-filesystem.js","sourceRoot":"","sources":["../src/adapter-filesystem.ts"],"names":[],"mappings":"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"}
@@ -4,48 +4,31 @@
4
4
  * Stores data in a SQLite database using better-sqlite3.
5
5
  * Suitable for medium-scale and embedded deployments.
6
6
  */
7
- import type { DataAdapter, ModelSchema, ReadOptions, LoomConfig } from './types.js';
7
+ import type { DataRecord, ReadOptions, LoomConfig } from './types.js';
8
+ import { BaseAdapter } from './adapter-base.js';
8
9
  export interface SQLiteAdapterOptions {
9
10
  /** Path to the SQLite database file */
10
11
  dbPath: string;
11
12
  config: LoomConfig;
12
13
  }
13
- export declare class SQLiteAdapter implements DataAdapter {
14
+ export declare class SQLiteAdapter extends BaseAdapter {
14
15
  readonly name = "sqlite";
15
16
  private dbPath;
16
- private config;
17
17
  private db;
18
- private schemas;
19
18
  constructor(options: SQLiteAdapterOptions);
20
19
  initialize(): Promise<void>;
21
20
  healthCheck(): Promise<boolean>;
22
- read(model: string, options?: ReadOptions): Promise<unknown>;
23
- write(model: string, data: Record<string, unknown>): Promise<unknown>;
24
- update(model: string, id: string, data: Record<string, unknown>): Promise<unknown>;
21
+ read(model: string, options?: ReadOptions): Promise<DataRecord | DataRecord[] | null>;
22
+ write(model: string, data: DataRecord): Promise<DataRecord>;
23
+ update(model: string, id: string, data: DataRecord): Promise<DataRecord>;
25
24
  delete(model: string, id: string): Promise<void>;
26
- getSchema(model: string): Promise<ModelSchema | undefined>;
27
- listModels(): Promise<string[]>;
28
25
  /**
29
26
  * Close the database connection
30
27
  */
31
28
  close(): void;
32
- private requireModel;
33
- /**
34
- * Create a table for a model based on its schema
35
- */
36
29
  private createTable;
37
- /**
38
- * Map Loom field types to SQLite types
39
- */
40
30
  private mapFieldType;
41
- /**
42
- * Serialize a value for SQLite storage
43
- */
44
31
  private serializeValue;
45
- /**
46
- * Deserialize a row from SQLite storage
47
- */
48
32
  private deserializeRow;
49
- private generateId;
50
33
  }
51
34
  //# sourceMappingURL=adapter-sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-sqlite.d.ts","sourceRoot":"","sources":["../src/adapter-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpF,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,OAAO,CAAuC;gBAE1C,OAAO,EAAE,oBAAoB;IASnC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA+B5D,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BrE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBlF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAI1D,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIrC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAyBnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAkBpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAetB;;OAEG;IACH,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,UAAU;CAKnB"}
1
+ {"version":3,"file":"adapter-sqlite.d.ts","sourceRoot":"","sources":["../src/adapter-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;CACpB;AAUD,qBAAa,aAAc,SAAQ,WAAW;IAC5C,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAqB;gBAEnB,OAAO,EAAE,oBAAoB;IAKnC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC;IA4DrF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB3D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAoBxE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,cAAc;CAyBvB"}
@@ -7,18 +7,20 @@
7
7
  import Database from 'better-sqlite3';
8
8
  import path from 'path';
9
9
  import fs from 'fs';
10
- export class SQLiteAdapter {
10
+ import { BaseAdapter } from './adapter-base.js';
11
+ const VALID_IDENTIFIER = /^[a-zA-Z][a-zA-Z0-9_]*$/;
12
+ function validateIdentifier(name, label) {
13
+ if (!VALID_IDENTIFIER.test(name)) {
14
+ throw new Error(`Invalid ${label}: "${name}". Must match /^[a-zA-Z][a-zA-Z0-9_]*$/`);
15
+ }
16
+ }
17
+ export class SQLiteAdapter extends BaseAdapter {
11
18
  name = 'sqlite';
12
19
  dbPath;
13
- config;
14
20
  db;
15
- schemas = new Map();
16
21
  constructor(options) {
22
+ super(options.config);
17
23
  this.dbPath = options.dbPath;
18
- this.config = options.config;
19
- for (const model of options.config.data.models) {
20
- this.schemas.set(model.name, model);
21
- }
22
24
  }
23
25
  async initialize() {
24
26
  // Ensure directory exists
@@ -26,9 +28,24 @@ export class SQLiteAdapter {
26
28
  if (!fs.existsSync(dir)) {
27
29
  fs.mkdirSync(dir, { recursive: true });
28
30
  }
29
- this.db = new Database(this.dbPath);
30
- this.db.pragma('journal_mode = WAL');
31
- this.db.pragma('foreign_keys = ON');
31
+ try {
32
+ this.db = new Database(this.dbPath);
33
+ this.db.pragma('journal_mode = WAL');
34
+ this.db.pragma('foreign_keys = ON');
35
+ }
36
+ catch (err) {
37
+ const msg = err?.message ?? String(err);
38
+ if (msg.includes('Could not locate the bindings file') || msg.includes('better_sqlite3.node')) {
39
+ throw new Error('better-sqlite3 native module failed to load.\n' +
40
+ 'This usually means the native binary was not compiled for your Node.js version.\n\n' +
41
+ 'Fix options:\n' +
42
+ ' 1. Rebuild from source: cd node_modules/better-sqlite3 && npx node-gyp rebuild\n' +
43
+ ' 2. Switch adapter: change defaultAdapter to "filesystem" in loom.config.ts\n' +
44
+ ` 3. Check Node.js: better-sqlite3 may not support Node ${process.version} yet\n\n` +
45
+ `Original error: ${msg}`);
46
+ }
47
+ throw err;
48
+ }
32
49
  // Auto-create tables from config
33
50
  for (const model of this.config.data.models) {
34
51
  this.createTable(model);
@@ -45,6 +62,7 @@ export class SQLiteAdapter {
45
62
  }
46
63
  async read(model, options) {
47
64
  this.requireModel(model);
65
+ validateIdentifier(model, 'model name');
48
66
  // Read by ID
49
67
  if (options?.id) {
50
68
  const row = this.db.prepare(`SELECT * FROM "${model}" WHERE id = ?`).get(options.id);
@@ -56,31 +74,51 @@ export class SQLiteAdapter {
56
74
  if (options?.filter && Object.keys(options.filter).length > 0) {
57
75
  const filter = options.filter;
58
76
  const conditions = Object.entries(filter).map(([key, value]) => {
77
+ validateIdentifier(key, 'filter field name');
78
+ // Range filter: { __range: [start, end] }
79
+ if (value && typeof value === 'object' && !Array.isArray(value) && '__range' in value) {
80
+ const range = value.__range;
81
+ if (range?.[0] && range?.[1]) {
82
+ params.push(range[0], range[1]);
83
+ return `"${key}" BETWEEN ? AND ?`;
84
+ }
85
+ return '1=1';
86
+ }
87
+ // Array filter: match any value in the array
88
+ if (Array.isArray(value)) {
89
+ const placeholders = value.map(() => '?').join(', ');
90
+ params.push(...value);
91
+ return `"${key}" IN (${placeholders})`;
92
+ }
93
+ // Equal match
59
94
  params.push(value);
60
95
  return `"${key}" = ?`;
61
96
  });
62
97
  sql += ` WHERE ${conditions.join(' AND ')}`;
63
98
  }
64
- if (options?.limit) {
65
- sql += ` LIMIT ?`;
66
- params.push(options.limit);
99
+ if (options?.sort) {
100
+ validateIdentifier(options.sort.field, 'sort field name');
101
+ sql += ` ORDER BY "${options.sort.field}" ${options.sort.order === 'desc' ? 'DESC' : 'ASC'}`;
102
+ }
103
+ if (options?.limit || options?.offset) {
104
+ if (options?.limit) {
105
+ sql += ` LIMIT ?`;
106
+ params.push(options.limit);
107
+ }
108
+ if (options?.offset) {
109
+ sql += ` OFFSET ?`;
110
+ params.push(options.offset);
111
+ }
67
112
  }
68
113
  const rows = this.db.prepare(sql).all(...params);
69
114
  return rows.map((row) => this.deserializeRow(row, model));
70
115
  }
71
116
  async write(model, data) {
72
117
  this.requireModel(model);
73
- const schema = this.schemas.get(model);
118
+ validateIdentifier(model, 'model name');
74
119
  const id = data.id || this.generateId();
75
- // Validate required fields (skip id — auto-generated if not provided)
76
- for (const field of schema.fields) {
77
- if (field.name === 'id')
78
- continue;
79
- if (field.required && data[field.name] === undefined) {
80
- throw new Error(`Missing required field: ${field.name}`);
81
- }
82
- }
83
120
  const record = { id, ...data };
121
+ this.validateAndApplyDefaults(model, record, true);
84
122
  // Insert into table
85
123
  const columns = Object.keys(record);
86
124
  const values = columns.map((col) => this.serializeValue(record[col], model, col));
@@ -91,6 +129,7 @@ export class SQLiteAdapter {
91
129
  }
92
130
  async update(model, id, data) {
93
131
  this.requireModel(model);
132
+ validateIdentifier(model, 'model name');
94
133
  // Check record exists
95
134
  const existing = this.db.prepare(`SELECT * FROM "${model}" WHERE id = ?`).get(id);
96
135
  if (!existing) {
@@ -105,17 +144,12 @@ export class SQLiteAdapter {
105
144
  }
106
145
  async delete(model, id) {
107
146
  this.requireModel(model);
147
+ validateIdentifier(model, 'model name');
108
148
  const result = this.db.prepare(`DELETE FROM "${model}" WHERE id = ?`).run(id);
109
149
  if (result.changes === 0) {
110
150
  throw new Error(`Record not found: ${model}/${id}`);
111
151
  }
112
152
  }
113
- async getSchema(model) {
114
- return this.schemas.get(model);
115
- }
116
- async listModels() {
117
- return Array.from(this.schemas.keys());
118
- }
119
153
  /**
120
154
  * Close the database connection
121
155
  */
@@ -123,19 +157,12 @@ export class SQLiteAdapter {
123
157
  this.db?.close();
124
158
  }
125
159
  // ── Private Helpers ──
126
- requireModel(model) {
127
- if (!this.schemas.has(model)) {
128
- throw new Error(`Unknown model: ${model}. Available: ${Array.from(this.schemas.keys()).join(', ')}`);
129
- }
130
- }
131
- /**
132
- * Create a table for a model based on its schema
133
- */
134
160
  createTable(model) {
135
161
  const columns = ['id TEXT PRIMARY KEY'];
136
162
  for (const field of model.fields) {
137
163
  if (field.name === 'id')
138
164
  continue; // Already added
165
+ validateIdentifier(field.name, `field name in model "${model.name}"`);
139
166
  const colType = this.mapFieldType(field.type);
140
167
  const notNull = field.required ? ' NOT NULL' : '';
141
168
  const defaultVal = field.default !== undefined ? ` DEFAULT ${JSON.stringify(field.default)}` : '';
@@ -148,14 +175,13 @@ export class SQLiteAdapter {
148
175
  for (const index of model.indexes) {
149
176
  const indexName = `idx_${model.name}_${index.fields.join('_')}`;
150
177
  const unique = index.unique ? 'UNIQUE ' : '';
178
+ for (const f of index.fields)
179
+ validateIdentifier(f, `index field name in model "${model.name}"`);
151
180
  const cols = index.fields.map((f) => `"${f}"`).join(', ');
152
181
  this.db.exec(`CREATE ${unique}INDEX IF NOT EXISTS "${indexName}" ON "${model.name}" (${cols})`);
153
182
  }
154
183
  }
155
184
  }
156
- /**
157
- * Map Loom field types to SQLite types
158
- */
159
185
  mapFieldType(type) {
160
186
  switch (type) {
161
187
  case 'string':
@@ -173,9 +199,6 @@ export class SQLiteAdapter {
173
199
  return 'TEXT';
174
200
  }
175
201
  }
176
- /**
177
- * Serialize a value for SQLite storage
178
- */
179
202
  serializeValue(value, model, field) {
180
203
  if (value === null || value === undefined)
181
204
  return null;
@@ -188,9 +211,6 @@ export class SQLiteAdapter {
188
211
  return value ? 1 : 0;
189
212
  return value;
190
213
  }
191
- /**
192
- * Deserialize a row from SQLite storage
193
- */
194
214
  deserializeRow(row, model) {
195
215
  const schema = this.schemas.get(model);
196
216
  if (!schema)
@@ -216,10 +236,5 @@ export class SQLiteAdapter {
216
236
  }
217
237
  return result;
218
238
  }
219
- generateId() {
220
- const timestamp = Date.now().toString(36);
221
- const random = Math.random().toString(36).substring(2, 10);
222
- return `rec_${timestamp}_${random}`;
223
- }
224
239
  }
225
240
  //# sourceMappingURL=adapter-sqlite.js.map