openvibers/openvibe.examples Commits
Examples: the webhook-consumer's inbox runs on PostgreSQL (PGlite locally), not better-sqlite3 (#16)
Changes
@@@ -30,7 +30,7 @@ Each example is small, runs on its own and proves one thing with a smoke test.| [node-server-app](examples/node-server-app) | Client-credentials app token per audience and what it carries, registry discovery, contracts version check, trace propagation | `openvibe-sdk/auth` (`getTokenInfo`), `/registry`, `/core` || [media-uploader](examples/media-uploader) | Upload into, read from and delete in your project's Media tenant; sandbox files come back with signed URLs | `openvibe-sdk/media` || [event-subscriber](examples/event-subscriber) | Publish to your project's topic `app.<project_key>.*`, pull it with a durable cursor, gaps; anonymous realtime for public events | `openvibe-sdk/events` (`createAppEvents`), `/realtime` |--| [webhook-consumer](examples/webhook-consumer) | An app subscription on your own topic; `X-OpenVibe-Signature-V2` (±300 s replay window) verified on the raw body; exactly-once handling with an inbox; secret rotation | `openvibe-sdk/events` (`createAppEvents`, `parseDelivery`, `createInbox`) |++| [webhook-consumer](examples/webhook-consumer) | An app subscription on your own topic; `X-OpenVibe-Signature-V2` (±300 s replay window) verified on the raw body; exactly-once handling with an inbox; secret rotation | `openvibe-sdk/events` (`createAppEvents`, `parseDelivery`, `createPgInbox`) || [tool-job](examples/tool-job) | An OpenVibe.Tools job submitted once (idempotency key), reattached after a restart, its SSE stream resumed with `Last-Event-ID` | `openvibe-sdk/jobs` || [oauth-app](examples/oauth-app) | Authorization code + PKCE for a confidential app, exchange on the server, offline app-token verification, session hygiene | `openvibe-sdk/auth` || [chat-bot](examples/chat-bot) | A bot on Chat's WebSocket protocol that stays in its one configured room and always identifies as a bot | Chat `/ws/chat` protocol (no SDK client: Chat is first-party) |@@@ -3,7 +3,9 @@ OV_WEBHOOK_SECRET=# While rotating: the previous secret, accepted until you remove it.OV_WEBHOOK_SECRET_PREVIOUS=OV_WEBHOOK_PATH=/webhooks/openvibe--OV_DB_PATH=./data/webhook-consumer.db++# PostgreSQL for the inbox and the received_events table; empty: an embedded PGlite in OV_DATA_DIR.++OV_DATABASE_URL=++OV_DATA_DIR=./data/pgOV_PORT=3003# subscribe.js: a CONFIDENTIAL sandbox app holding events.app.subscribe (audience openvibe.events).@@@ -21,8 +21,10 @@ node --env-file=.env --env-file=.env.webhook server.js- **Signature first, on the raw bytes.** `parseDelivery()` checks`sha256=<HMAC-SHA256 of the raw body>` in constant time before anything is parsed. A wrongsecret, a changed byte or a missing header is `401`, with no detail.--- **Exactly once.** `createInbox(db).once(consumer, event_id, fn)` writes the receipt and runs your-- handler in one SQLite transaction. A redelivery (a retry after a lost `2xx`, a replay) is++- **Exactly once.** `createPgInbox(db).once(consumer, event_id, fn)` writes the receipt and runs your++ handler in one PostgreSQL transaction (`fn` gets the transaction handle; write with it and the write++ commits with the receipt). The database is `OV_DATABASE_URL`, or an embedded PGlite in `OV_DATA_DIR`++ (default `./data/pg`) when no URL is set, so the example needs nothing installed but Node. A redelivery (a retry after a lost `2xx`, a replay) isanswered `200 { duplicate: true }` without running the handler. A handler that throws leavesno receipt, the consumer answers `500`, and Events retries with backoff.- **The signed body is the truth.** `X-OpenVibe-Event-Id` must match the body's `event_id`.@@@ -1,6 +1,6 @@{"name": "openvibe-example-webhook-consumer",-- "version": "0.2.0",++ "version": "0.3.0","private": true,"description": "Webhook consumer: verify X-OpenVibe-Signature and process each event exactly once through an inbox.","license": "MIT",@@@ -14,6 +14,7 @@},"dependencies": {"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0",-- "better-sqlite3": "^11.10.0"++ "@electric-sql/pglite": "^0.5.8",++ "pg": "^8.23.0"}}@@@ -6,7 +6,7 @@** node --env-file=.env server.js # POST http://localhost:3003/webhooks/openvibe*-- * A delivery is POSTed as { "event": <events.event-envelope@1>, "seq": <n> } with++ * A delivery is POSTed as { "event": <events.event-envelope@1> } (no sequence number: dedupe on the event id) with* X-OpenVibe-Timestamp: <unix seconds> and* X-OpenVibe-Signature-V2: t=<that timestamp>,v2=<HMAC-SHA256 of "<t>.<raw body>" with the subscription secret>* (plus the older body-only X-OpenVibe-Signature, which this consumer does not accept on its own).@@@ -16,16 +16,20 @@* constant time, refuses a timestamp more than 300 s from this clock or a missing v2 header* (a replayed capture), and parses it. A bad signature is 401 and nothing else is said.* 3. Delivery is at least once (retries, replays). inbox.once() records the event id and runs-- * the handler in ONE SQLite transaction, so a repeat is answered 200 without running it again,-- * and a handler that throws leaves no receipt: the 500 makes Events retry later.++ * the handler in ONE PostgreSQL transaction (openvibe-sdk/events createPgInbox), so a repeat is++ * answered 200 without running it again, and a handler that throws leaves no receipt: the 500 makes++ * Events retry later.++ *++ * The database is PostgreSQL: OV_DATABASE_URL, or an embedded PGlite in OV_DATA_DIR (default ./data/pg) when no URL++ * is set, so the example runs with nothing installed but Node.** Secret rotation: set OV_WEBHOOK_SECRET_PREVIOUS to the old secret while both are in use.*/const fs = require('node:fs');const http = require('node:http');const path = require('node:path');--const Database = require('better-sqlite3');--const { parseDelivery, createInbox } = require('openvibe-sdk/events');++const { createDb, sql } = require('openvibe-sdk/db');++const { parseDelivery, createPgInbox, inboxSchema } = require('openvibe-sdk/events');const MAX_BODY = 1024 * 1024;const CONSUMER = 'webhook-consumer';@@@ -38,36 +42,41 @@ function loadConfig(env = process.env) {}return {secrets: [env.OV_WEBHOOK_SECRET, env.OV_WEBHOOK_SECRET_PREVIOUS].filter(Boolean),-- dbPath: env.OV_DB_PATH || path.join(__dirname, 'data', 'webhook-consumer.db'),++ databaseUrl: env.OV_DATABASE_URL || null,++ dataDir: env.OV_DATA_DIR || path.join(__dirname, 'data', 'pg'),port: Number(env.OV_PORT || 3003),path: env.OV_WEBHOOK_PATH || '/webhooks/openvibe',};}--function openDatabase(dbPath) {-- if (dbPath !== ':memory:') fs.mkdirSync(path.dirname(dbPath), { recursive: true });-- const db = new Database(dbPath);-- db.pragma('journal_mode = WAL');-- db.exec(`CREATE TABLE IF NOT EXISTS received_events (-- event_id TEXT PRIMARY KEY,-- event_type TEXT NOT NULL,-- source TEXT NOT NULL,-- seq INTEGER,-- subject TEXT,-- received_at TEXT NOT NULL++/** The database and its two tables: the inbox's receipts and the app's own row per event. */++async function openDatabase(config) {++ let db;++ if (config.databaseUrl) db = createDb({ url: config.databaseUrl, service: 'webhook-consumer' });++ else {++ if (config.dataDir !== 'memory') fs.mkdirSync(config.dataDir, { recursive: true });++ db = createDb({ pglite: config.dataDir === 'memory' ? true : config.dataDir, service: 'webhook-consumer' });++ }++ await db.query(inboxSchema());++ await db.query(`CREATE TABLE IF NOT EXISTS received_events (++ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,++ event_id text NOT NULL UNIQUE,++ event_type text NOT NULL,++ source text NOT NULL,++ subject text,++ received_at timestamptz NOT NULL DEFAULT now())`);return db;}/**-- * createConsumer(config, { db, handle, log })-- * handle(event, { seq, attempt }) runs inside the inbox transaction and must be synchronous-- * (better-sqlite3). Put side effects that need the network in your own outbox table instead.++ * createConsumer(config, { db, handle, log }) db: from openDatabase(config)++ * handle(event, { attempt }, t) runs inside the inbox transaction (t, the transaction handle: write with it and the++ * write commits with the receipt). Put side effects that need the network in your own outbox table instead.*/--function createConsumer(config, { db = openDatabase(config.dbPath), handle = () => {}, log = console } = {}) {-- const inbox = createInbox(db);-- inbox.ensureSchema();-- const record = db.prepare('INSERT INTO received_events (event_id, event_type, source, seq, subject, received_at) VALUES (?, ?, ?, ?, ?, ?)');++function createConsumer(config, { db, handle = () => {}, log = console } = {}) {++ if (!db) throw new TypeError('createConsumer: pass { db } from openDatabase(config)');++ const inbox = createPgInbox(db);function verify(raw, headers) {for (const secret of config.secrets) {@@@ -77,20 +86,20 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()return null;}-- function receive(raw, headers) {++ async function receive(raw, headers) {const delivery = verify(raw, headers);if (!delivery) return { status: 401, body: { error: 'bad_signature' } };-- const { event, seq, attempt } = delivery;++ const { event, attempt } = delivery;if (!event || typeof event.event_id !== 'string' || typeof event.event_type !== 'string') return { status: 400, body: { error: 'bad_envelope' } };// The header is informational; the signed body is the truth. They must agree.const headerId = headers['x-openvibe-event-id'];if (headerId && headerId !== event.event_id) return { status: 400, body: { error: 'event_id_mismatch' } };-- const out = inbox.once(CONSUMER, event.event_id, () => {-- record.run(event.event_id, event.event_type, String(event.source || ''), Number.isFinite(seq) ? seq : null,-- event.subject ? `${event.subject.type}:${event.subject.id}` : null, new Date().toISOString());-- return handle(event, { seq, attempt });++ const out = await inbox.once(CONSUMER, event.event_id, async (t) => {++ await t.exec(sql`INSERT INTO received_events (event_id, event_type, source, subject)++ VALUES (${event.event_id}, ${event.event_type}, ${String(event.source || '')}, ${event.subject ? `${event.subject.type}:${event.subject.id}` : null})`);++ return await handle(event, { attempt }, t);});-- log.log(`[webhook] ${event.event_type} ${event.event_id} seq=${seq} attempt=${attempt}${out.duplicate ? ' (duplicate, skipped)' : ''}`);++ log.log(`[webhook] ${event.event_type} ${event.event_id} attempt=${attempt}${out.duplicate ? ' (duplicate, skipped)' : ''}`);return { status: 200, body: { ok: true, duplicate: out.duplicate } };}@@@ -110,10 +119,10 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()if (size > MAX_BODY) { tooLarge = true; chunks.length = 0; return; }chunks.push(c);});-- req.on('end', () => {++ req.on('end', async () => {if (tooLarge) return send(413, { error: 'too_large' });try {-- const out = receive(Buffer.concat(chunks), req.headers);++ const out = await receive(Buffer.concat(chunks), req.headers);send(out.status, out.body);} catch (err) {// No receipt was written: Events retries this delivery with backoff.@@@ -129,12 +138,15 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()if (require.main === module) {let config;try { config = loadConfig(); } catch (err) { console.error(err.message); process.exit(2); }-- const { server } = createConsumer(config, {-- handle(event) {-- // Your side effect goes here (synchronous, same transaction as the receipt).-- },-- });-- server.listen(config.port, () => console.log(`webhook-consumer: POST http://localhost:${config.port}${config.path}`));++ openDatabase(config).then((db) => {++ const { server } = createConsumer(config, {++ db,++ async handle(event, meta, t) {++ // Your side effect goes here: write with `t` and it commits with the receipt.++ },++ });++ server.listen(config.port, () => console.log(`webhook-consumer: POST http://localhost:${config.port}${config.path}`));++ }).catch((err) => { console.error(`webhook-consumer: ${err.message}`); process.exit(1); });}module.exports = { loadConfig, openDatabase, createConsumer, CONSUMER };@@@ -45,12 +45,13 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';// 2. Run the consumer with that secret.const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-webhook-'));-- const config = loadConfig({ OV_WEBHOOK_SECRET: created.secret, OV_DB_PATH: path.join(dir, 'consumer.db') });++ const config = loadConfig({ OV_WEBHOOK_SECRET: created.secret, OV_DATA_DIR: path.join(dir, 'pg') });++ const db = await openDatabase(config);const handled = [];let failNext = false;const logs = [];const consumer = createConsumer(config, {-- db: openDatabase(config.dbPath),++ db,handle(event) {if (failNext) { failNext = false; throw new Error('downstream unavailable'); }handled.push(event.event_id);@@@ -95,7 +96,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';round = await platform.deliverEvents({ fetch: worker });assert.equal(round.failed, 1);assert.equal(round.attempts[0].status, 500);-- assert.equal(consumer.inbox.seen('webhook-consumer', e2.event_id), false);++ assert.equal(await consumer.inbox.seen('webhook-consumer', e2.event_id), false);round = await platform.deliverEvents({ fetch: worker });assert.deepEqual([round.delivered, round.attempts[0].attempt], [1, 2]);assert.deepEqual(handled, [e1.event_id, e2.event_id]);@@@ -116,8 +117,8 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';// Secret rotation: deliveries signed with the previous secret are accepted while it is configured.consumer.server.close();-- const rotated = createConsumer(loadConfig({ OV_WEBHOOK_SECRET: 'whsec_new_secret_after_rotation_0000000000', OV_WEBHOOK_SECRET_PREVIOUS: created.secret, OV_DB_PATH: config.dbPath }), {-- db: consumer.db, handle: (event) => handled.push(event.event_id), log: { log() {}, error() {} },++ const rotated = createConsumer(loadConfig({ OV_WEBHOOK_SECRET: 'whsec_new_secret_after_rotation_0000000000', OV_WEBHOOK_SECRET_PREVIOUS: created.secret, OV_DATA_DIR: config.dataDir }), {++ db, handle: (event) => handled.push(event.event_id), log: { log() {}, error() {} },});await new Promise((res) => rotated.server.listen(0, '127.0.0.1', res));local = `http://127.0.0.1:${rotated.server.address().port}/webhooks/openvibe`;@@@ -130,7 +131,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';assert.deepEqual(handled, [e1.event_id, e2.event_id, e3.event_id, e4.event_id]);// The receipt and the app's own row were written together.-- const rows = consumer.db.prepare('SELECT event_id FROM received_events ORDER BY seq').all().map((x) => x.event_id);++ const rows = (await db.many('SELECT event_id FROM received_events ORDER BY id')).map((x) => x.event_id);assert.deepEqual(rows, handled);// Oversized bodies are refused before parsing.@@@ -141,7 +142,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';assert.ok(!logs.join('\n').includes(created.secret));rotated.server.close();-- consumer.db.close();++ await db.close();fs.rmSync(dir, { recursive: true, force: true });console.log('webhook-consumer: ok');})().catch((err) => { console.error(err); process.exit(1); });@@@ -112,16 +112,23 @@},"examples/webhook-consumer": {"name": "openvibe-example-webhook-consumer",-- "version": "0.2.0",++ "version": "0.3.0","license": "MIT","dependencies": {-- "better-sqlite3": "^11.10.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0"++ "@electric-sql/pglite": "^0.5.8",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0",++ "pg": "^8.23.0"},"engines": {"node": ">=22"}},++ "node_modules/@electric-sql/pglite": {++ "version": "0.5.8",++ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.5.8.tgz",++ "integrity": "sha512-n9tsbUOhwx2epK1V0ZG9Ar4SHWUju04dhmzZXiSBXwBoleOvIfals33NAaWgagQVAL4Rbvx/Ptsu3P+pA09f6Q==",++ "license": "Apache-2.0"++ },"node_modules/ajv": {"version": "8.20.0","resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",@@@ -155,138 +162,6 @@}}},-- "node_modules/base64-js": {-- "version": "1.5.1",-- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",-- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],-- "license": "MIT"-- },-- "node_modules/better-sqlite3": {-- "version": "11.10.0",-- "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",-- "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",-- "hasInstallScript": true,-- "license": "MIT",-- "dependencies": {-- "bindings": "^1.5.0",-- "prebuild-install": "^7.1.1"-- }-- },-- "node_modules/bindings": {-- "version": "1.5.0",-- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",-- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",-- "license": "MIT",-- "dependencies": {-- "file-uri-to-path": "1.0.0"-- }-- },-- "node_modules/bl": {-- "version": "4.1.0",-- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",-- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",-- "license": "MIT",-- "dependencies": {-- "buffer": "^5.5.0",-- "inherits": "^2.0.4",-- "readable-stream": "^3.4.0"-- }-- },-- "node_modules/buffer": {-- "version": "5.7.1",-- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",-- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],-- "license": "MIT",-- "dependencies": {-- "base64-js": "^1.3.1",-- "ieee754": "^1.1.13"-- }-- },-- "node_modules/chownr": {-- "version": "1.1.4",-- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",-- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",-- "license": "ISC"-- },-- "node_modules/decompress-response": {-- "version": "6.0.0",-- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",-- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",-- "license": "MIT",-- "dependencies": {-- "mimic-response": "^3.1.0"-- },-- "engines": {-- "node": ">=10"-- },-- "funding": {-- "url": "https://github.com/sponsors/sindresorhus"-- }-- },-- "node_modules/deep-extend": {-- "version": "0.6.0",-- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",-- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",-- "license": "MIT",-- "engines": {-- "node": ">=4.0.0"-- }-- },-- "node_modules/detect-libc": {-- "version": "2.1.2",-- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",-- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",-- "license": "Apache-2.0",-- "engines": {-- "node": ">=8"-- }-- },-- "node_modules/end-of-stream": {-- "version": "1.4.5",-- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",-- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",-- "license": "MIT",-- "dependencies": {-- "once": "^1.4.0"-- }-- },-- "node_modules/expand-template": {-- "version": "2.0.3",-- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",-- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",-- "license": "(MIT OR WTFPL)",-- "engines": {-- "node": ">=6"-- }-- },"node_modules/fast-deep-equal": {"version": "3.1.3","resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",@@@ -309,116 +184,12 @@],"license": "BSD-3-Clause"},-- "node_modules/file-uri-to-path": {-- "version": "1.0.0",-- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",-- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",-- "license": "MIT"-- },-- "node_modules/fs-constants": {-- "version": "1.0.0",-- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",-- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",-- "license": "MIT"-- },-- "node_modules/github-from-package": {-- "version": "0.0.0",-- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",-- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",-- "license": "MIT"-- },-- "node_modules/ieee754": {-- "version": "1.2.1",-- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",-- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],-- "license": "BSD-3-Clause"-- },-- "node_modules/inherits": {-- "version": "2.0.4",-- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",-- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",-- "license": "ISC"-- },-- "node_modules/ini": {-- "version": "1.3.8",-- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",-- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",-- "license": "ISC"-- },"node_modules/json-schema-traverse": {"version": "1.0.0","resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz","integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==","license": "MIT"},-- "node_modules/mimic-response": {-- "version": "3.1.0",-- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",-- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",-- "license": "MIT",-- "engines": {-- "node": ">=10"-- },-- "funding": {-- "url": "https://github.com/sponsors/sindresorhus"-- }-- },-- "node_modules/minimist": {-- "version": "1.2.8",-- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",-- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",-- "license": "MIT",-- "funding": {-- "url": "https://github.com/sponsors/ljharb"-- }-- },-- "node_modules/mkdirp-classic": {-- "version": "0.5.3",-- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",-- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",-- "license": "MIT"-- },-- "node_modules/napi-build-utils": {-- "version": "2.0.0",-- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",-- "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",-- "license": "MIT"-- },-- "node_modules/node-abi": {-- "version": "3.96.0",-- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.96.0.tgz",-- "integrity": "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==",-- "license": "MIT",-- "dependencies": {-- "semver": "^7.3.5"-- },-- "engines": {-- "node": ">=10"-- }-- },-- "node_modules/once": {-- "version": "1.4.0",-- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",-- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",-- "license": "ISC",-- "dependencies": {-- "wrappy": "1"-- }-- },"node_modules/openvibe-contracts": {"version": "0.129.0","resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",@@@ -500,227 +271,151 @@}}},-- "node_modules/prebuild-install": {-- "version": "7.1.3",-- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",-- "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",-- "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",++ "node_modules/pg": {++ "version": "8.23.1",++ "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz",++ "integrity": "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ==","license": "MIT","dependencies": {-- "detect-libc": "^2.0.0",-- "expand-template": "^2.0.3",-- "github-from-package": "0.0.0",-- "minimist": "^1.2.3",-- "mkdirp-classic": "^0.5.3",-- "napi-build-utils": "^2.0.0",-- "node-abi": "^3.3.0",-- "pump": "^3.0.0",-- "rc": "^1.2.7",-- "simple-get": "^4.0.0",-- "tar-fs": "^2.0.0",-- "tunnel-agent": "^0.6.0"-- },-- "bin": {-- "prebuild-install": "bin.js"++ "pg-connection-string": "^2.14.1",++ "pg-pool": "^3.14.0",++ "pg-protocol": "^1.16.1",++ "pg-types": "2.2.0",++ "pgpass": "1.0.5"},"engines": {-- "node": ">=10"++ "node": ">= 16.0.0"++ },++ "optionalDependencies": {++ "pg-cloudflare": "^1.4.1"++ },++ "peerDependencies": {++ "pg-native": ">=3.0.1"++ },++ "peerDependenciesMeta": {++ "pg-native": {++ "optional": true++ }}},-- "node_modules/pump": {-- "version": "3.0.4",-- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",-- "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",++ "node_modules/pg-cloudflare": {++ "version": "1.4.1",++ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.1.tgz",++ "integrity": "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ==","license": "MIT",-- "dependencies": {-- "end-of-stream": "^1.1.0",-- "once": "^1.3.1"-- }++ "optional": true},-- "node_modules/rc": {-- "version": "1.2.8",-- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",-- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",-- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",-- "dependencies": {-- "deep-extend": "^0.6.0",-- "ini": "~1.3.0",-- "minimist": "^1.2.0",-- "strip-json-comments": "~2.0.1"-- },-- "bin": {-- "rc": "cli.js"-- }++ "node_modules/pg-connection-string": {++ "version": "2.14.1",++ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz",++ "integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==",++ "license": "MIT"},-- "node_modules/readable-stream": {-- "version": "3.6.2",-- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",-- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",-- "license": "MIT",-- "dependencies": {-- "inherits": "^2.0.3",-- "string_decoder": "^1.1.1",-- "util-deprecate": "^1.0.1"-- },++ "node_modules/pg-int8": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",++ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",++ "license": "ISC","engines": {-- "node": ">= 6"++ "node": ">=4.0.0"}},-- "node_modules/require-from-string": {-- "version": "2.0.2",-- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",-- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",++ "node_modules/pg-pool": {++ "version": "3.14.0",++ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",++ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==","license": "MIT",-- "engines": {-- "node": ">=0.10.0"++ "peerDependencies": {++ "pg": ">=8.0"}},-- "node_modules/safe-buffer": {-- "version": "5.2.1",-- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",-- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],++ "node_modules/pg-protocol": {++ "version": "1.16.1",++ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.1.tgz",++ "integrity": "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg==","license": "MIT"},-- "node_modules/semver": {-- "version": "7.8.5",-- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",-- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",-- "license": "ISC",-- "bin": {-- "semver": "bin/semver.js"++ "node_modules/pg-types": {++ "version": "2.2.0",++ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",++ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",++ "license": "MIT",++ "dependencies": {++ "pg-int8": "1.0.1",++ "postgres-array": "~2.0.0",++ "postgres-bytea": "~1.0.0",++ "postgres-date": "~1.0.4",++ "postgres-interval": "^1.1.0"},"engines": {-- "node": ">=10"++ "node": ">=4"}},-- "node_modules/simple-concat": {-- "version": "1.0.1",-- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",-- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],-- "license": "MIT"-- },-- "node_modules/simple-get": {-- "version": "4.0.1",-- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",-- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",-- "funding": [-- {-- "type": "github",-- "url": "https://github.com/sponsors/feross"-- },-- {-- "type": "patreon",-- "url": "https://www.patreon.com/feross"-- },-- {-- "type": "consulting",-- "url": "https://feross.org/support"-- }-- ],++ "node_modules/pgpass": {++ "version": "1.0.5",++ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",++ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==","license": "MIT","dependencies": {-- "decompress-response": "^6.0.0",-- "once": "^1.3.1",-- "simple-concat": "^1.0.0"++ "split2": "^4.1.0"}},-- "node_modules/string_decoder": {-- "version": "1.3.0",-- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",-- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",++ "node_modules/postgres-array": {++ "version": "2.0.0",++ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",++ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==","license": "MIT",-- "dependencies": {-- "safe-buffer": "~5.2.0"++ "engines": {++ "node": ">=4"}},-- "node_modules/strip-json-comments": {-- "version": "2.0.1",-- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",-- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",++ "node_modules/postgres-bytea": {++ "version": "1.0.1",++ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",++ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==","license": "MIT","engines": {"node": ">=0.10.0"}},-- "node_modules/tar-fs": {-- "version": "2.1.5",-- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",-- "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",++ "node_modules/postgres-date": {++ "version": "1.0.7",++ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",++ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==","license": "MIT",-- "dependencies": {-- "chownr": "^1.1.1",-- "mkdirp-classic": "^0.5.2",-- "pump": "^3.0.0",-- "tar-stream": "^2.1.4"++ "engines": {++ "node": ">=0.10.0"}},-- "node_modules/tar-stream": {-- "version": "2.2.0",-- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",-- "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",++ "node_modules/postgres-interval": {++ "version": "1.2.0",++ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",++ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==","license": "MIT","dependencies": {-- "bl": "^4.0.3",-- "end-of-stream": "^1.4.1",-- "fs-constants": "^1.0.0",-- "inherits": "^2.0.3",-- "readable-stream": "^3.1.1"++ "xtend": "^4.0.0"},"engines": {-- "node": ">=6"++ "node": ">=0.10.0"}},-- "node_modules/tunnel-agent": {-- "version": "0.6.0",-- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",-- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",-- "license": "Apache-2.0",-- "dependencies": {-- "safe-buffer": "^5.0.1"-- },++ "node_modules/require-from-string": {++ "version": "2.0.2",++ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",++ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",++ "license": "MIT","engines": {-- "node": "*"++ "node": ">=0.10.0"}},-- "node_modules/util-deprecate": {-- "version": "1.0.2",-- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",-- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",-- "license": "MIT"-- },-- "node_modules/wrappy": {-- "version": "1.0.2",-- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",-- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",-- "license": "ISC"++ "node_modules/split2": {++ "version": "4.2.0",++ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",++ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",++ "license": "ISC",++ "engines": {++ "node": ">= 10.x"++ }},"node_modules/ws": {"version": "8.21.3",@@@ -743,6 +438,15 @@"optional": true}}++ },++ "node_modules/xtend": {++ "version": "4.0.2",++ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",++ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",++ "license": "MIT",++ "engines": {++ "node": ">=0.4"++ }}}}
@@ -30,7 +30,7 @@ Each example is small, runs on its own and proves one thing with a smoke test.
@@ -30,7 +30,7 @@ Each example is small, runs on its own and proves one thing with a smoke test.
| [node-server-app](examples/node-server-app) | Client-credentials app token per audience and what it carries, registry discovery, contracts version check, trace propagation | `openvibe-sdk/auth` (`getTokenInfo`), `/registry`, `/core` |
| [node-server-app](examples/node-server-app) | Client-credentials app token per audience and what it carries, registry discovery, contracts version check, trace propagation | `openvibe-sdk/auth` (`getTokenInfo`), `/registry`, `/core` |
| [media-uploader](examples/media-uploader) | Upload into, read from and delete in your project's Media tenant; sandbox files come back with signed URLs | `openvibe-sdk/media` |
| [media-uploader](examples/media-uploader) | Upload into, read from and delete in your project's Media tenant; sandbox files come back with signed URLs | `openvibe-sdk/media` |
| [event-subscriber](examples/event-subscriber) | Publish to your project's topic `app.<project_key>.*`, pull it with a durable cursor, gaps; anonymous realtime for public events | `openvibe-sdk/events` (`createAppEvents`), `/realtime` |
| [event-subscriber](examples/event-subscriber) | Publish to your project's topic `app.<project_key>.*`, pull it with a durable cursor, gaps; anonymous realtime for public events | `openvibe-sdk/events` (`createAppEvents`), `/realtime` |
-| [webhook-consumer](examples/webhook-consumer) | An app subscription on your own topic; `X-OpenVibe-Signature-V2` (±300 s replay window) verified on the raw body; exactly-once handling with an inbox; secret rotation | `openvibe-sdk/events` (`createAppEvents`, `parseDelivery`, `createInbox`) |
+| [webhook-consumer](examples/webhook-consumer) | An app subscription on your own topic; `X-OpenVibe-Signature-V2` (±300 s replay window) verified on the raw body; exactly-once handling with an inbox; secret rotation | `openvibe-sdk/events` (`createAppEvents`, `parseDelivery`, `createPgInbox`) |
| [tool-job](examples/tool-job) | An OpenVibe.Tools job submitted once (idempotency key), reattached after a restart, its SSE stream resumed with `Last-Event-ID` | `openvibe-sdk/jobs` |
| [tool-job](examples/tool-job) | An OpenVibe.Tools job submitted once (idempotency key), reattached after a restart, its SSE stream resumed with `Last-Event-ID` | `openvibe-sdk/jobs` |
| [oauth-app](examples/oauth-app) | Authorization code + PKCE for a confidential app, exchange on the server, offline app-token verification, session hygiene | `openvibe-sdk/auth` |
| [oauth-app](examples/oauth-app) | Authorization code + PKCE for a confidential app, exchange on the server, offline app-token verification, session hygiene | `openvibe-sdk/auth` |
| [chat-bot](examples/chat-bot) | A bot on Chat's WebSocket protocol that stays in its one configured room and always identifies as a bot | Chat `/ws/chat` protocol (no SDK client: Chat is first-party) |
| [chat-bot](examples/chat-bot) | A bot on Chat's WebSocket protocol that stays in its one configured room and always identifies as a bot | Chat `/ws/chat` protocol (no SDK client: Chat is first-party) |
@@ -3,7 +3,9 @@ OV_WEBHOOK_SECRET=
@@ -3,7 +3,9 @@ OV_WEBHOOK_SECRET=
# While rotating: the previous secret, accepted until you remove it.
# While rotating: the previous secret, accepted until you remove it.
OV_WEBHOOK_SECRET_PREVIOUS=
OV_WEBHOOK_SECRET_PREVIOUS=
OV_WEBHOOK_PATH=/webhooks/openvibe
OV_WEBHOOK_PATH=/webhooks/openvibe
-OV_DB_PATH=./data/webhook-consumer.db
+# PostgreSQL for the inbox and the received_events table; empty: an embedded PGlite in OV_DATA_DIR.
+OV_DATABASE_URL=
+OV_DATA_DIR=./data/pg
OV_PORT=3003
OV_PORT=3003
# subscribe.js: a CONFIDENTIAL sandbox app holding events.app.subscribe (audience openvibe.events).
# subscribe.js: a CONFIDENTIAL sandbox app holding events.app.subscribe (audience openvibe.events).
@@ -21,8 +21,10 @@ node --env-file=.env --env-file=.env.webhook server.js
@@ -21,8 +21,10 @@ node --env-file=.env --env-file=.env.webhook server.js
- **Signature first, on the raw bytes.** `parseDelivery()` checks
- **Signature first, on the raw bytes.** `parseDelivery()` checks
`sha256=<HMAC-SHA256 of the raw body>` in constant time before anything is parsed. A wrong
`sha256=<HMAC-SHA256 of the raw body>` in constant time before anything is parsed. A wrong
secret, a changed byte or a missing header is `401`, with no detail.
secret, a changed byte or a missing header is `401`, with no detail.
-- **Exactly once.** `createInbox(db).once(consumer, event_id, fn)` writes the receipt and runs your
- handler in one SQLite transaction. A redelivery (a retry after a lost `2xx`, a replay) is
+- **Exactly once.** `createPgInbox(db).once(consumer, event_id, fn)` writes the receipt and runs your
+ handler in one PostgreSQL transaction (`fn` gets the transaction handle; write with it and the write
+ commits with the receipt). The database is `OV_DATABASE_URL`, or an embedded PGlite in `OV_DATA_DIR`
+ (default `./data/pg`) when no URL is set, so the example needs nothing installed but Node. A redelivery (a retry after a lost `2xx`, a replay) is
answered `200 { duplicate: true }` without running the handler. A handler that throws leaves
answered `200 { duplicate: true }` without running the handler. A handler that throws leaves
no receipt, the consumer answers `500`, and Events retries with backoff.
no receipt, the consumer answers `500`, and Events retries with backoff.
- **The signed body is the truth.** `X-OpenVibe-Event-Id` must match the body's `event_id`.
- **The signed body is the truth.** `X-OpenVibe-Event-Id` must match the body's `event_id`.
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-example-webhook-consumer",
"name": "openvibe-example-webhook-consumer",
- "version": "0.2.0",
+ "version": "0.3.0",
"private": true,
"private": true,
"description": "Webhook consumer: verify X-OpenVibe-Signature and process each event exactly once through an inbox.",
"description": "Webhook consumer: verify X-OpenVibe-Signature and process each event exactly once through an inbox.",
"license": "MIT",
"license": "MIT",
@@ -14,6 +14,7 @@
@@ -14,6 +14,7 @@
},
},
"dependencies": {
"dependencies": {
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0",
- "better-sqlite3": "^11.10.0"
+ "@electric-sql/pglite": "^0.5.8",
+ "pg": "^8.23.0"
}
}
}
}
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
*
*
* node --env-file=.env server.js # POST http://localhost:3003/webhooks/openvibe
* node --env-file=.env server.js # POST http://localhost:3003/webhooks/openvibe
*
*
- * A delivery is POSTed as { "event": <events.event-envelope@1>, "seq": <n> } with
+ * A delivery is POSTed as { "event": <events.event-envelope@1> } (no sequence number: dedupe on the event id) with
* X-OpenVibe-Timestamp: <unix seconds> and
* X-OpenVibe-Timestamp: <unix seconds> and
* X-OpenVibe-Signature-V2: t=<that timestamp>,v2=<HMAC-SHA256 of "<t>.<raw body>" with the subscription secret>
* X-OpenVibe-Signature-V2: t=<that timestamp>,v2=<HMAC-SHA256 of "<t>.<raw body>" with the subscription secret>
* (plus the older body-only X-OpenVibe-Signature, which this consumer does not accept on its own).
* (plus the older body-only X-OpenVibe-Signature, which this consumer does not accept on its own).
@@ -16,16 +16,20 @@
@@ -16,16 +16,20 @@
* constant time, refuses a timestamp more than 300 s from this clock or a missing v2 header
* constant time, refuses a timestamp more than 300 s from this clock or a missing v2 header
* (a replayed capture), and parses it. A bad signature is 401 and nothing else is said.
* (a replayed capture), and parses it. A bad signature is 401 and nothing else is said.
* 3. Delivery is at least once (retries, replays). inbox.once() records the event id and runs
* 3. Delivery is at least once (retries, replays). inbox.once() records the event id and runs
- * the handler in ONE SQLite transaction, so a repeat is answered 200 without running it again,
- * and a handler that throws leaves no receipt: the 500 makes Events retry later.
+ * the handler in ONE PostgreSQL transaction (openvibe-sdk/events createPgInbox), so a repeat is
+ * answered 200 without running it again, and a handler that throws leaves no receipt: the 500 makes
+ * Events retry later.
+ *
+ * The database is PostgreSQL: OV_DATABASE_URL, or an embedded PGlite in OV_DATA_DIR (default ./data/pg) when no URL
+ * is set, so the example runs with nothing installed but Node.
*
*
* Secret rotation: set OV_WEBHOOK_SECRET_PREVIOUS to the old secret while both are in use.
* Secret rotation: set OV_WEBHOOK_SECRET_PREVIOUS to the old secret while both are in use.
*/
*/
const fs = require('node:fs');
const fs = require('node:fs');
const http = require('node:http');
const http = require('node:http');
const path = require('node:path');
const path = require('node:path');
-const Database = require('better-sqlite3');
-const { parseDelivery, createInbox } = require('openvibe-sdk/events');
+const { createDb, sql } = require('openvibe-sdk/db');
+const { parseDelivery, createPgInbox, inboxSchema } = require('openvibe-sdk/events');
const MAX_BODY = 1024 * 1024;
const MAX_BODY = 1024 * 1024;
const CONSUMER = 'webhook-consumer';
const CONSUMER = 'webhook-consumer';
@@ -38,36 +42,41 @@ function loadConfig(env = process.env) {
@@ -38,36 +42,41 @@ function loadConfig(env = process.env) {
}
}
return {
return {
secrets: [env.OV_WEBHOOK_SECRET, env.OV_WEBHOOK_SECRET_PREVIOUS].filter(Boolean),
secrets: [env.OV_WEBHOOK_SECRET, env.OV_WEBHOOK_SECRET_PREVIOUS].filter(Boolean),
- dbPath: env.OV_DB_PATH || path.join(__dirname, 'data', 'webhook-consumer.db'),
+ databaseUrl: env.OV_DATABASE_URL || null,
+ dataDir: env.OV_DATA_DIR || path.join(__dirname, 'data', 'pg'),
port: Number(env.OV_PORT || 3003),
port: Number(env.OV_PORT || 3003),
path: env.OV_WEBHOOK_PATH || '/webhooks/openvibe',
path: env.OV_WEBHOOK_PATH || '/webhooks/openvibe',
};
};
}
}
-function openDatabase(dbPath) {
- if (dbPath !== ':memory:') fs.mkdirSync(path.dirname(dbPath), { recursive: true });
- const db = new Database(dbPath);
- db.pragma('journal_mode = WAL');
- db.exec(`CREATE TABLE IF NOT EXISTS received_events (
- event_id TEXT PRIMARY KEY,
- event_type TEXT NOT NULL,
- source TEXT NOT NULL,
- seq INTEGER,
- subject TEXT,
- received_at TEXT NOT NULL
+/** The database and its two tables: the inbox's receipts and the app's own row per event. */
+async function openDatabase(config) {
+ let db;
+ if (config.databaseUrl) db = createDb({ url: config.databaseUrl, service: 'webhook-consumer' });
+ else {
+ if (config.dataDir !== 'memory') fs.mkdirSync(config.dataDir, { recursive: true });
+ db = createDb({ pglite: config.dataDir === 'memory' ? true : config.dataDir, service: 'webhook-consumer' });
+ }
+ await db.query(inboxSchema());
+ await db.query(`CREATE TABLE IF NOT EXISTS received_events (
+ id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+ event_id text NOT NULL UNIQUE,
+ event_type text NOT NULL,
+ source text NOT NULL,
+ subject text,
+ received_at timestamptz NOT NULL DEFAULT now()
)`);
)`);
return db;
return db;
}
}
/**
/**
- * createConsumer(config, { db, handle, log })
- * handle(event, { seq, attempt }) runs inside the inbox transaction and must be synchronous
- * (better-sqlite3). Put side effects that need the network in your own outbox table instead.
+ * createConsumer(config, { db, handle, log }) db: from openDatabase(config)
+ * handle(event, { attempt }, t) runs inside the inbox transaction (t, the transaction handle: write with it and the
+ * write commits with the receipt). Put side effects that need the network in your own outbox table instead.
*/
*/
-function createConsumer(config, { db = openDatabase(config.dbPath), handle = () => {}, log = console } = {}) {
- const inbox = createInbox(db);
- inbox.ensureSchema();
- const record = db.prepare('INSERT INTO received_events (event_id, event_type, source, seq, subject, received_at) VALUES (?, ?, ?, ?, ?, ?)');
+function createConsumer(config, { db, handle = () => {}, log = console } = {}) {
+ if (!db) throw new TypeError('createConsumer: pass { db } from openDatabase(config)');
+ const inbox = createPgInbox(db);
function verify(raw, headers) {
function verify(raw, headers) {
for (const secret of config.secrets) {
for (const secret of config.secrets) {
@@ -77,20 +86,20 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
@@ -77,20 +86,20 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
return null;
return null;
}
}
- function receive(raw, headers) {
+ async function receive(raw, headers) {
const delivery = verify(raw, headers);
const delivery = verify(raw, headers);
if (!delivery) return { status: 401, body: { error: 'bad_signature' } };
if (!delivery) return { status: 401, body: { error: 'bad_signature' } };
- const { event, seq, attempt } = delivery;
+ const { event, attempt } = delivery;
if (!event || typeof event.event_id !== 'string' || typeof event.event_type !== 'string') return { status: 400, body: { error: 'bad_envelope' } };
if (!event || typeof event.event_id !== 'string' || typeof event.event_type !== 'string') return { status: 400, body: { error: 'bad_envelope' } };
// The header is informational; the signed body is the truth. They must agree.
// The header is informational; the signed body is the truth. They must agree.
const headerId = headers['x-openvibe-event-id'];
const headerId = headers['x-openvibe-event-id'];
if (headerId && headerId !== event.event_id) return { status: 400, body: { error: 'event_id_mismatch' } };
if (headerId && headerId !== event.event_id) return { status: 400, body: { error: 'event_id_mismatch' } };
- const out = inbox.once(CONSUMER, event.event_id, () => {
- record.run(event.event_id, event.event_type, String(event.source || ''), Number.isFinite(seq) ? seq : null,
- event.subject ? `${event.subject.type}:${event.subject.id}` : null, new Date().toISOString());
- return handle(event, { seq, attempt });
+ const out = await inbox.once(CONSUMER, event.event_id, async (t) => {
+ await t.exec(sql`INSERT INTO received_events (event_id, event_type, source, subject)
+ VALUES (${event.event_id}, ${event.event_type}, ${String(event.source || '')}, ${event.subject ? `${event.subject.type}:${event.subject.id}` : null})`);
+ return await handle(event, { attempt }, t);
});
});
- log.log(`[webhook] ${event.event_type} ${event.event_id} seq=${seq} attempt=${attempt}${out.duplicate ? ' (duplicate, skipped)' : ''}`);
+ log.log(`[webhook] ${event.event_type} ${event.event_id} attempt=${attempt}${out.duplicate ? ' (duplicate, skipped)' : ''}`);
return { status: 200, body: { ok: true, duplicate: out.duplicate } };
return { status: 200, body: { ok: true, duplicate: out.duplicate } };
}
}
@@ -110,10 +119,10 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
@@ -110,10 +119,10 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
if (size > MAX_BODY) { tooLarge = true; chunks.length = 0; return; }
if (size > MAX_BODY) { tooLarge = true; chunks.length = 0; return; }
chunks.push(c);
chunks.push(c);
});
});
- req.on('end', () => {
+ req.on('end', async () => {
if (tooLarge) return send(413, { error: 'too_large' });
if (tooLarge) return send(413, { error: 'too_large' });
try {
try {
- const out = receive(Buffer.concat(chunks), req.headers);
+ const out = await receive(Buffer.concat(chunks), req.headers);
send(out.status, out.body);
send(out.status, out.body);
} catch (err) {
} catch (err) {
// No receipt was written: Events retries this delivery with backoff.
// No receipt was written: Events retries this delivery with backoff.
@@ -129,12 +138,15 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
@@ -129,12 +138,15 @@ function createConsumer(config, { db = openDatabase(config.dbPath), handle = ()
if (require.main === module) {
if (require.main === module) {
let config;
let config;
try { config = loadConfig(); } catch (err) { console.error(err.message); process.exit(2); }
try { config = loadConfig(); } catch (err) { console.error(err.message); process.exit(2); }
- const { server } = createConsumer(config, {
- handle(event) {
- // Your side effect goes here (synchronous, same transaction as the receipt).
- },
- });
- server.listen(config.port, () => console.log(`webhook-consumer: POST http://localhost:${config.port}${config.path}`));
+ openDatabase(config).then((db) => {
+ const { server } = createConsumer(config, {
+ db,
+ async handle(event, meta, t) {
+ // Your side effect goes here: write with `t` and it commits with the receipt.
+ },
+ });
+ server.listen(config.port, () => console.log(`webhook-consumer: POST http://localhost:${config.port}${config.path}`));
+ }).catch((err) => { console.error(`webhook-consumer: ${err.message}`); process.exit(1); });
}
}
module.exports = { loadConfig, openDatabase, createConsumer, CONSUMER };
module.exports = { loadConfig, openDatabase, createConsumer, CONSUMER };
@@ -45,12 +45,13 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
@@ -45,12 +45,13 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
// 2. Run the consumer with that secret.
// 2. Run the consumer with that secret.
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-webhook-'));
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-webhook-'));
- const config = loadConfig({ OV_WEBHOOK_SECRET: created.secret, OV_DB_PATH: path.join(dir, 'consumer.db') });
+ const config = loadConfig({ OV_WEBHOOK_SECRET: created.secret, OV_DATA_DIR: path.join(dir, 'pg') });
+ const db = await openDatabase(config);
const handled = [];
const handled = [];
let failNext = false;
let failNext = false;
const logs = [];
const logs = [];
const consumer = createConsumer(config, {
const consumer = createConsumer(config, {
- db: openDatabase(config.dbPath),
+ db,
handle(event) {
handle(event) {
if (failNext) { failNext = false; throw new Error('downstream unavailable'); }
if (failNext) { failNext = false; throw new Error('downstream unavailable'); }
handled.push(event.event_id);
handled.push(event.event_id);
@@ -95,7 +96,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
@@ -95,7 +96,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
round = await platform.deliverEvents({ fetch: worker });
round = await platform.deliverEvents({ fetch: worker });
assert.equal(round.failed, 1);
assert.equal(round.failed, 1);
assert.equal(round.attempts[0].status, 500);
assert.equal(round.attempts[0].status, 500);
- assert.equal(consumer.inbox.seen('webhook-consumer', e2.event_id), false);
+ assert.equal(await consumer.inbox.seen('webhook-consumer', e2.event_id), false);
round = await platform.deliverEvents({ fetch: worker });
round = await platform.deliverEvents({ fetch: worker });
assert.deepEqual([round.delivered, round.attempts[0].attempt], [1, 2]);
assert.deepEqual([round.delivered, round.attempts[0].attempt], [1, 2]);
assert.deepEqual(handled, [e1.event_id, e2.event_id]);
assert.deepEqual(handled, [e1.event_id, e2.event_id]);
@@ -116,8 +117,8 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
@@ -116,8 +117,8 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
// Secret rotation: deliveries signed with the previous secret are accepted while it is configured.
// Secret rotation: deliveries signed with the previous secret are accepted while it is configured.
consumer.server.close();
consumer.server.close();
- const rotated = createConsumer(loadConfig({ OV_WEBHOOK_SECRET: 'whsec_new_secret_after_rotation_0000000000', OV_WEBHOOK_SECRET_PREVIOUS: created.secret, OV_DB_PATH: config.dbPath }), {
- db: consumer.db, handle: (event) => handled.push(event.event_id), log: { log() {}, error() {} },
+ const rotated = createConsumer(loadConfig({ OV_WEBHOOK_SECRET: 'whsec_new_secret_after_rotation_0000000000', OV_WEBHOOK_SECRET_PREVIOUS: created.secret, OV_DATA_DIR: config.dataDir }), {
+ db, handle: (event) => handled.push(event.event_id), log: { log() {}, error() {} },
});
});
await new Promise((res) => rotated.server.listen(0, '127.0.0.1', res));
await new Promise((res) => rotated.server.listen(0, '127.0.0.1', res));
local = `http://127.0.0.1:${rotated.server.address().port}/webhooks/openvibe`;
local = `http://127.0.0.1:${rotated.server.address().port}/webhooks/openvibe`;
@@ -130,7 +131,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
@@ -130,7 +131,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
assert.deepEqual(handled, [e1.event_id, e2.event_id, e3.event_id, e4.event_id]);
assert.deepEqual(handled, [e1.event_id, e2.event_id, e3.event_id, e4.event_id]);
// The receipt and the app's own row were written together.
// The receipt and the app's own row were written together.
- const rows = consumer.db.prepare('SELECT event_id FROM received_events ORDER BY seq').all().map((x) => x.event_id);
+ const rows = (await db.many('SELECT event_id FROM received_events ORDER BY id')).map((x) => x.event_id);
assert.deepEqual(rows, handled);
assert.deepEqual(rows, handled);
// Oversized bodies are refused before parsing.
// Oversized bodies are refused before parsing.
@@ -141,7 +142,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
@@ -141,7 +142,7 @@ const ENDPOINT = 'https://hooks.example.com/webhooks/openvibe';
assert.ok(!logs.join('\n').includes(created.secret));
assert.ok(!logs.join('\n').includes(created.secret));
rotated.server.close();
rotated.server.close();
- consumer.db.close();
+ await db.close();
fs.rmSync(dir, { recursive: true, force: true });
fs.rmSync(dir, { recursive: true, force: true });
console.log('webhook-consumer: ok');
console.log('webhook-consumer: ok');
})().catch((err) => { console.error(err); process.exit(1); });
})().catch((err) => { console.error(err); process.exit(1); });
@@ -112,16 +112,23 @@
@@ -112,16 +112,23 @@
},
},
"examples/webhook-consumer": {
"examples/webhook-consumer": {
"name": "openvibe-example-webhook-consumer",
"name": "openvibe-example-webhook-consumer",
- "version": "0.2.0",
+ "version": "0.3.0",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
- "better-sqlite3": "^11.10.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0"
+ "@electric-sql/pglite": "^0.5.8",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.43.0",
+ "pg": "^8.23.0"
},
},
"engines": {
"engines": {
"node": ">=22"
"node": ">=22"
}
}
},
},
+ "node_modules/@electric-sql/pglite": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.5.8.tgz",
+ "integrity": "sha512-n9tsbUOhwx2epK1V0ZG9Ar4SHWUju04dhmzZXiSBXwBoleOvIfals33NAaWgagQVAL4Rbvx/Ptsu3P+pA09f6Q==",
+ "license": "Apache-2.0"
+ },
"node_modules/ajv": {
"node_modules/ajv": {
"version": "8.20.0",
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
@@ -155,138 +162,6 @@
@@ -155,138 +162,6 @@
}
}
}
}
},
},
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/better-sqlite3": {
- "version": "11.10.0",
- "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
- "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "bindings": "^1.5.0",
- "prebuild-install": "^7.1.1"
- }
- },
- "node_modules/bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
- "license": "MIT",
- "dependencies": {
- "file-uri-to-path": "1.0.0"
- }
- },
- "node_modules/bl": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
- "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
- "license": "MIT",
- "dependencies": {
- "buffer": "^5.5.0",
- "inherits": "^2.0.4",
- "readable-stream": "^3.4.0"
- }
- },
- "node_modules/buffer": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
- "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.1.13"
- }
- },
- "node_modules/chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "license": "ISC"
- },
- "node_modules/decompress-response": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
- "license": "MIT",
- "dependencies": {
- "mimic-response": "^3.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/detect-libc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/end-of-stream": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
- "license": "MIT",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/expand-template": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
- "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
- "license": "(MIT OR WTFPL)",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/fast-deep-equal": {
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -309,116 +184,12 @@
@@ -309,116 +184,12 @@
],
],
"license": "BSD-3-Clause"
"license": "BSD-3-Clause"
},
},
- "node_modules/file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "license": "MIT"
- },
- "node_modules/fs-constants": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
- "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
- "license": "MIT"
- },
- "node_modules/github-from-package": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
- "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
- "license": "MIT"
- },
- "node_modules/ieee754": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
- "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "BSD-3-Clause"
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "license": "ISC"
- },
- "node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "license": "ISC"
- },
"node_modules/json-schema-traverse": {
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
"license": "MIT"
},
},
- "node_modules/mimic-response": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/mkdirp-classic": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
- "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
- "license": "MIT"
- },
- "node_modules/napi-build-utils": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
- "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
- "license": "MIT"
- },
- "node_modules/node-abi": {
- "version": "3.96.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.96.0.tgz",
- "integrity": "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==",
- "license": "MIT",
- "dependencies": {
- "semver": "^7.3.5"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
"version": "0.129.0",
"version": "0.129.0",
"resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
"resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.129.0",
@@ -500,227 +271,151 @@
@@ -500,227 +271,151 @@
}
}
}
}
},
},
- "node_modules/prebuild-install": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
- "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
- "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
+ "node_modules/pg": {
+ "version": "8.23.1",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz",
+ "integrity": "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
- "detect-libc": "^2.0.0",
- "expand-template": "^2.0.3",
- "github-from-package": "0.0.0",
- "minimist": "^1.2.3",
- "mkdirp-classic": "^0.5.3",
- "napi-build-utils": "^2.0.0",
- "node-abi": "^3.3.0",
- "pump": "^3.0.0",
- "rc": "^1.2.7",
- "simple-get": "^4.0.0",
- "tar-fs": "^2.0.0",
- "tunnel-agent": "^0.6.0"
- },
- "bin": {
- "prebuild-install": "bin.js"
+ "pg-connection-string": "^2.14.1",
+ "pg-pool": "^3.14.0",
+ "pg-protocol": "^1.16.1",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
},
},
"engines": {
"engines": {
- "node": ">=10"
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.4.1"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
}
}
},
},
- "node_modules/pump": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
- "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "node_modules/pg-cloudflare": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.1.tgz",
+ "integrity": "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ==",
"license": "MIT",
"license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
+ "optional": true
},
},
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
+ "node_modules/pg-connection-string": {
+ "version": "2.14.1",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz",
+ "integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==",
+ "license": "MIT"
},
},
- "node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
"engines": {
"engines": {
- "node": ">= 6"
+ "node": ">=4.0.0"
}
}
},
},
- "node_modules/require-from-string": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "node_modules/pg-pool": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
"license": "MIT",
"license": "MIT",
- "engines": {
- "node": ">=0.10.0"
+ "peerDependencies": {
+ "pg": ">=8.0"
}
}
},
},
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
+ "node_modules/pg-protocol": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.1.tgz",
+ "integrity": "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg==",
"license": "MIT"
"license": "MIT"
},
},
- "node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
},
},
"engines": {
"engines": {
- "node": ">=10"
+ "node": ">=4"
}
}
},
},
- "node_modules/simple-concat": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/simple-get": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
- "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
- "decompress-response": "^6.0.0",
- "once": "^1.3.1",
- "simple-concat": "^1.0.0"
+ "split2": "^4.1.0"
}
}
},
},
- "node_modules/string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"license": "MIT",
"license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.2.0"
+ "engines": {
+ "node": ">=4"
}
}
},
},
- "node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "node_modules/postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
"license": "MIT",
"license": "MIT",
"engines": {
"engines": {
"node": ">=0.10.0"
"node": ">=0.10.0"
}
}
},
},
- "node_modules/tar-fs": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",
- "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"license": "MIT",
"license": "MIT",
- "dependencies": {
- "chownr": "^1.1.1",
- "mkdirp-classic": "^0.5.2",
- "pump": "^3.0.0",
- "tar-stream": "^2.1.4"
+ "engines": {
+ "node": ">=0.10.0"
}
}
},
},
- "node_modules/tar-stream": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
- "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
- "bl": "^4.0.3",
- "end-of-stream": "^1.4.1",
- "fs-constants": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^3.1.1"
+ "xtend": "^4.0.0"
},
},
"engines": {
"engines": {
- "node": ">=6"
+ "node": ">=0.10.0"
}
}
},
},
- "node_modules/tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
- "license": "Apache-2.0",
- "dependencies": {
- "safe-buffer": "^5.0.1"
- },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
"engines": {
"engines": {
- "node": "*"
+ "node": ">=0.10.0"
}
}
},
},
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "license": "MIT"
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "license": "ISC"
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
},
},
"node_modules/ws": {
"node_modules/ws": {
"version": "8.21.3",
"version": "8.21.3",
@@ -743,6 +438,15 @@
@@ -743,6 +438,15 @@
"optional": true
"optional": true
}
}
}
}
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
}
}
}
}
}
}