Changelog
Minor Changes
Section titled “Minor Changes”- #118
ccf1c3dThanks @michaelscott-1963! - ExportBUILTIN_CLEANSE_OPSfrom the package root — an immutable, ordered array of{ id, description, argSpec? }records describing every built-in cleanse op accepted byapplyCleanse(trim,uppercase,lowercase,titleCase,stripNonAlpha,stripNonNumeric,stripWhitespace,nullIfEmpty,normaliseQuotes,normaliseUnicode,padStart,padEnd,truncate). Lets external tooling —@caracal-lynx/sluice-mcp’slist_transform_opstool, doc generators, IDE autocomplete helpers — enumerate the supported ops without duplicating the list. The correspondingBuiltinCleanseOpInfotype is also exported.
Minor Changes
Section titled “Minor Changes”- #114
1e4d3ceThanks @michaelscott-1963! - AddstagingDb?: stringtoRunOverrides. Library callers (notably@caracal-lynx/sluice-mcp’sdry_run_pipelinetool) can now force a specific DuckDB staging path — typically':memory:'— for a single invocation without rewriting the YAML on disk. CLI behaviour is unchanged: when the override is omitted,run.stagingDbcontinues to come from the loaded config.
Patch Changes
Section titled “Patch Changes”-
#111
cb6273fThanks @michaelscott-1963! - Security: replaceexpr-eval@2.0.2withexpr-eval-fork@^3.0.3to remediate two HIGH severity vulnerabilities (GHSA-rpw9-cf2g-5q7g prototype pollution and the unrestricted function-evaluation advisory). The fork is a community-maintained drop-in replacement — same Parser API, same expression syntax — that ships the patches the original maintainer never released to npm.No user-visible behaviour change: pipeline YAML files using
type: expressioncontinue to work without modification. -
#113
c1bc6e4Thanks @michaelscott-1963! - Security: replacexlsx@0.18.5(SheetJS) withexceljs@^4.4.0to remediate two HIGH severity vulnerabilities — GHSA-4r6h-8v6p-xvw6 (prototype pollution) and GHSA-5pgg-2g8v-p4x9 (ReDoS). Both advisories havefix: nullon npm because SheetJS publishes patches only via their CDN tarball, not to the public registry.The
xlsxsource adapter is rewritten on top of ExcelJS. The pipeline YAMLadapter: xlsxidentifier and all its options (file,sheet) remain unchanged — pipelines using the adapter continue to work without modification.Together with the earlier
expr-eval-forkswap this run,npm auditnow reports zero vulnerabilities on the public sluice repo.
Minor Changes
Section titled “Minor Changes”-
#67
b224131Thanks @michaelscott-1963! - ✨ Add theodoo-csvsource adapter for Odoo’s product/customer/etc. CSV exports.Odoo’s CSV exports have one structural quirk the plain
csvadapter can’t handle: products with multi-axis variants emit a “continuation row” for every variant axis beyond the first, leaving every column blank except the one carrying theKey: valuecell (typicallyVariant Values).The new adapter merges continuation rows into their preceding parent and, when
pivot:is declared, splits eachKey: valuecell on the first colon and routes the value into a new column named after the key.source:adapter: odoo-csvfile: ./sources/odoo-products.csvpivot:column: 'Variant Values'keys: [Size, 'Colours Pioneer', COLOUR_YARN]onUnknownKey: warn # warn (default) | errordropOriginal: true # default true — drop the pivot column from outputBehaviour:
- Continuation merge is unambiguous: a row where every column except
pivot.columnis blank is treated as an additionalKey: valuecontribution to the preceding parent row. - Output schema is stable: declared
pivot.keysare the only new columns. InonUnknownKey: warnmode, unknown keys are logged and dropped — they do not become output columns. - Same-key collision inside one logical row (e.g. parent and
continuation both contribute
Size:) warns and last-wins. - Orphan continuation rows (no preceding parent) abort the run with
a clear
SourceError. - Without
pivot:, the adapter behaves like the plaincsvadapter — the brand reserves namespace for future Odoo-specific quirks (M2M-comma-joined cells, locale-aware dates/currencies) without bloating other adapters.
Backwards compatible. Existing pipelines are unaffected.
- Continuation merge is unambiguous: a row where every column except
-
#66
fc84d9bThanks @michaelscott-1963! - ✨ Add theunmapped: truefield-mapping directive for iterative pipeline drafts.When a field mapping declares
unmapped: true, the transform engine emitstransform.unmappedPlaceholder(default*** TBC ***) for every row, regardless offrom,type,cleanse, ormax. The directive lets a draft pipeline run end-to-end before its source fields have been identified, so client-facing output can be reviewed iteratively as mappings are wired in.transform:unmappedPlaceholder: '*** TBC ***' # optional overridefields:- to: Divisiontype: stringunmapped: true # emits placeholder for every rowThe Zod refinement on
FieldMappingSchemathat requiresfromfor source-reading types (string,number,decimal,boolean,date,lookup,concat) is relaxed whenunmapped: true. Existing pipelines are unaffected —unmappeddefaults to undefined.
Minor Changes
Section titled “Minor Changes”-
#65
90d50a7Thanks @michaelscott-1963! - ✨ Add Phase 12 — Prep Phase (pre-enrich data fixup).A new optional
prep:block on the pipeline YAML lets you mutate the staging table in place between Extract and Enrich, so external API lookups and DQ both see already-fixed data. Each rule applies acleanse:pipe chain (with the newpadEnd:<len>:<char>op), anexpression:, or alookup:to one column, with an optionalwhen:row predicate. Multi-source pipelines support both pre-merge per-source firings (sourceId:scoped) and a post-merge firing againststg_merged.Companion CLI:
sluice run --no-prepandsluice validate --no-prepskip the phase. New exit code 5 surfacesPrepError. Aggregated per-firing results are written to{outputDir}/{name}-prep-summary.json(override viaprep.summaryFile).Backwards compatible: pipelines without a
prep:block are unaffected; no existing schema, plugin interface, or test changes in a breaking way. Seedocs/PHASE-12-prep-phase-spec.mdfor the full specification.
Patch Changes
Section titled “Patch Changes”-
#62
5f4f04cThanks @michaelscott-1963! - 📝 Correct company legal name in copyright headers and docs.The legal entity registered with Companies House (SC826823) is Caracal Lynx Limited, not “Caracal Lynx Ltd.”. An earlier sweep had standardised the codebase on the abbreviated form. This change corrects every copyright header, sign-off,
authorfield inpackage.json, and prose reference across the repo (112 occurrences in 90 files) back to the legal name. No runtime behaviour changes — comments and metadata only. -
#64
dc57822Thanks @michaelscott-1963! - 📧 Standardise commercial-enquiry contact onsluice@caracallynx.com.Replaces 12 references to
michael.scott@caracallynx.comacross docs, GitHub issue templates,CONTRIBUTING.md, and thepackage.jsonauthor field with the dedicatedsluice@caracallynx.commailbox. The README and doc-site had already moved tosluice@…(PR #34); this cleans up the remaining files so every public-facing contact point routes through the same inbox. No code changes.
Patch Changes
Section titled “Patch Changes”-
#52
06f70dcThanks @michaelscott-1963! - 🐛 Fixsluice --versionreporting a stale hardcoded0.1.0regardless of installed version.src/cli.tspreviously calledprogram.version('0.1.0')with a literal string that was set at first release and never updated. Every published version since (0.1.1,0.1.2,0.1.3,0.2.0) reported0.1.0when users ransluice --version.Now reads the version dynamically from the installed package’s
package.jsonat runtime, mirroring the pattern already used by@caracal-lynx/sluice-enrich’s CLI. Future releases will self-report correctly without anyone needing to remember to update a literal.
Minor Changes
Section titled “Minor Changes”-
#48
d6d06a1Thanks @michaelscott-1963! - Phase 4 prep — table-name plumbing for the enrich phase.The Phase 4a OSC scaffolding hardcoded
stg_rawas the table the enrich runner operates on, but the multi-source pipeline runner invokes the enrich phase betweenmerge(which producesstg_merged) and post-merge DQ. To make that work end-to-end without mutatingstg_mergedin place, the public surface now plumbs asourceTableargument:EnrichPhaseFactorygains a 6th parametersourceTable: string. The open-sourcePipelineRunner.runEnrich()passes'stg_raw'for single-source pipelines;MultiSourcePipelineRunner.runEnrich()passes'stg_merged'.- The three Phase 4a
StagingStorestubs (selectDistinct,addColumnIfNotExists,batchUpdateColumns) now taketable: stringas their first parameter. They still throw with theinstall @caracal-lynx/sluice-enrichmessage until the private package is installed and patches the prototype. Logger(from pino) is now re-exported from the public barrel so downstream consumers can import the type via@caracal-lynx/sluicewithout taking a direct dependency on pino.EnrichErroris now re-exported alongside the other public error classes. It’s already used internally by the CLI to map onto exit code 4, but downstream packages (notably@caracal-lynx/sluice-enrich) need to be able to throw aninstanceof EnrichErrorfor that mapping to fire — so it has to be reachable via the public path.
The upcoming
@caracal-lynx/sluice-enrich@0.1.0will require this version as its peer dependency lower bound.
Patch Changes
Section titled “Patch Changes”- #34
3825f80Thanks @michaelscott-1963! - Update the paid-services contact email in README.md from a personal address to the dedicatedsluice@caracallynx.commailbox so commercial enquiries land in a routable inbox.