From a47ff469027af27757a341df608972252b4a4166 Mon Sep 17 00:00:00 2001 From: Jeremi Joslin Date: Wed, 9 Sep 2026 22:07:48 +0700 Subject: [PATCH] Prepare v0.1.0 beta dependency pins and release notes Signed-off-by: Jeremi Joslin --- CHANGELOG.md | 31 ++++++++++++ README.md | 3 ++ package-lock.json | 24 +++++++--- package.json | 8 ++++ pilot/test/common-compatibility.test.js | 63 +++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 pilot/test/common-compatibility.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f986bba --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## 0.1.0 Beta + +First self-hosted Evidence and BREG agricultural-holdings pilot. + +- Separate OpenFn adaptors use the maintained Registry Stack 0.27.0 client. +- Registration uses explicit idempotency. Corrections follow governed change + requests with separate reviewer approval and manual application. +- Committed events pass through an authenticated bridge, request verified signed + registration evidence, and produce atomic, deduplicated destination updates. +- Pinned upstream Lightning and worker packaging, Compose setup, repeatable + workflow provisioning, and operator recovery commands are included. +- Linux acceptance covers fresh installation, the complete workflow journey, + restart recovery, failed delivery, failed workflow steps and queued work. + +### Release scope + +This is a source release for supervised synthetic pilots. Setup builds the +containers locally from the committed dependency locks and pinned images. +The worker requires Linux amd64; native adaptor imports also support Linux +arm64. The assertion confirms registration only, without disclosing the holding +name or establishing ownership or programme eligibility. + +Relay, legacy Notary, hosted OpenFn, wallet issuance, npm publication and OpenFn +catalogue acceptance are outside this beta. Root dependency overrides apply to +the locked source deployment and do not accompany independently installed npm +packages. + +Stop/start preserves data; reset is a separate explicit destructive operation. +See the [operator guide](README.md) for private inputs, retention and recovery. diff --git a/README.md b/README.md index ad53fff..0ece034 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Registry Stack adaptors for OpenFn +**Beta:** intended for supervised, self-hosted pilots. APIs and deployment +configuration may change before 1.0. + A self-hosted synthetic pilot connecting upstream OpenFn Lightning to Registry Stack Base Registry Engine (BREG) and signed Evidence. It demonstrates registration, reviewed correction, and an idempotent downstream update containing a verified diff --git a/package-lock.json b/package-lock.json index 2f0f839..632ec30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -286,6 +286,12 @@ "undici": "^7.28.0" } }, + "node_modules/@openfn/language-common/node_modules/csv-parse": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.2.tgz", + "integrity": "sha512-uKZghv9UmPkMVLYy//KZ9HFAIJsl7wkhoEdIL0+rhuSY9pZQlhaeGEDPIe+/w7eh81MOql8Q/9+inAGWG6ZHYA==", + "license": "MIT" + }, "node_modules/@openfn/language-http": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/@openfn/language-http/-/language-http-7.3.3.tgz", @@ -313,6 +319,12 @@ "undici": "^7.29.0" } }, + "node_modules/@openfn/language-http/node_modules/csv-parse": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.2.tgz", + "integrity": "sha512-uKZghv9UmPkMVLYy//KZ9HFAIJsl7wkhoEdIL0+rhuSY9pZQlhaeGEDPIe+/w7eh81MOql8Q/9+inAGWG6ZHYA==", + "license": "MIT" + }, "node_modules/@openfn/language-registry-breg": { "resolved": "packages/registry-breg", "link": true @@ -1440,9 +1452,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "funding": [ { "type": "github", @@ -1765,9 +1777,9 @@ } }, "node_modules/js-yaml": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", - "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 45ca0b2..e02d24e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,14 @@ "dependencies": { "@openfn/language-http": "7.3.3" }, + "overrides": { + "@openfn/language-common@3.3.4": { + "csv-parse": "7.0.2" + }, + "@openfn/language-common@3.3.5": { + "csv-parse": "7.0.2" + } + }, "engines": { "node": ">=22.12.0" } diff --git a/pilot/test/common-compatibility.test.js b/pilot/test/common-compatibility.test.js new file mode 100644 index 0000000..4405cc4 --- /dev/null +++ b/pilot/test/common-compatibility.test.js @@ -0,0 +1,63 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; +import { Readable } from 'node:stream'; +import test from 'node:test'; +import { pathToFileURL } from 'node:url'; +import * as evidence from '../../packages/registry-evidence/src/index.js'; +import * as breg from '../../packages/registry-breg/src/index.js'; +import * as http from '@openfn/language-http'; + +const rootRequire = createRequire(import.meta.url); + +function dependency(parentRequire, name) { + let directory = dirname(parentRequire.resolve(name)); + while (true) { + try { + const manifestPath = join(directory, 'package.json'); + const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); + if (manifest.name === name) return { manifest, require: createRequire(manifestPath) }; + } catch (error) { + if (error.code !== 'ENOENT') throw error; + } + const parent = dirname(directory); + if (parent === directory) throw new Error(`Cannot find package manifest for ${name}`); + directory = parent; + } +} + +for (const [name, adaptor, parentRequire] of [ + ['Evidence', evidence, createRequire(new URL('../../packages/registry-evidence/package.json', import.meta.url))], + ['BREG', breg, createRequire(new URL('../../packages/registry-breg/package.json', import.meta.url))], + ['HTTP', http, dependency(rootRequire, '@openfn/language-http').require], +]) { + test(`${name} common CSV operations remain compatible with the scoped parser override`, async () => { + const common = dependency(parentRequire, '@openfn/language-common'); + assert.equal(typeof adaptor.parseCsv, 'function'); + const csv = '\uFEFFname,value\n "Synthetic, Farm" , 42 \n\nSecond, 7\n'; + const expected = [{ name: 'Synthetic, Farm', value: '42' }, { name: 'Second', value: '7' }]; + const result = await adaptor.parseCsv(csv)({ data: {}, configuration: {} }); + assert.deepEqual(result.data, expected); + + const chunks = []; + const streamed = await adaptor.parseCsv(Readable.from([csv.slice(0, 20), csv.slice(20)]), { chunkSize: 1 }, (state, records) => { + chunks.push(records); + return { ...state, rows: [...state.rows, ...records] }; + })({ data: {}, configuration: {}, rows: [] }); + assert.deepEqual(streamed.rows, expected); + assert.deepEqual(chunks, expected.map(row => [row])); + + // Exercise the actual parser selected for this common package, including its ESM API. + const parserRequire = dependency(common.require, 'csv-parse').require; + const { parse } = await import(pathToFileURL(join(dirname(parserRequire.resolve('csv-parse')), '../../lib/index.js'))); + const records = []; + for await (const record of parse('__proto__,__proto__,name\nfirst,second,synthetic\n', { columns: true, group_columns_by_name: true })) records.push(record); + assert.equal(records.length, 1); + assert.equal(Object.getPrototypeOf(records[0]), Object.prototype); + assert.equal(Object.hasOwn(records[0], '__proto__'), true); + assert.deepEqual(records[0].__proto__, ['first', 'second']); + assert.equal(records[0][0], undefined); + assert.equal(records[0].name, 'synthetic'); + }); +}