openvibers/openvibe.sdk Commits
v0.20.3: importSqlite reads the connection's current schema instead of public only, so rehearsals can import into a createTestDb schema on the containers (production is unchanged, its search path is public). Documents that an embedded PGlite can break on very large rows (OpenVibe.AI's runs; the same import verifies on PostgreSQL). tools/asyncify: functions imported by destructuring from a converted file are async when they are there (AI's seed was left unawaited). npm test 41/41 with the containers.
Changes
@@@ -8,6 +8,11 @@ release may change an API and says so here.++## 0.20.3 (2026-09-28)++++- `importSqlite` reads the target tables of the connection's current schema, not only `public`. A per-run test schema from `createTestDb({ store: 'pg' })` works now, so a rehearsal can use the containers. Production is unchanged: its search path is `public`.++- Rehearsal note: an embedded PGlite (`--pglite`) can break on very large rows. OpenVibe.AI's `runs` did, while the same import verified on PostgreSQL. When `--pglite` reports every table empty, rehearse on the containers instead (`docs/migrating-to-postgresql.md` §6).++## 0.20.2 (2026-09-28)- **`importSqlite` refuses a source table that has no target table.** Before, such a table was skipped without a word. A table that a module created at runtime and the migration forgot would have been dropped by the switch (found converting OpenVibe.AI: `subject_credentials`). SQLite's own tables and FTS5 virtual tables with their shadow tables are still skipped, because an index is rebuilt, not copied. Anything else not imported on purpose goes in the new `skipSource` option, also on `runSqliteMigration`. An audit of the eleven services already switched found nothing lost. `test/db.test.js`.@@@ -3,7 +3,7 @@"stage": "alpha","code": true,"wave": "W2",-- "version": "0.20.2",++ "version": "0.20.3","plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30","deployed": false,"deployment": "none: a package released as git tags; services and apps pin a tag",@@@ -1,4 +1,4 @@--// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.20.2, MIT). Do not edit.++// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.20.3, MIT). Do not edit.// One self-contained ES module: core at the top level plus the browser-safe subpaths as namespaces// (auth, registry, modules, realtime, media, community, search, chat, ai, jobs, tools, projects, geo). No server-only code: no service tokens, JWT verification, events, identity./* eslint-disable */@@@ -41,7 +41,7 @@ const { paginate, offsetPager } = __load(7);const semver = __load(6);/** The package version (test/version.test.js keeps it equal to package.json). */--const SDK_VERSION = '0.20.2';++const SDK_VERSION = '0.20.3';module.exports = {createClient,@@@ -133,6 +133,8 @@ It changes nothing in the SQLite file.- `STATUS.json` (the database) and the README (Depends on: PostgreSQL and Valkey);- the roadmap plan's WS-X2 migration list.++**Rehearsing on the containers.** An embedded PGlite can break on very large rows (OpenVibe.AI's run payloads). When `--pglite` reports every table empty, import into a schema of the containers: `createTestDb({ migrations, store: 'pg' })`, then `importSqlite({ sqlite, db: t.db })`.++## 7. Lessons from the first migrations- **Tips (2026-09-28)** is the reference: its `postgres` branch has the schema, the importer script, `test/helpers/db.js` and the multi-process integration test. Copy them.@@@ -1,12 +1,12 @@{"name": "openvibe-sdk",-- "version": "0.20.2",++ "version": "0.20.3","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-sdk",-- "version": "0.20.2",++ "version": "0.20.3","license": "MIT","devDependencies": {"@electric-sql/pglite": "^0.5.8",@@@ -1,6 +1,6 @@{"name": "openvibe-sdk",-- "version": "0.20.2",++ "version": "0.20.3","description": "Supported browser and server clients for the OpenVibe platform: auth (incl. developer apps), registry discovery, identity, user modules, events, realtime, media, community, search, chat, AI runs, Tools (registry, run API and jobs), VIP memberships and developer projects, the async PostgreSQL data layer (db) and the Valkey cache, limits store, queue and pub/sub, with deadlines, safe retries, problem errors and trace propagation.","main": "index.js","types": "types/index.d.ts",@@@ -8,7 +8,7 @@ const { paginate, offsetPager } = require('./paginate');const semver = require('./semver');/** The package version (test/version.test.js keeps it equal to package.json). */--const SDK_VERSION = '0.20.2';++const SDK_VERSION = '0.20.3';module.exports = {createClient,@@@ -96,17 +96,17 @@ async function schemaOf(db) {c.is_identity = 'YES' AS identity, c.is_generated = 'ALWAYS' AS generatedFROM information_schema.columns cJOIN information_schema.tables t ON t.table_schema = c.table_schema AND t.table_name = c.table_name-- WHERE c.table_schema = 'public' AND t.table_type = 'BASE TABLE' AND c.table_name <> 'ov_migrations'++ WHERE c.table_schema = current_schema() AND t.table_type = 'BASE TABLE' AND c.table_name <> 'ov_migrations'ORDER BY c.table_name, c.ordinal_position`);const pks = await db.many(sql`SELECT tc.relname AS table_name, a.attname AS column_name, array_position(i.indkey::int2[], a.attnum) AS posFROM pg_index i JOIN pg_class tc ON tc.oid = i.indrelid JOIN pg_namespace n ON n.oid = tc.relnamespaceJOIN pg_attribute a ON a.attrelid = tc.oid AND a.attnum = ANY(i.indkey)-- WHERE i.indisprimary AND n.nspname = 'public' ORDER BY 1, 3`);++ WHERE i.indisprimary AND n.nspname = current_schema() ORDER BY 1, 3`);const fks = await db.many(sql`SELECT c.conrelid::regclass::text AS child, c.confrelid::regclass::text AS parentFROM pg_constraint c JOIN pg_namespace n ON n.oid = c.connamespace-- WHERE c.contype = 'f' AND n.nspname = 'public'`);++ WHERE c.contype = 'f' AND n.nspname = current_schema()`);const tables = new Map();for (const c of cols) {if (!tables.has(c.table_name)) tables.set(c.table_name, { name: c.table_name, columns: [], pk: [] });@@@ -62,6 +62,21 @@ function analyse(file, src, cfg, asyncNames) {const apis = cfg.apis || {};const syncNames = new Set(cfg.syncNames || []);const localAsync = new Set(asyncNames.local.get(file) || []);++ // const { seed } = require('./workflows/seed'): a function imported from a converted file is async when it is there.++ walk.full(ast, (n) => {++ if (n.type !== 'VariableDeclarator' || n.id.type !== 'ObjectPattern' || !n.init || n.init.type !== 'CallExpression') return;++ const c = n.init;++ if (c.callee.type !== 'Identifier' || c.callee.name !== 'require' || !c.arguments[0] || typeof c.arguments[0].value !== 'string' || !c.arguments[0].value.startsWith('.')) return;++ const base = path.relative(process.cwd(), path.resolve(path.dirname(file), c.arguments[0].value));++ const set = asyncNames.api.get(base.endsWith('.js') ? base : `${base}.js`) || asyncNames.api.get(path.join(base, 'index.js'));++ if (!set) return;++ for (const prop of n.id.properties) {++ if (prop.type !== 'Property' || !prop.key) continue;++ const exported = prop.key.name || prop.key.value;++ const local = prop.value && prop.value.type === 'Identifier' ? prop.value.name : exported;++ if (set.has(exported) && !syncNames.has(exported)) localAsync.add(local);++ }++ });// const view = ops.providerView: an alias of an async module method is async too.walk.full(ast, (n) => {if (n.type !== 'VariableDeclarator' || n.id.type !== 'Identifier' || !n.init || n.init.type !== 'MemberExpression' || n.init.computed) return;
@@ -8,6 +8,11 @@ release may change an API and says so here.
@@ -8,6 +8,11 @@ release may change an API and says so here.
+## 0.20.3 (2026-09-28)
+
+- `importSqlite` reads the target tables of the connection's current schema, not only `public`. A per-run test schema from `createTestDb({ store: 'pg' })` works now, so a rehearsal can use the containers. Production is unchanged: its search path is `public`.
+- Rehearsal note: an embedded PGlite (`--pglite`) can break on very large rows. OpenVibe.AI's `runs` did, while the same import verified on PostgreSQL. When `--pglite` reports every table empty, rehearse on the containers instead (`docs/migrating-to-postgresql.md` §6).
+
## 0.20.2 (2026-09-28)
## 0.20.2 (2026-09-28)
- **`importSqlite` refuses a source table that has no target table.** Before, such a table was skipped without a word. A table that a module created at runtime and the migration forgot would have been dropped by the switch (found converting OpenVibe.AI: `subject_credentials`). SQLite's own tables and FTS5 virtual tables with their shadow tables are still skipped, because an index is rebuilt, not copied. Anything else not imported on purpose goes in the new `skipSource` option, also on `runSqliteMigration`. An audit of the eleven services already switched found nothing lost. `test/db.test.js`.
- **`importSqlite` refuses a source table that has no target table.** Before, such a table was skipped without a word. A table that a module created at runtime and the migration forgot would have been dropped by the switch (found converting OpenVibe.AI: `subject_credentials`). SQLite's own tables and FTS5 virtual tables with their shadow tables are still skipped, because an index is rebuilt, not copied. Anything else not imported on purpose goes in the new `skipSource` option, also on `runSqliteMigration`. An audit of the eleven services already switched found nothing lost. `test/db.test.js`.
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
"stage": "alpha",
"stage": "alpha",
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
- "version": "0.20.2",
+ "version": "0.20.3",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026), §3.2; roadmap §30",
"deployed": false,
"deployed": false,
"deployment": "none: a package released as git tags; services and apps pin a tag",
"deployment": "none: a package released as git tags; services and apps pin a tag",
@@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
-// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.20.2, MIT). Do not edit.
+// Generated by scripts/browser-bundle.js from browser.js (openvibe-sdk 0.20.3, MIT). Do not edit.
// One self-contained ES module: core at the top level plus the browser-safe subpaths as namespaces
// One self-contained ES module: core at the top level plus the browser-safe subpaths as namespaces
// (auth, registry, modules, realtime, media, community, search, chat, ai, jobs, tools, projects, geo). No server-only code: no service tokens, JWT verification, events, identity.
// (auth, registry, modules, realtime, media, community, search, chat, ai, jobs, tools, projects, geo). No server-only code: no service tokens, JWT verification, events, identity.
/* eslint-disable */
/* eslint-disable */
@@ -41,7 +41,7 @@ const { paginate, offsetPager } = __load(7);
@@ -41,7 +41,7 @@ const { paginate, offsetPager } = __load(7);
const semver = __load(6);
const semver = __load(6);
/** The package version (test/version.test.js keeps it equal to package.json). */
/** The package version (test/version.test.js keeps it equal to package.json). */
-const SDK_VERSION = '0.20.2';
+const SDK_VERSION = '0.20.3';
module.exports = {
module.exports = {
createClient,
createClient,
@@ -133,6 +133,8 @@ It changes nothing in the SQLite file.
@@ -133,6 +133,8 @@ It changes nothing in the SQLite file.
- `STATUS.json` (the database) and the README (Depends on: PostgreSQL and Valkey);
- `STATUS.json` (the database) and the README (Depends on: PostgreSQL and Valkey);
- the roadmap plan's WS-X2 migration list.
- the roadmap plan's WS-X2 migration list.
+**Rehearsing on the containers.** An embedded PGlite can break on very large rows (OpenVibe.AI's run payloads). When `--pglite` reports every table empty, import into a schema of the containers: `createTestDb({ migrations, store: 'pg' })`, then `importSqlite({ sqlite, db: t.db })`.
+
## 7. Lessons from the first migrations
## 7. Lessons from the first migrations
- **Tips (2026-09-28)** is the reference: its `postgres` branch has the schema, the importer script, `test/helpers/db.js` and the multi-process integration test. Copy them.
- **Tips (2026-09-28)** is the reference: its `postgres` branch has the schema, the importer script, `test/helpers/db.js` and the multi-process integration test. Copy them.
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-sdk",
"name": "openvibe-sdk",
- "version": "0.20.2",
+ "version": "0.20.3",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-sdk",
"name": "openvibe-sdk",
- "version": "0.20.2",
+ "version": "0.20.3",
"license": "MIT",
"license": "MIT",
"devDependencies": {
"devDependencies": {
"@electric-sql/pglite": "^0.5.8",
"@electric-sql/pglite": "^0.5.8",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-sdk",
"name": "openvibe-sdk",
- "version": "0.20.2",
+ "version": "0.20.3",
"description": "Supported browser and server clients for the OpenVibe platform: auth (incl. developer apps), registry discovery, identity, user modules, events, realtime, media, community, search, chat, AI runs, Tools (registry, run API and jobs), VIP memberships and developer projects, the async PostgreSQL data layer (db) and the Valkey cache, limits store, queue and pub/sub, with deadlines, safe retries, problem errors and trace propagation.",
"description": "Supported browser and server clients for the OpenVibe platform: auth (incl. developer apps), registry discovery, identity, user modules, events, realtime, media, community, search, chat, AI runs, Tools (registry, run API and jobs), VIP memberships and developer projects, the async PostgreSQL data layer (db) and the Valkey cache, limits store, queue and pub/sub, with deadlines, safe retries, problem errors and trace propagation.",
"main": "index.js",
"main": "index.js",
"types": "types/index.d.ts",
"types": "types/index.d.ts",
@@ -8,7 +8,7 @@ const { paginate, offsetPager } = require('./paginate');
@@ -8,7 +8,7 @@ const { paginate, offsetPager } = require('./paginate');
const semver = require('./semver');
const semver = require('./semver');
/** The package version (test/version.test.js keeps it equal to package.json). */
/** The package version (test/version.test.js keeps it equal to package.json). */
-const SDK_VERSION = '0.20.2';
+const SDK_VERSION = '0.20.3';
module.exports = {
module.exports = {
createClient,
createClient,
@@ -96,17 +96,17 @@ async function schemaOf(db) {
@@ -96,17 +96,17 @@ async function schemaOf(db) {
c.is_identity = 'YES' AS identity, c.is_generated = 'ALWAYS' AS generated
c.is_identity = 'YES' AS identity, c.is_generated = 'ALWAYS' AS generated
FROM information_schema.columns c
FROM information_schema.columns c
JOIN information_schema.tables t ON t.table_schema = c.table_schema AND t.table_name = c.table_name
JOIN information_schema.tables t ON t.table_schema = c.table_schema AND t.table_name = c.table_name
- WHERE c.table_schema = 'public' AND t.table_type = 'BASE TABLE' AND c.table_name <> 'ov_migrations'
+ WHERE c.table_schema = current_schema() AND t.table_type = 'BASE TABLE' AND c.table_name <> 'ov_migrations'
ORDER BY c.table_name, c.ordinal_position`);
ORDER BY c.table_name, c.ordinal_position`);
const pks = await db.many(sql`
const pks = await db.many(sql`
SELECT tc.relname AS table_name, a.attname AS column_name, array_position(i.indkey::int2[], a.attnum) AS pos
SELECT tc.relname AS table_name, a.attname AS column_name, array_position(i.indkey::int2[], a.attnum) AS pos
FROM pg_index i JOIN pg_class tc ON tc.oid = i.indrelid JOIN pg_namespace n ON n.oid = tc.relnamespace
FROM pg_index i JOIN pg_class tc ON tc.oid = i.indrelid JOIN pg_namespace n ON n.oid = tc.relnamespace
JOIN pg_attribute a ON a.attrelid = tc.oid AND a.attnum = ANY(i.indkey)
JOIN pg_attribute a ON a.attrelid = tc.oid AND a.attnum = ANY(i.indkey)
- WHERE i.indisprimary AND n.nspname = 'public' ORDER BY 1, 3`);
+ WHERE i.indisprimary AND n.nspname = current_schema() ORDER BY 1, 3`);
const fks = await db.many(sql`
const fks = await db.many(sql`
SELECT c.conrelid::regclass::text AS child, c.confrelid::regclass::text AS parent
SELECT c.conrelid::regclass::text AS child, c.confrelid::regclass::text AS parent
FROM pg_constraint c JOIN pg_namespace n ON n.oid = c.connamespace
FROM pg_constraint c JOIN pg_namespace n ON n.oid = c.connamespace
- WHERE c.contype = 'f' AND n.nspname = 'public'`);
+ WHERE c.contype = 'f' AND n.nspname = current_schema()`);
const tables = new Map();
const tables = new Map();
for (const c of cols) {
for (const c of cols) {
if (!tables.has(c.table_name)) tables.set(c.table_name, { name: c.table_name, columns: [], pk: [] });
if (!tables.has(c.table_name)) tables.set(c.table_name, { name: c.table_name, columns: [], pk: [] });
@@ -62,6 +62,21 @@ function analyse(file, src, cfg, asyncNames) {
@@ -62,6 +62,21 @@ function analyse(file, src, cfg, asyncNames) {
const apis = cfg.apis || {};
const apis = cfg.apis || {};
const syncNames = new Set(cfg.syncNames || []);
const syncNames = new Set(cfg.syncNames || []);
const localAsync = new Set(asyncNames.local.get(file) || []);
const localAsync = new Set(asyncNames.local.get(file) || []);
+ // const { seed } = require('./workflows/seed'): a function imported from a converted file is async when it is there.
+ walk.full(ast, (n) => {
+ if (n.type !== 'VariableDeclarator' || n.id.type !== 'ObjectPattern' || !n.init || n.init.type !== 'CallExpression') return;
+ const c = n.init;
+ if (c.callee.type !== 'Identifier' || c.callee.name !== 'require' || !c.arguments[0] || typeof c.arguments[0].value !== 'string' || !c.arguments[0].value.startsWith('.')) return;
+ const base = path.relative(process.cwd(), path.resolve(path.dirname(file), c.arguments[0].value));
+ const set = asyncNames.api.get(base.endsWith('.js') ? base : `${base}.js`) || asyncNames.api.get(path.join(base, 'index.js'));
+ if (!set) return;
+ for (const prop of n.id.properties) {
+ if (prop.type !== 'Property' || !prop.key) continue;
+ const exported = prop.key.name || prop.key.value;
+ const local = prop.value && prop.value.type === 'Identifier' ? prop.value.name : exported;
+ if (set.has(exported) && !syncNames.has(exported)) localAsync.add(local);
+ }
+ });
// const view = ops.providerView: an alias of an async module method is async too.
// const view = ops.providerView: an alias of an async module method is async too.
walk.full(ast, (n) => {
walk.full(ast, (n) => {
if (n.type !== 'VariableDeclarator' || n.id.type !== 'Identifier' || !n.init || n.init.type !== 'MemberExpression' || n.init.computed) return;
if (n.type !== 'VariableDeclarator' || n.id.type !== 'Identifier' || !n.init || n.init.type !== 'MemberExpression' || n.init.computed) return;