@caracal-lynx/sluice 0.1.0

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 (281) hide show
  1. package/CLAUDE.md +1822 -0
  2. package/LICENCE-FAQ.md +74 -0
  3. package/LICENSE +92 -0
  4. package/README.md +582 -0
  5. package/dist/adapters/source/csv.d.ts +10 -0
  6. package/dist/adapters/source/csv.d.ts.map +1 -0
  7. package/dist/adapters/source/csv.js +110 -0
  8. package/dist/adapters/source/csv.js.map +1 -0
  9. package/dist/adapters/source/index.d.ts +9 -0
  10. package/dist/adapters/source/index.d.ts.map +1 -0
  11. package/dist/adapters/source/index.js +26 -0
  12. package/dist/adapters/source/index.js.map +1 -0
  13. package/dist/adapters/source/mssql.d.ts +11 -0
  14. package/dist/adapters/source/mssql.d.ts.map +1 -0
  15. package/dist/adapters/source/mssql.js +230 -0
  16. package/dist/adapters/source/mssql.js.map +1 -0
  17. package/dist/adapters/source/pg.d.ts +11 -0
  18. package/dist/adapters/source/pg.d.ts.map +1 -0
  19. package/dist/adapters/source/pg.js +88 -0
  20. package/dist/adapters/source/pg.js.map +1 -0
  21. package/dist/adapters/source/registry.d.ts +10 -0
  22. package/dist/adapters/source/registry.d.ts.map +1 -0
  23. package/dist/adapters/source/registry.js +36 -0
  24. package/dist/adapters/source/registry.js.map +1 -0
  25. package/dist/adapters/source/rest.d.ts +16 -0
  26. package/dist/adapters/source/rest.d.ts.map +1 -0
  27. package/dist/adapters/source/rest.js +182 -0
  28. package/dist/adapters/source/rest.js.map +1 -0
  29. package/dist/adapters/source/rest.types.d.ts +15 -0
  30. package/dist/adapters/source/rest.types.d.ts.map +1 -0
  31. package/dist/adapters/source/rest.types.js +6 -0
  32. package/dist/adapters/source/rest.types.js.map +1 -0
  33. package/dist/adapters/source/types.d.ts +23 -0
  34. package/dist/adapters/source/types.d.ts.map +1 -0
  35. package/dist/adapters/source/types.js +4 -0
  36. package/dist/adapters/source/types.js.map +1 -0
  37. package/dist/adapters/source/xlsx.d.ts +10 -0
  38. package/dist/adapters/source/xlsx.d.ts.map +1 -0
  39. package/dist/adapters/source/xlsx.js +71 -0
  40. package/dist/adapters/source/xlsx.js.map +1 -0
  41. package/dist/adapters/target/bc.d.ts +21 -0
  42. package/dist/adapters/target/bc.d.ts.map +1 -0
  43. package/dist/adapters/target/bc.js +188 -0
  44. package/dist/adapters/target/bc.js.map +1 -0
  45. package/dist/adapters/target/bluecherry.d.ts +10 -0
  46. package/dist/adapters/target/bluecherry.d.ts.map +1 -0
  47. package/dist/adapters/target/bluecherry.js +127 -0
  48. package/dist/adapters/target/bluecherry.js.map +1 -0
  49. package/dist/adapters/target/csv.d.ts +10 -0
  50. package/dist/adapters/target/csv.d.ts.map +1 -0
  51. package/dist/adapters/target/csv.js +40 -0
  52. package/dist/adapters/target/csv.js.map +1 -0
  53. package/dist/adapters/target/ifs.d.ts +10 -0
  54. package/dist/adapters/target/ifs.d.ts.map +1 -0
  55. package/dist/adapters/target/ifs.js +55 -0
  56. package/dist/adapters/target/ifs.js.map +1 -0
  57. package/dist/adapters/target/index.d.ts +8 -0
  58. package/dist/adapters/target/index.d.ts.map +1 -0
  59. package/dist/adapters/target/index.js +22 -0
  60. package/dist/adapters/target/index.js.map +1 -0
  61. package/dist/adapters/target/pg.d.ts +11 -0
  62. package/dist/adapters/target/pg.d.ts.map +1 -0
  63. package/dist/adapters/target/pg.js +103 -0
  64. package/dist/adapters/target/pg.js.map +1 -0
  65. package/dist/adapters/target/registry.d.ts +9 -0
  66. package/dist/adapters/target/registry.d.ts.map +1 -0
  67. package/dist/adapters/target/registry.js +29 -0
  68. package/dist/adapters/target/registry.js.map +1 -0
  69. package/dist/adapters/target/types.d.ts +15 -0
  70. package/dist/adapters/target/types.d.ts.map +1 -0
  71. package/dist/adapters/target/types.js +4 -0
  72. package/dist/adapters/target/types.js.map +1 -0
  73. package/dist/cli.d.ts +25 -0
  74. package/dist/cli.d.ts.map +1 -0
  75. package/dist/cli.js +354 -0
  76. package/dist/cli.js.map +1 -0
  77. package/dist/config/index.d.ts +4 -0
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/config/index.js +6 -0
  80. package/dist/config/index.js.map +1 -0
  81. package/dist/config/loader.d.ts +5 -0
  82. package/dist/config/loader.d.ts.map +1 -0
  83. package/dist/config/loader.js +135 -0
  84. package/dist/config/loader.js.map +1 -0
  85. package/dist/config/schema.d.ts +4162 -0
  86. package/dist/config/schema.d.ts.map +1 -0
  87. package/dist/config/schema.js +263 -0
  88. package/dist/config/schema.js.map +1 -0
  89. package/dist/config/types.d.ts +3 -0
  90. package/dist/config/types.d.ts.map +1 -0
  91. package/dist/config/types.js +4 -0
  92. package/dist/config/types.js.map +1 -0
  93. package/dist/dq/engine.d.ts +10 -0
  94. package/dist/dq/engine.d.ts.map +1 -0
  95. package/dist/dq/engine.js +114 -0
  96. package/dist/dq/engine.js.map +1 -0
  97. package/dist/dq/index.d.ts +6 -0
  98. package/dist/dq/index.d.ts.map +1 -0
  99. package/dist/dq/index.js +6 -0
  100. package/dist/dq/index.js.map +1 -0
  101. package/dist/dq/reporter.d.ts +5 -0
  102. package/dist/dq/reporter.d.ts.map +1 -0
  103. package/dist/dq/reporter.js +41 -0
  104. package/dist/dq/reporter.js.map +1 -0
  105. package/dist/dq/rules/allowedValues.d.ts +7 -0
  106. package/dist/dq/rules/allowedValues.d.ts.map +1 -0
  107. package/dist/dq/rules/allowedValues.js +26 -0
  108. package/dist/dq/rules/allowedValues.js.map +1 -0
  109. package/dist/dq/rules/email.d.ts +7 -0
  110. package/dist/dq/rules/email.d.ts.map +1 -0
  111. package/dist/dq/rules/email.js +24 -0
  112. package/dist/dq/rules/email.js.map +1 -0
  113. package/dist/dq/rules/index.d.ts +15 -0
  114. package/dist/dq/rules/index.d.ts.map +1 -0
  115. package/dist/dq/rules/index.js +30 -0
  116. package/dist/dq/rules/index.js.map +1 -0
  117. package/dist/dq/rules/maxLength.d.ts +7 -0
  118. package/dist/dq/rules/maxLength.d.ts.map +1 -0
  119. package/dist/dq/rules/maxLength.js +25 -0
  120. package/dist/dq/rules/maxLength.js.map +1 -0
  121. package/dist/dq/rules/minMax.d.ts +11 -0
  122. package/dist/dq/rules/minMax.d.ts.map +1 -0
  123. package/dist/dq/rules/minMax.js +52 -0
  124. package/dist/dq/rules/minMax.js.map +1 -0
  125. package/dist/dq/rules/notNull.d.ts +7 -0
  126. package/dist/dq/rules/notNull.d.ts.map +1 -0
  127. package/dist/dq/rules/notNull.js +21 -0
  128. package/dist/dq/rules/notNull.js.map +1 -0
  129. package/dist/dq/rules/pattern.d.ts +7 -0
  130. package/dist/dq/rules/pattern.d.ts.map +1 -0
  131. package/dist/dq/rules/pattern.js +31 -0
  132. package/dist/dq/rules/pattern.js.map +1 -0
  133. package/dist/dq/rules/types.d.ts +6 -0
  134. package/dist/dq/rules/types.d.ts.map +1 -0
  135. package/dist/dq/rules/types.js +4 -0
  136. package/dist/dq/rules/types.js.map +1 -0
  137. package/dist/dq/rules/ukPostcode.d.ts +7 -0
  138. package/dist/dq/rules/ukPostcode.d.ts.map +1 -0
  139. package/dist/dq/rules/ukPostcode.js +24 -0
  140. package/dist/dq/rules/ukPostcode.js.map +1 -0
  141. package/dist/dq/rules/unique.d.ts +14 -0
  142. package/dist/dq/rules/unique.d.ts.map +1 -0
  143. package/dist/dq/rules/unique.js +9 -0
  144. package/dist/dq/rules/unique.js.map +1 -0
  145. package/dist/dq/types.d.ts +29 -0
  146. package/dist/dq/types.d.ts.map +1 -0
  147. package/dist/dq/types.js +4 -0
  148. package/dist/dq/types.js.map +1 -0
  149. package/dist/enrich/types.d.ts +87 -0
  150. package/dist/enrich/types.d.ts.map +1 -0
  151. package/dist/enrich/types.js +4 -0
  152. package/dist/enrich/types.js.map +1 -0
  153. package/dist/index.d.ts +17 -0
  154. package/dist/index.d.ts.map +1 -0
  155. package/dist/index.js +17 -0
  156. package/dist/index.js.map +1 -0
  157. package/dist/merge/conflict-log.d.ts +9 -0
  158. package/dist/merge/conflict-log.d.ts.map +1 -0
  159. package/dist/merge/conflict-log.js +28 -0
  160. package/dist/merge/conflict-log.js.map +1 -0
  161. package/dist/merge/engine.d.ts +7 -0
  162. package/dist/merge/engine.d.ts.map +1 -0
  163. package/dist/merge/engine.js +19 -0
  164. package/dist/merge/engine.js.map +1 -0
  165. package/dist/merge/index.d.ts +11 -0
  166. package/dist/merge/index.d.ts.map +1 -0
  167. package/dist/merge/index.js +34 -0
  168. package/dist/merge/index.js.map +1 -0
  169. package/dist/merge/sql-builder.d.ts +19 -0
  170. package/dist/merge/sql-builder.d.ts.map +1 -0
  171. package/dist/merge/sql-builder.js +148 -0
  172. package/dist/merge/sql-builder.js.map +1 -0
  173. package/dist/merge/strategies/coalesce.d.ts +17 -0
  174. package/dist/merge/strategies/coalesce.d.ts.map +1 -0
  175. package/dist/merge/strategies/coalesce.js +77 -0
  176. package/dist/merge/strategies/coalesce.js.map +1 -0
  177. package/dist/merge/strategies/index.d.ts +5 -0
  178. package/dist/merge/strategies/index.d.ts.map +1 -0
  179. package/dist/merge/strategies/index.js +7 -0
  180. package/dist/merge/strategies/index.js.map +1 -0
  181. package/dist/merge/strategies/intersect.d.ts +17 -0
  182. package/dist/merge/strategies/intersect.d.ts.map +1 -0
  183. package/dist/merge/strategies/intersect.js +75 -0
  184. package/dist/merge/strategies/intersect.js.map +1 -0
  185. package/dist/merge/strategies/priority-override.d.ts +16 -0
  186. package/dist/merge/strategies/priority-override.d.ts.map +1 -0
  187. package/dist/merge/strategies/priority-override.js +78 -0
  188. package/dist/merge/strategies/priority-override.js.map +1 -0
  189. package/dist/merge/strategies/registry.d.ts +8 -0
  190. package/dist/merge/strategies/registry.d.ts.map +1 -0
  191. package/dist/merge/strategies/registry.js +19 -0
  192. package/dist/merge/strategies/registry.js.map +1 -0
  193. package/dist/merge/strategies/union.d.ts +15 -0
  194. package/dist/merge/strategies/union.d.ts.map +1 -0
  195. package/dist/merge/strategies/union.js +75 -0
  196. package/dist/merge/strategies/union.js.map +1 -0
  197. package/dist/merge/types.d.ts +24 -0
  198. package/dist/merge/types.d.ts.map +1 -0
  199. package/dist/merge/types.js +4 -0
  200. package/dist/merge/types.js.map +1 -0
  201. package/dist/multi-source-runner.d.ts +22 -0
  202. package/dist/multi-source-runner.d.ts.map +1 -0
  203. package/dist/multi-source-runner.js +398 -0
  204. package/dist/multi-source-runner.js.map +1 -0
  205. package/dist/plugins/index.d.ts +4 -0
  206. package/dist/plugins/index.d.ts.map +1 -0
  207. package/dist/plugins/index.js +5 -0
  208. package/dist/plugins/index.js.map +1 -0
  209. package/dist/plugins/loader.d.ts +22 -0
  210. package/dist/plugins/loader.d.ts.map +1 -0
  211. package/dist/plugins/loader.js +151 -0
  212. package/dist/plugins/loader.js.map +1 -0
  213. package/dist/plugins/registry.d.ts +25 -0
  214. package/dist/plugins/registry.d.ts.map +1 -0
  215. package/dist/plugins/registry.js +42 -0
  216. package/dist/plugins/registry.js.map +1 -0
  217. package/dist/plugins/types.d.ts +61 -0
  218. package/dist/plugins/types.d.ts.map +1 -0
  219. package/dist/plugins/types.js +4 -0
  220. package/dist/plugins/types.js.map +1 -0
  221. package/dist/runner.d.ts +97 -0
  222. package/dist/runner.d.ts.map +1 -0
  223. package/dist/runner.js +520 -0
  224. package/dist/runner.js.map +1 -0
  225. package/dist/staging/index.d.ts +3 -0
  226. package/dist/staging/index.d.ts.map +1 -0
  227. package/dist/staging/index.js +5 -0
  228. package/dist/staging/index.js.map +1 -0
  229. package/dist/staging/schema.d.ts +19 -0
  230. package/dist/staging/schema.d.ts.map +1 -0
  231. package/dist/staging/schema.js +15 -0
  232. package/dist/staging/schema.js.map +1 -0
  233. package/dist/staging/store.d.ts +71 -0
  234. package/dist/staging/store.d.ts.map +1 -0
  235. package/dist/staging/store.js +270 -0
  236. package/dist/staging/store.js.map +1 -0
  237. package/dist/transform/cleanse.d.ts +2 -0
  238. package/dist/transform/cleanse.d.ts.map +1 -0
  239. package/dist/transform/cleanse.js +59 -0
  240. package/dist/transform/cleanse.js.map +1 -0
  241. package/dist/transform/engine.d.ts +10 -0
  242. package/dist/transform/engine.d.ts.map +1 -0
  243. package/dist/transform/engine.js +225 -0
  244. package/dist/transform/engine.js.map +1 -0
  245. package/dist/transform/expression.d.ts +5 -0
  246. package/dist/transform/expression.d.ts.map +1 -0
  247. package/dist/transform/expression.js +52 -0
  248. package/dist/transform/expression.js.map +1 -0
  249. package/dist/transform/index.d.ts +6 -0
  250. package/dist/transform/index.d.ts.map +1 -0
  251. package/dist/transform/index.js +7 -0
  252. package/dist/transform/index.js.map +1 -0
  253. package/dist/transform/lookup.d.ts +10 -0
  254. package/dist/transform/lookup.d.ts.map +1 -0
  255. package/dist/transform/lookup.js +66 -0
  256. package/dist/transform/lookup.js.map +1 -0
  257. package/dist/transform/types.d.ts +10 -0
  258. package/dist/transform/types.d.ts.map +1 -0
  259. package/dist/transform/types.js +4 -0
  260. package/dist/transform/types.js.map +1 -0
  261. package/dist/utils/env.d.ts +3 -0
  262. package/dist/utils/env.d.ts.map +1 -0
  263. package/dist/utils/env.js +26 -0
  264. package/dist/utils/env.js.map +1 -0
  265. package/dist/utils/errors.d.ts +26 -0
  266. package/dist/utils/errors.d.ts.map +1 -0
  267. package/dist/utils/errors.js +39 -0
  268. package/dist/utils/errors.js.map +1 -0
  269. package/dist/utils/index.d.ts +5 -0
  270. package/dist/utils/index.d.ts.map +1 -0
  271. package/dist/utils/index.js +7 -0
  272. package/dist/utils/index.js.map +1 -0
  273. package/dist/utils/logger.d.ts +14 -0
  274. package/dist/utils/logger.d.ts.map +1 -0
  275. package/dist/utils/logger.js +16 -0
  276. package/dist/utils/logger.js.map +1 -0
  277. package/dist/utils/progress.d.ts +66 -0
  278. package/dist/utils/progress.d.ts.map +1 -0
  279. package/dist/utils/progress.js +283 -0
  280. package/dist/utils/progress.js.map +1 -0
  281. package/package.json +92 -0
@@ -0,0 +1,5 @@
1
+ // SPDX-License-Identifier: Elastic-2.0
2
+ // Copyright (c) 2026 Caracal Lynx Ltd.
3
+ export { RuleRegistry, TransformRegistry } from './registry.js';
4
+ export { loadPlugins, loadNpmPlugins } from './loader.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,uCAAuC;AAEvC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { MergeStrategyRegistry } from '../merge/index.js';
2
+ import type { RuleRegistry, TransformRegistry } from './registry.js';
3
+ /**
4
+ * Scan `pluginDir` for `*.rule.{ts,js}` and `*.transform.{ts,js}` files,
5
+ * import each one, and register the exported `rule` / `transform` with the
6
+ * supplied registries.
7
+ *
8
+ * Silently returns if `pluginDir` does not exist.
9
+ * Throws `ConfigError` if a file is found but does not export a valid plugin.
10
+ */
11
+ export declare function loadPlugins(pluginDir: string, rules: RuleRegistry, transforms: TransformRegistry, mergeStrategies?: typeof MergeStrategyRegistry): Promise<void>;
12
+ /**
13
+ * Read `sluice.config.yaml` at `configPath`, parse it with
14
+ * `ToolkitConfigSchema`, then import each declared npm package and call its
15
+ * `plugin.register()` function.
16
+ *
17
+ * Silently returns if `configPath` does not exist.
18
+ * Throws `ConfigError` for unreadable config files, packages that fail to
19
+ * import, or packages that do not export `plugin.register`.
20
+ */
21
+ export declare function loadNpmPlugins(configPath: string, rules: RuleRegistry, transforms: TransformRegistry, mergeStrategies?: typeof MergeStrategyRegistry): Promise<void>;
22
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAK/D,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AA0BrE;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,iBAAiB,EAC7B,eAAe,CAAC,EAAE,OAAO,qBAAqB,GAC7C,OAAO,CAAC,IAAI,CAAC,CAmEf;AAID;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,iBAAiB,EAC7B,eAAe,CAAC,EAAE,OAAO,qBAAqB,GAC7C,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
@@ -0,0 +1,151 @@
1
+ // SPDX-License-Identifier: Elastic-2.0
2
+ // Copyright (c) 2026 Caracal Lynx Ltd.
3
+ /**
4
+ * Sluice — plugin loader
5
+ * @caracal-lynx/sluice
6
+ *
7
+ * Tier 2: discovers *.rule.{ts,js} and *.transform.{ts,js} files from a
8
+ * plugins directory and registers them with the provided registries.
9
+ *
10
+ * Tier 3: reads sluice.config.yaml, imports declared npm packages, and
11
+ * calls each package's register() function.
12
+ *
13
+ * Neither function throws if the directory / config file does not exist —
14
+ * both are optional for any given project.
15
+ */
16
+ import * as fs from 'node:fs/promises';
17
+ import * as path from 'node:path';
18
+ import { pathToFileURL } from 'node:url';
19
+ import { load as yamlLoad } from 'js-yaml';
20
+ import { ToolkitConfigSchema } from '../config/schema.js';
21
+ import { ConfigError } from '../utils/errors.js';
22
+ import { logger } from '../utils/logger.js';
23
+ // ── Internal helpers ──────────────────────────────────────────────────────────
24
+ async function dirExists(p) {
25
+ try {
26
+ const stat = await fs.stat(p);
27
+ return stat.isDirectory();
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Recursively list all files under `dir` whose name ends with one of the
35
+ * given `suffixes`, returning absolute paths.
36
+ */
37
+ async function findFiles(dir, suffixes) {
38
+ const entries = await fs.readdir(dir, { recursive: true });
39
+ return entries
40
+ .filter(e => suffixes.some(s => e.endsWith(s)))
41
+ .map(e => path.resolve(path.join(dir, e)));
42
+ }
43
+ // ── Tier 2: file-based plugins ────────────────────────────────────────────────
44
+ /**
45
+ * Scan `pluginDir` for `*.rule.{ts,js}` and `*.transform.{ts,js}` files,
46
+ * import each one, and register the exported `rule` / `transform` with the
47
+ * supplied registries.
48
+ *
49
+ * Silently returns if `pluginDir` does not exist.
50
+ * Throws `ConfigError` if a file is found but does not export a valid plugin.
51
+ */
52
+ export async function loadPlugins(pluginDir, rules, transforms, mergeStrategies) {
53
+ if (!(await dirExists(pluginDir)))
54
+ return;
55
+ const ruleFiles = await findFiles(pluginDir, ['.rule.ts', '.rule.js']);
56
+ for (const file of ruleFiles) {
57
+ try {
58
+ const mod = (await import(pathToFileURL(file).href));
59
+ const plugin = mod['rule'];
60
+ if (typeof plugin?.id !== 'string' || typeof plugin.validate !== 'function') {
61
+ throw new Error('Plugin file must export: export const rule: RulePlugin with id and validate()');
62
+ }
63
+ rules.register(plugin);
64
+ logger.debug({ pluginId: plugin.id, file }, 'Registered rule plugin');
65
+ }
66
+ catch (err) {
67
+ if (err instanceof ConfigError)
68
+ throw err;
69
+ throw new ConfigError(`Failed to load rule plugin "${path.basename(file)}": ${String(err)}`, err);
70
+ }
71
+ }
72
+ const transformFiles = await findFiles(pluginDir, ['.transform.ts', '.transform.js']);
73
+ for (const file of transformFiles) {
74
+ try {
75
+ const mod = (await import(pathToFileURL(file).href));
76
+ const plugin = mod['transform'];
77
+ if (typeof plugin?.id !== 'string' || typeof plugin.apply !== 'function') {
78
+ throw new Error('Plugin file must export: export const transform: TransformPlugin with id and apply()');
79
+ }
80
+ transforms.register(plugin);
81
+ logger.debug({ pluginId: plugin.id, file }, 'Registered transform plugin');
82
+ }
83
+ catch (err) {
84
+ if (err instanceof ConfigError)
85
+ throw err;
86
+ throw new ConfigError(`Failed to load transform plugin "${path.basename(file)}": ${String(err)}`, err);
87
+ }
88
+ }
89
+ if (!mergeStrategies)
90
+ return;
91
+ const mergeFiles = await findFiles(pluginDir, ['.merge.ts', '.merge.js']);
92
+ for (const file of mergeFiles) {
93
+ try {
94
+ const mod = (await import(pathToFileURL(file).href));
95
+ const plugin = mod['mergeStrategy'];
96
+ if (typeof plugin?.id !== 'string' || typeof plugin.merge !== 'function') {
97
+ throw new Error('Plugin file must export: export const mergeStrategy: MergeStrategyPlugin with id and merge()');
98
+ }
99
+ mergeStrategies.register(plugin);
100
+ logger.debug({ pluginId: plugin.id, file }, 'Registered merge strategy plugin');
101
+ }
102
+ catch (err) {
103
+ if (err instanceof ConfigError)
104
+ throw err;
105
+ throw new ConfigError(`Failed to load merge strategy plugin "${path.basename(file)}": ${String(err)}`, err);
106
+ }
107
+ }
108
+ }
109
+ // ── Tier 3: npm package plugins ───────────────────────────────────────────────
110
+ /**
111
+ * Read `sluice.config.yaml` at `configPath`, parse it with
112
+ * `ToolkitConfigSchema`, then import each declared npm package and call its
113
+ * `plugin.register()` function.
114
+ *
115
+ * Silently returns if `configPath` does not exist.
116
+ * Throws `ConfigError` for unreadable config files, packages that fail to
117
+ * import, or packages that do not export `plugin.register`.
118
+ */
119
+ export async function loadNpmPlugins(configPath, rules, transforms, mergeStrategies) {
120
+ try {
121
+ await fs.access(configPath);
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ let raw;
127
+ try {
128
+ raw = yamlLoad(await fs.readFile(configPath, 'utf-8'));
129
+ }
130
+ catch (err) {
131
+ throw new ConfigError(`Failed to read toolkit config "${configPath}": ${String(err)}`, err);
132
+ }
133
+ const config = ToolkitConfigSchema.parse(raw);
134
+ for (const entry of config.plugins) {
135
+ try {
136
+ const pkg = (await import(entry.package));
137
+ const exported = (pkg['plugin'] ?? pkg['default']);
138
+ if (typeof exported?.register !== 'function') {
139
+ throw new Error(`Package "${entry.package}" must export plugin.register()`);
140
+ }
141
+ exported.register(rules, transforms, entry.options, mergeStrategies);
142
+ logger.info({ package: entry.package }, 'Loaded npm plugin package');
143
+ }
144
+ catch (err) {
145
+ if (err instanceof ConfigError)
146
+ throw err;
147
+ throw new ConfigError(`Failed to load plugin package "${entry.package}": ${String(err)}`, err);
148
+ }
149
+ }
150
+ }
151
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Sluice — plugin registries
3
+ * @caracal-lynx/sluice
4
+ *
5
+ * RuleRegistry and TransformRegistry hold custom plugins loaded from
6
+ * file plugins (Tier 2) and npm packages (Tier 3). Built-in rules and
7
+ * transform types are NOT stored here — the DQ and transform engines
8
+ * consult built-ins first, then fall through to the registry.
9
+ */
10
+ import type { RulePlugin, TransformPlugin } from './types.js';
11
+ export declare class RuleRegistry {
12
+ private readonly plugins;
13
+ register(plugin: RulePlugin): void;
14
+ get(id: string): RulePlugin | undefined;
15
+ has(id: string): boolean;
16
+ list(): string[];
17
+ }
18
+ export declare class TransformRegistry {
19
+ private readonly plugins;
20
+ register(plugin: TransformPlugin): void;
21
+ get(id: string): TransformPlugin | undefined;
22
+ has(id: string): boolean;
23
+ list(): string[];
24
+ }
25
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9D,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IAEzD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAUlC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIvC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAE9D,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAUvC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI5C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,IAAI,IAAI,MAAM,EAAE;CAGjB"}
@@ -0,0 +1,42 @@
1
+ // SPDX-License-Identifier: Elastic-2.0
2
+ // Copyright (c) 2026 Caracal Lynx Ltd.
3
+ import { ConfigError } from '../utils/errors.js';
4
+ export class RuleRegistry {
5
+ plugins = new Map();
6
+ register(plugin) {
7
+ if (this.plugins.has(plugin.id)) {
8
+ throw new ConfigError(`Duplicate rule plugin id "${plugin.id}". ` +
9
+ `Check plugins/ folder and npm plugin packages for conflicts.`);
10
+ }
11
+ this.plugins.set(plugin.id, plugin);
12
+ }
13
+ get(id) {
14
+ return this.plugins.get(id);
15
+ }
16
+ has(id) {
17
+ return this.plugins.has(id);
18
+ }
19
+ list() {
20
+ return [...this.plugins.keys()];
21
+ }
22
+ }
23
+ export class TransformRegistry {
24
+ plugins = new Map();
25
+ register(plugin) {
26
+ if (this.plugins.has(plugin.id)) {
27
+ throw new ConfigError(`Duplicate transform plugin id "${plugin.id}". ` +
28
+ `Check plugins/ folder and npm plugin packages for conflicts.`);
29
+ }
30
+ this.plugins.set(plugin.id, plugin);
31
+ }
32
+ get(id) {
33
+ return this.plugins.get(id);
34
+ }
35
+ has(id) {
36
+ return this.plugins.has(id);
37
+ }
38
+ list() {
39
+ return [...this.plugins.keys()];
40
+ }
41
+ }
42
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/plugins/registry.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,uCAAuC;AAavC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,OAAO,YAAY;IACN,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEzD,QAAQ,CAAC,MAAkB;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,WAAW,CACnB,6BAA6B,MAAM,CAAC,EAAE,KAAK;gBAC3C,8DAA8D,CAC/D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;CACF;AAED,MAAM,OAAO,iBAAiB;IACX,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IAE9D,QAAQ,CAAC,MAAuB;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,WAAW,CACnB,kCAAkC,MAAM,CAAC,EAAE,KAAK;gBAChD,8DAA8D,CAC/D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;CACF"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Sluice — plugin interfaces
3
+ * @caracal-lynx/sluice
4
+ *
5
+ * Interfaces for Tier 2 (file plugins) and Tier 3 (npm package plugins).
6
+ * Plugins must be pure — no I/O, no side effects, no async.
7
+ */
8
+ import type { CheckConfig } from '../config/types.js';
9
+ import type { MergeStrategyRegistry } from '../merge/index.js';
10
+ import type { RuleRegistry, TransformRegistry } from './registry.js';
11
+ export interface RulePlugin {
12
+ /** Must match the 'type' key used in pipeline YAML checks. */
13
+ readonly id: string;
14
+ /** Optional human-readable description for error messages and profiling. */
15
+ readonly description?: string;
16
+ /**
17
+ * Validate a single cell value.
18
+ * Return null if valid; RuleViolation if not.
19
+ * Must be pure — no side effects, no I/O.
20
+ */
21
+ validate(value: unknown, config: CheckConfig, rowIndex: number, field: string): RuleViolation | null;
22
+ }
23
+ export interface RuleViolation {
24
+ field: string;
25
+ rowIndex: number;
26
+ value: unknown;
27
+ rule: string;
28
+ severity: 'critical' | 'warning' | 'info';
29
+ message: string;
30
+ }
31
+ export interface TransformPlugin {
32
+ /**
33
+ * Must match the 'customOp' key used in pipeline YAML field mappings.
34
+ * Used with: type: custom, customOp: <id>
35
+ */
36
+ readonly id: string;
37
+ readonly description?: string;
38
+ /**
39
+ * Transform a single source value.
40
+ * Receives the raw value and the full source row for cross-field operations.
41
+ * Return the transformed value. Return null to emit a null in the output.
42
+ * Throw TransformError for unrecoverable errors.
43
+ * Must be pure — no side effects, no I/O.
44
+ * Must not mutate the row object.
45
+ */
46
+ apply(value: unknown, row: Record<string, unknown>, config: CustomFieldMapping): unknown;
47
+ }
48
+ export interface CustomFieldMapping {
49
+ from?: string | string[];
50
+ to: string;
51
+ type: 'custom';
52
+ customOp: string;
53
+ /** Arbitrary per-plugin config from YAML, passed through verbatim. */
54
+ options?: Record<string, unknown>;
55
+ default?: unknown;
56
+ optional?: boolean;
57
+ }
58
+ export interface PluginPackage {
59
+ register(rules: RuleRegistry, transforms: TransformRegistry, options?: Record<string, unknown>, mergeStrategies?: typeof MergeStrategyRegistry): void;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIrE,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;OAIG;IACH,QAAQ,CACN,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,aAAa,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAK,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAK,OAAO,CAAC;IAClB,IAAI,EAAM,MAAM,CAAC;IACjB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAG,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;OAOG;IACH,KAAK,CACH,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAK,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,EAAE,EAAQ,MAAM,CAAC;IACjB,IAAI,EAAM,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAID,MAAM,WAAW,aAAa;IAC5B,QAAQ,CACN,KAAK,EAAO,YAAY,EACxB,UAAU,EAAE,iBAAiB,EAC7B,OAAO,CAAC,EAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,eAAe,CAAC,EAAE,OAAO,qBAAqB,GAC7C,IAAI,CAAC;CACT"}
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: Elastic-2.0
2
+ // Copyright (c) 2026 Caracal Lynx Ltd.
3
+ export {};
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,uCAAuC"}
@@ -0,0 +1,97 @@
1
+ import type { ColumnMeta } from './staging/index.js';
2
+ import { type ExtractResult } from './adapters/source/index.js';
3
+ import { type LoadResult } from './adapters/target/index.js';
4
+ import type { Pipeline } from './config/types.js';
5
+ import { DQEngine, type DQSummary } from './dq/index.js';
6
+ import type { EnrichPhaseFactory, EnrichSummary } from './enrich/types.js';
7
+ import { StagingStore } from './staging/index.js';
8
+ import { TransformEngine, type TransformResult } from './transform/index.js';
9
+ import { RuleRegistry, TransformRegistry } from './plugins/registry.js';
10
+ import { ProgressReporter } from './utils/progress.js';
11
+ export declare function registerEnrichPhase(factory: EnrichPhaseFactory): void;
12
+ /** Test-only helper. Not exported from `src/index.ts`. */
13
+ export declare function _resetEnrichPhaseForTesting(): void;
14
+ /** Test-only helper. Not exported from `src/index.ts`. */
15
+ export declare function _isEnrichPhaseRegistered(): boolean;
16
+ export interface RunResult {
17
+ pipeline: string;
18
+ mode: Pipeline['run']['mode'];
19
+ extract: ExtractResult;
20
+ dq: DQSummary;
21
+ transform: TransformResult | null;
22
+ load: LoadResult | null;
23
+ merge?: {
24
+ rowsMerged: number;
25
+ conflicts: number;
26
+ unmatched: number;
27
+ };
28
+ /** Phase 4a — populated when an `enrich:` block ran. */
29
+ enrichSummary?: EnrichSummary;
30
+ /** Path to the state JSON written at the end of a successful run. */
31
+ stateFilePath?: string;
32
+ /** Set by `sluice profile`; absent for other commands. */
33
+ profilePath?: string;
34
+ }
35
+ /** CLI-level overrides applied to the loaded Pipeline config. */
36
+ export interface RunOverrides {
37
+ outputDir?: string;
38
+ dryRun?: boolean;
39
+ mode?: Pipeline['run']['mode'];
40
+ pluginDirs?: string[];
41
+ /** Set by `sluice run --no-enrich` to skip the Phase 4a enrich phase. */
42
+ skipEnrich?: boolean;
43
+ /**
44
+ * Optional progress reporter. CLI callers pass a live reporter; library
45
+ * callers (and tests) can omit it to get a silent no-op.
46
+ */
47
+ progress?: ProgressReporter;
48
+ }
49
+ export declare class PipelineRunner {
50
+ protected readonly ruleRegistry: RuleRegistry;
51
+ protected readonly transformRegistry: TransformRegistry;
52
+ protected readonly dqEngine: DQEngine;
53
+ protected readonly transformEngine: TransformEngine;
54
+ protected progress: ProgressReporter;
55
+ private pluginsLoaded;
56
+ private incrementalSinceUsed;
57
+ constructor(ruleRegistry?: RuleRegistry, transformRegistry?: TransformRegistry);
58
+ /**
59
+ * Load plugins from the file system and npm packages.
60
+ * Called once per runner instance at the start of the first pipeline run.
61
+ *
62
+ * - Tier 2: Scans `{cwd}/plugins/` for `*.rule.{ts,js}` and `*.transform.{ts,js}`.
63
+ * - Tier 3: Reads `{cwd}/sluice.config.yaml` for npm plugin declarations.
64
+ */
65
+ protected loadAllPlugins(cwd: string, pluginDirs?: string[]): Promise<void>;
66
+ run(yamlPath: string, overrides?: RunOverrides): Promise<RunResult>;
67
+ protected runExtract(config: Pipeline, store: StagingStore, tableName?: string, phaseLabel?: string): Promise<ExtractResult>;
68
+ protected runDQ(config: Pipeline, store: StagingStore, tableName?: string, _sourceId?: string, // Reserved for per-source filtering in MultiSourcePipelineRunner.
69
+ phaseLabel?: string): Promise<DQSummary>;
70
+ /**
71
+ * Phase 4a enrich phase. No-ops (returns `undefined`) when:
72
+ * - the pipeline has no `enrich:` block;
73
+ * - `--no-enrich` was set (`overrides.skipEnrich`);
74
+ * - the run is `validate-only` or `dryRun`;
75
+ * - `@caracal-lynx/sluice-enrich` is not installed (no factory registered).
76
+ *
77
+ * The "not installed" case logs a WARN so an operator who configured an
78
+ * `enrich:` block expecting it to run notices the misconfiguration.
79
+ */
80
+ protected runEnrich(config: Pipeline, store: StagingStore, pluginDir: string, overrides: RunOverrides): Promise<EnrichSummary | undefined>;
81
+ protected runTransform(config: Pipeline, store: StagingStore, sourceTable?: string, targetTable?: string): Promise<TransformResult>;
82
+ protected runLoad(config: Pipeline, store: StagingStore): Promise<LoadResult>;
83
+ protected writeStateFile(config: Pipeline, extract: ExtractResult, dq: DQSummary, load: LoadResult, enrichSummary?: EnrichSummary): Promise<string>;
84
+ /**
85
+ * `sluice profile` entry point — extract only, then emit per-column stats.
86
+ * Never writes an output CSV, never runs DQ or transforms.
87
+ */
88
+ profile(yamlPath: string, overrides?: RunOverrides): Promise<RunResult>;
89
+ private buildProfile;
90
+ protected applyOverrides(config: Pipeline, overrides: RunOverrides): Pipeline;
91
+ protected resolveStagingDb(config: Pipeline): string;
92
+ protected resolveIncrementalSince(config: Pipeline): Promise<string>;
93
+ protected applyIncrementalFilter(store: StagingStore, tableName: string, incrementalField: string, incrementalSince: string): Promise<void>;
94
+ protected materializeAcceptedRows(store: StagingStore, tableName: string, columns: ColumnMeta[], rejectedRowIndices: number[]): Promise<string>;
95
+ protected buildRunResult(config: Pipeline, extract: ExtractResult, dq: DQSummary, transform: TransformResult | null, load: LoadResult | null, enrichSummary?: EnrichSummary): RunResult;
96
+ }
97
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}