@feltdb/core 0.4.13 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,10 +4,10 @@
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
6
  import http from 'http';
7
+ import net from 'net';
7
8
  import { createRequire } from 'module';
8
9
  import { spawn, spawnSync } from 'child_process';
9
10
  import { createFeltDB, diffFlowSpec, formatFlowSpec, parseFlowSpec, planFlowSpecMigration, validateFlowSpec } from '@feltdb/core';
10
- const RELEASE_VERSION = '0.4.13';
11
11
  function loadProjectEnvironment(file = path.resolve('.env.local')) {
12
12
  if (!fs.existsSync(file))
13
13
  return;
@@ -24,6 +24,19 @@ function loadProjectEnvironment(file = path.resolve('.env.local')) {
24
24
  process.env[key] = value;
25
25
  }
26
26
  }
27
+ async function availablePort(preferred, host = '127.0.0.1') {
28
+ for (let port = preferred; port < preferred + 100; port += 1) {
29
+ const available = await new Promise(resolve => {
30
+ const server = net.createServer();
31
+ server.unref();
32
+ server.once('error', () => resolve(false));
33
+ server.listen(port, host, () => server.close(() => resolve(true)));
34
+ });
35
+ if (available)
36
+ return port;
37
+ }
38
+ throw new Error(`No available port found between ${preferred} and ${preferred + 99}`);
39
+ }
27
40
  export async function handleCommand(command, args) {
28
41
  switch (command) {
29
42
  case 'studio':
@@ -387,35 +400,36 @@ async function handleDev(args) {
387
400
  const runtimeNamespace = config.runtime === 'managed'
388
401
  ? process.env.VITE_FELTDB_MANAGED_NAMESPACE || config.namespace
389
402
  : config.namespace;
390
- const appPort = args.includes('--port') ? args[args.indexOf('--port') + 1] || '5173' : '5173';
391
- const studioPort = args.includes('--studio-port') ? args[args.indexOf('--studio-port') + 1] || '3000' : '3000';
392
- let selfHosted;
403
+ const requestedAppPort = Number(args.includes('--port') ? args[args.indexOf('--port') + 1] || '5173' : '5173');
404
+ const requestedStudioPort = Number(args.includes('--studio-port') ? args[args.indexOf('--studio-port') + 1] || '3000' : '3000');
405
+ const appPort = String(await availablePort(requestedAppPort));
406
+ const studioPort = String(await availablePort(requestedStudioPort));
407
+ if (appPort !== String(requestedAppPort))
408
+ console.log(`Port ${requestedAppPort} is busy; using ${appPort} for the application.`);
409
+ if (studioPort !== String(requestedStudioPort))
410
+ console.log(`Port ${requestedStudioPort} is busy; using ${studioPort} for Studio.`);
411
+ process.env.APP_PORT = appPort;
412
+ process.env.STUDIO_PORT = studioPort;
413
+ let selfHostedStarted = false;
393
414
  const stopSelfHosted = () => {
394
- if (selfHosted && !selfHosted.killed)
395
- selfHosted.kill('SIGTERM');
415
+ if (selfHostedStarted) {
416
+ spawnSync('docker', ['compose', 'stop', 'feltdb'], { cwd: process.cwd(), stdio: 'ignore' });
417
+ selfHostedStarted = false;
418
+ }
396
419
  };
397
420
  if (config.runtime === 'self-hosted') {
398
421
  const docker = spawnSync('docker', ['version'], { stdio: 'ignore' });
399
422
  if (docker.status !== 0) {
400
423
  throw new Error('Self-hosted development requires Docker Desktop or Docker Engine. Install Docker, start it, and rerun npm run dev.');
401
424
  }
402
- const containerName = `feltdb-${String(config.namespace || 'app').replace(/[^a-z0-9_.-]/gi, '-').toLowerCase()}`;
403
- const image = process.env.FELTDB_IMAGE || `ghcr.io/rkendel1/feltdb:${RELEASE_VERSION}`;
404
- const existing = spawnSync('docker', ['rm', '-f', containerName], { stdio: 'ignore' });
405
- void existing;
406
- console.log(`Starting self-hosted FeltDB server from ${image}...`);
407
- selfHosted = spawn('docker', [
408
- 'run', '--rm', '--name', containerName,
409
- '-p', '7700:8080',
410
- '-v', `${containerName}-data:/data`,
411
- image,
412
- '--allow-origin', `http://127.0.0.1:${appPort}`,
413
- '--allow-origin', `http://127.0.0.1:${studioPort}`,
414
- ], { stdio: 'inherit' });
415
- selfHosted.once('exit', code => {
416
- if (code && code !== 0)
417
- console.error(`Self-hosted FeltDB server exited with status ${code}`);
418
- });
425
+ if (!fs.existsSync(path.join(process.cwd(), 'docker-compose.yml'))) {
426
+ throw new Error('Self-hosted project is missing docker-compose.yml. Regenerate the project with create-feltdb.');
427
+ }
428
+ console.log('Building and starting the generated self-hosted FeltDB service...');
429
+ const compose = spawnSync('docker', ['compose', 'up', '--build', '--detach', 'feltdb'], { cwd: process.cwd(), stdio: 'inherit' });
430
+ if (compose.status !== 0)
431
+ throw new Error(`docker compose failed with status ${compose.status ?? 'unknown'}`);
432
+ selfHostedStarted = true;
419
433
  (_c = process.env).VITE_FELTDB_URL || (_c.VITE_FELTDB_URL = 'http://127.0.0.1:7700');
420
434
  }
421
435
  console.log('FeltDB Dev Server');
@@ -426,7 +440,7 @@ async function handleDev(args) {
426
440
  const open = !args.includes('--no-open');
427
441
  console.log(`Application: http://127.0.0.1:${appPort}`);
428
442
  console.log(`Studio: http://127.0.0.1:${studioPort}\n`);
429
- const viteArgs = ['--host', '127.0.0.1', '--port', appPort, ...(open ? ['--open'] : [])];
443
+ const viteArgs = ['--host', '127.0.0.1', '--port', appPort, '--strictPort', ...(open ? ['--open'] : [])];
430
444
  const vite = runLocalVite(viteArgs, false);
431
445
  let shuttingDown = false;
432
446
  const stopVite = () => { if (!vite.killed)
package/dist/cli/index.js CHANGED
@@ -23,7 +23,7 @@ import * as path from 'path';
23
23
  import * as readline from 'readline';
24
24
  import { getClient } from './api-client.js';
25
25
  import { loadFeltDBConfig, createDefaultConfig, validateModel, } from './config.js';
26
- const VERSION = '0.4.13';
26
+ const VERSION = '0.4.15';
27
27
  function prompt(question) {
28
28
  const rl = readline.createInterface({
29
29
  input: process.stdin,
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAIxC,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,kBAAkB,CAAC;AAEhE,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;AAChD,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;AAEjD;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,EAAE,CAAO;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAA8B;IAC1D,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,aAAa,CAAS;gBAElB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAmBpF;;OAEG;YACW,oBAAoB;IAalC;;;OAGG;IACG,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAKzB,0DAA0D;IACpD,IAAI,CAAC,KAAK,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAQhD;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAWtC;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE;IAI5B;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrC;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAazC;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAajD;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAO7C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BrE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAK9B;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI;IA6CxE,mEAAmE;IACnE,KAAK,IAAI,IAAI;IASb;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC/B;AAED;;GAEG;AACH,qBAAa,YAAY,CAAC,MAAM,EAAE,KAAK;IACrC,OAAO,CAAC,QAAQ,CAAO;IACvB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAoC;gBAGpD,QAAQ,EAAE,IAAI,EACd,OAAO,EAAE,IAAI,EACb,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,MAAM;IAQ/C;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;CAoBxD"}
@@ -258,10 +258,16 @@ export class Collection {
258
258
  }
259
259
  // Subscribe to changes via reactive dependency graph
260
260
  if (!this.graphUnsubscribe) {
261
- const notify = async () => {
262
- await this.refresh();
263
- for (const sub of this.subscribers)
264
- sub([...this.cache]);
261
+ let refreshInFlight = null;
262
+ const notify = () => {
263
+ if (refreshInFlight)
264
+ return refreshInFlight;
265
+ refreshInFlight = (async () => {
266
+ await this.refresh();
267
+ for (const sub of this.subscribers)
268
+ sub([...this.cache]);
269
+ })().finally(() => { refreshInFlight = null; });
270
+ return refreshInFlight;
265
271
  };
266
272
  const graph = getReactiveDependencyGraph();
267
273
  this.graphUnsubscribe = graph.subscribe(this.name, notify);
@@ -269,7 +275,7 @@ export class Collection {
269
275
  this.runtimeUnsubscribe = this.db.subscribe_changes(collection => {
270
276
  if (collection === this.name)
271
277
  void notify();
272
- });
278
+ }, this.name);
273
279
  }
274
280
  }
275
281
  // Return unsubscribe function
@@ -73,6 +73,14 @@ async function prompt(message) {
73
73
  const interface_ = readline.createInterface({ input: process.stdin, output: process.stdout });
74
74
  return new Promise(resolve => interface_.question(message, value => { interface_.close(); resolve(value.trim()); }));
75
75
  }
76
+ async function promptForProjectName(defaultName = 'feltdb-app') {
77
+ while (true) {
78
+ const value = (await prompt(`What should we name your FeltDB app? (${defaultName}): `)) || defaultName;
79
+ if (/^[a-z0-9][a-z0-9-]*$/.test(value))
80
+ return value;
81
+ console.log('Use lowercase letters, numbers, and dashes (for example: customer-portal).');
82
+ }
83
+ }
76
84
  async function promptSecret(message) {
77
85
  if (!process.stdin.isTTY || !process.stdout.isTTY)
78
86
  return '';
@@ -207,7 +215,7 @@ Deployment Target (Explicit):
207
215
  Requires production FeltDB instance or self-host
208
216
 
209
217
  --self-host Dedicated FeltDB server with Docker Compose
210
- Best for: Production deployments, multi-user systems
218
+ Best for: Staging first, then explicit production promotion
211
219
  Generates Docker Compose with persistent /data volume
212
220
 
213
221
  --managed Managed FeltDB runtime
@@ -245,10 +253,12 @@ Learn more: https://github.com/rkendel1/feltdb`);
245
253
  }
246
254
  // Find project name (first non-flag argument)
247
255
  let projectName = 'feltdb-app';
256
+ let projectNameProvided = false;
248
257
  for (let i = 0; i < args.length; i++) {
249
258
  const arg = args[i];
250
259
  if (!arg.startsWith('-')) {
251
260
  projectName = arg;
261
+ projectNameProvided = true;
252
262
  break;
253
263
  }
254
264
  // Skip next arg if current arg is a flag that takes a value
@@ -260,10 +270,16 @@ Learn more: https://github.com/rkendel1/feltdb`);
260
270
  const shouldInstall = !args.includes('--no-install');
261
271
  const shouldStart = !args.includes('--no-start');
262
272
  let options = parseArgs(args);
273
+ if (!projectNameProvided && !shouldAutoYes && process.stdin.isTTY && process.stdout.isTTY) {
274
+ projectName = await promptForProjectName(projectName);
275
+ }
276
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(projectName)) {
277
+ throw new Error('Application names must use lowercase letters, numbers, and dashes.');
278
+ }
263
279
  const runtimeDescriptions = {
264
280
  'browser': 'Browser with IndexedDB (local-first, no server)',
265
281
  'node': 'Node.js server (server-side authority)',
266
- 'self-hosted': 'Self-hosted with Docker Compose (production)',
282
+ 'self-hosted': 'Self-hosted with Docker Compose (staging)',
267
283
  'managed': 'Managed FeltDB service (hosted persistence, sync, and workloads)',
268
284
  };
269
285
  if (!runtimeDescriptions[options.runtime]) {
@@ -286,6 +302,7 @@ Learn more: https://github.com/rkendel1/feltdb`);
286
302
  console.log(`Capabilities: ${options.capabilities}`);
287
303
  console.log('\n═══════════════════════════════════════════════\n');
288
304
  const projectDir = path.resolve(process.cwd(), projectName);
305
+ let projectCreated = false;
289
306
  try {
290
307
  await createProject({
291
308
  projectName,
@@ -298,6 +315,7 @@ Learn more: https://github.com/rkendel1/feltdb`);
298
315
  webllm: options.webllm,
299
316
  capabilities: options.capabilities,
300
317
  });
318
+ projectCreated = true;
301
319
  if (options.runtime === 'managed') {
302
320
  console.log('\n☁️ Setting up your managed FeltDB account...\n');
303
321
  const email = process.env.FELTDB_MANAGED_EMAIL || (shouldAutoYes ? '' : await prompt('Email: '));
@@ -341,7 +359,9 @@ Learn more: https://github.com/rkendel1/feltdb`);
341
359
  }
342
360
  }
343
361
  catch (error) {
344
- console.error('❌ Failed to create project:', error);
362
+ console.error(projectCreated
363
+ ? `❌ The project was created at ${projectDir}, but setup or startup failed:`
364
+ : '❌ Failed to create project:', error);
345
365
  process.exit(1);
346
366
  }
347
367
  }
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
- import { fileURLToPath } from 'url';
7
6
  import { randomBytes } from 'crypto';
8
7
  import { feltdbPackageRange } from './package-versions.js';
9
8
  import { generateDockerCompose, generateDockerfile, generateDockerIgnore, generateDotEnvLocal, generateFeltDBDockerfile, generateStudioDockerfile, } from './docker-compose-generator.js';
@@ -1218,7 +1217,8 @@ VITE_FELTDB_MANAGED_NAMESPACE=
1218
1217
  VITE_FELTDB_MANAGED_ENVIRONMENT=production
1219
1218
 
1220
1219
  # Override the default self-hosted container image
1221
- # Example: ghcr.io/rkendel1/feltdb:latest
1220
+ # By default the server image is built locally from the bundled source.
1221
+ # Example override: your-registry.example/feltdb:staging
1222
1222
  FELTDB_IMAGE=
1223
1223
 
1224
1224
  # Node environment
@@ -1239,11 +1239,8 @@ NODE_ENV=development
1239
1239
  fs.writeFileSync(path.join(projectDir, 'Dockerfile'), generateDockerfile(dockerConfig, framework));
1240
1240
  fs.writeFileSync(path.join(projectDir, 'Dockerfile.feltdb'), generateFeltDBDockerfile(dockerConfig));
1241
1241
  fs.writeFileSync(path.join(projectDir, 'Dockerfile.studio'), generateStudioDockerfile(dockerConfig));
1242
- const serverSource = fileURLToPath(new URL('./server-source', import.meta.url));
1243
- fs.cpSync(serverSource, path.join(projectDir, '.feltdb', 'server-source'), { recursive: true });
1244
1242
  fs.writeFileSync(path.join(projectDir, '.dockerignore'), generateDockerIgnore());
1245
1243
  const dockerEnvironment = generateDotEnvLocal(dockerConfig, randomBytes(32).toString('hex'));
1246
- fs.writeFileSync(path.join(projectDir, '.env.docker'), dockerEnvironment);
1247
1244
  // Docker Compose loads .env automatically; it is already excluded from git.
1248
1245
  fs.writeFileSync(path.join(projectDir, '.env'), dockerEnvironment);
1249
1246
  }
@@ -1497,7 +1494,8 @@ Verify vector database connection and that embeddings are enabled in capabilitie
1497
1494
  - Check out example projects in the templates
1498
1495
  - Join the community Discord for support
1499
1496
  `;
1500
- fs.writeFileSync(path.join(projectDir, 'RUNTIME_GUIDE.md'), runtimeGuide);
1497
+ // Keep generated projects focused; the runtime guide lives in FeltDB's docs.
1498
+ void runtimeGuide;
1501
1499
  // Create .gitignore
1502
1500
  const gitignore = `node_modules/
1503
1501
  dist/
@@ -14,16 +14,33 @@ export function generateDockerCompose(config) {
14
14
  build:
15
15
  context: .
16
16
  dockerfile: Dockerfile.feltdb
17
+ args:
18
+ FELTDB_VERSION: ${config.version}
17
19
  container_name: \${COMPOSE_PROJECT_NAME}-feltdb
18
20
  environment:
19
21
  FELTDB_APP_NAME: \${FELTDB_APP_NAME:-${config.appName}}
20
22
  FELTDB_APP_ID: \${FELTDB_APP_ID:-${config.appId}}
21
23
  FELTDB_VERSION: \${FELTDB_VERSION:-${config.version}}
24
+ FELTDB_ENVIRONMENT: \${FELTDB_ENVIRONMENT:-staging}
22
25
  FELTDB_PEERS: \${FELTDB_PEERS:-}
23
26
  FELTDB_MASTER_KEY: \${FELTDB_MASTER_KEY}
24
27
  NODE_ENV: \${NODE_ENV:-production}
25
28
  ports:
26
29
  - "\${FELTDB_API_PORT:-${config.port}}:7700"
30
+ command:
31
+ - feltdb-server
32
+ - --host
33
+ - 0.0.0.0
34
+ - --port
35
+ - "7700"
36
+ - --namespace
37
+ - \${FELTDB_APP_ID:-${config.appId}}
38
+ - --data
39
+ - /data/feltdb.log
40
+ - --allow-origin
41
+ - http://127.0.0.1:\${APP_PORT:-5173}
42
+ - --allow-origin
43
+ - http://127.0.0.1:\${STUDIO_PORT:-${config.studioPort}}
27
44
  volumes:
28
45
  - feltdb_data:/data
29
46
  - ./feltdb.flow:/app/feltdb.flow:ro
@@ -106,11 +123,13 @@ volumes:
106
123
  `;
107
124
  }
108
125
  export function generateFeltDBDockerfile(config) {
109
- return `# FeltDB server source is bundled with create-feltdb for a reproducible build.
126
+ return `# Build the matching FeltDB server without adding engine source to the application.
110
127
  FROM rust:1.96-alpine AS builder
111
- RUN apk add --no-cache pkgconf openssl-dev musl-dev
128
+ RUN apk add --no-cache nodejs npm pkgconf openssl-dev musl-dev
112
129
  WORKDIR /source
113
- COPY .feltdb/server-source/ ./
130
+ ARG FELTDB_VERSION=${config.version}
131
+ RUN npm install --global "create-feltdb@\${FELTDB_VERSION}" \
132
+ && cp -R "\$(npm root --global)/create-feltdb/dist/server-source/." /source/
114
133
  RUN cargo build -p feltdb-server --release --locked
115
134
 
116
135
  FROM alpine:3.22
@@ -191,6 +210,7 @@ COMPOSE_PROJECT_NAME=${config.appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}
191
210
  FELTDB_APP_NAME=${config.appName}
192
211
  FELTDB_APP_ID=${config.appId}
193
212
  FELTDB_VERSION=${config.version}
213
+ FELTDB_ENVIRONMENT=staging
194
214
  FELTDB_API_PORT=${config.port}
195
215
  FELTDB_REPLICATION_PORT=${config.replicationPort}
196
216
  FELTDB_PEERS=
@@ -203,8 +223,6 @@ FELTDB_API_URL=http://feltdb:7700
203
223
  # Studio Configuration
204
224
  STUDIO_PORT=${config.studioPort}
205
225
 
206
- # Runtime
207
- NODE_ENV=production
208
226
  `;
209
227
  }
210
228
  export function generateHealthCheckScript() {
@@ -1,4 +1,4 @@
1
1
  // One release train keeps generated applications installable. The repository
2
2
  // validation script checks these values against every workspace manifest.
3
- export const FELTDB_PACKAGE_VERSION = '0.4.13';
3
+ export const FELTDB_PACKAGE_VERSION = '0.4.15';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
package/dist/feltdb.d.ts CHANGED
@@ -52,7 +52,7 @@ export interface JsDb {
52
52
  /** Delete a record when supported by the runtime. */
53
53
  delete?(key: string): any | Promise<any>;
54
54
  /** Subscribe to runtime-originated changes, including remote mutations. */
55
- subscribe_changes?(callback: (collection: string) => void): () => void;
55
+ subscribe_changes?(callback: (collection: string) => void, collection?: string): () => void;
56
56
  close?(): void | Promise<void>;
57
57
  /**
58
58
  * Get capability records
@@ -1 +1 @@
1
- {"version":3,"file":"feltdb.d.ts","sourceRoot":"","sources":["../src/feltdb.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,CAAC;IAEhC;;OAEG;IACH,SAAS,IAAI,GAAG,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAE/B;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAEpC;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAEvC;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnE;;OAEG;IACH,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;IAEpE;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEzC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEvD,6DAA6D;IAC7D,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,qDAAqD;IACrD,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEzC,2EAA2E;IAC3E,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC;IAEhD;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,IAAI,MAAM,CAAC;IAEvB,0EAA0E;IAC1E,YAAY,CAAC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEpC,6DAA6D;IAC7D,iBAAiB,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/D,uBAAuB,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACjE;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB"}
1
+ {"version":3,"file":"feltdb.d.ts","sourceRoot":"","sources":["../src/feltdb.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,CAAC;IAEhC;;OAEG;IACH,SAAS,IAAI,GAAG,CAAC;IAEjB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAE/B;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAEpC;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IAEvC;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnE;;OAEG;IACH,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;IAEpE;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEzC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEvD,6DAA6D;IAC7D,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,qDAAqD;IACrD,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEzC,2EAA2E;IAC3E,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;IAC5F,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC;IAEhD;;OAEG;IACH,WAAW,IAAI,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,IAAI,MAAM,CAAC;IAEvB,0EAA0E;IAC1E,YAAY,CAAC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAEpC,6DAA6D;IAC7D,iBAAiB,CAAC,CAAC,cAAc,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/D,uBAAuB,CAAC,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACjE;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB"}
@@ -14,6 +14,7 @@ interface ChangeRecord {
14
14
  origin: string;
15
15
  value?: unknown;
16
16
  }
17
+ type ChangeListener = (collection: string) => void;
17
18
  /** Durable browser runtime. Every mutation resolves only after its IndexedDB transaction commits. */
18
19
  export declare class IndexedDbJsDb implements JsDb {
19
20
  private readonly database;
@@ -21,6 +22,15 @@ export declare class IndexedDbJsDb implements JsDb {
21
22
  private sequence;
22
23
  private readonly origin;
23
24
  private readonly peers;
25
+ private readonly changeListeners;
26
+ private changeCursor;
27
+ private changePollGeneration;
28
+ private changePollRunning;
29
+ private changePollTimer?;
30
+ private changeChannelListening;
31
+ private changeCoordinatorReady;
32
+ private pendingChangeAnnouncements;
33
+ private readonly announcedChangeSequences;
24
34
  constructor(namespace: string);
25
35
  private open;
26
36
  private splitKey;
@@ -31,7 +41,16 @@ export declare class IndexedDbJsDb implements JsDb {
31
41
  get(key: string): Promise<JsResult>;
32
42
  query(collection: string): Promise<JsResult>;
33
43
  private request;
34
- subscribe_changes(callback: (collection: string) => void): () => void;
44
+ subscribe_changes(callback: ChangeListener, collection?: string): () => void;
45
+ private readonly receiveChangeMessage;
46
+ private deliverChangeAnnouncement;
47
+ private announceLocalChange;
48
+ private rememberAnnouncedSequence;
49
+ private notifyChangeListeners;
50
+ private startChangeCoordinator;
51
+ private latestChangeSequence;
52
+ private pollChanges;
53
+ private stopChangeCoordinator;
35
54
  close(): void;
36
55
  get_capability_records(capability: string): Promise<JsResult>;
37
56
  execute_op(): JsResult;
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb-db.d.ts","sourceRoot":"","sources":["../src/indexeddb-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,UAAU,QAAQ;IAAG,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AACtE,UAAU,YAAY;IAAG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE;AAErK,qGAAqG;AACrG,qBAAa,aAAc,YAAW,IAAI;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;gBAE/B,SAAS,EAAE,MAAM;IAO7B,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,QAAQ;YAMF,MAAM;IA2BpB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IACrD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IACrD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAEhC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKnC,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAapC,OAAO;IAKrB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAuBrE,KAAK,IAAI,IAAI;IACb,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC7D,UAAU,IAAI,QAAQ;IACtB,SAAS,IAAI,QAAQ;IACrB,QAAQ,IAAI,QAAQ;IACpB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IACvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAC1C,oBAAoB,IAAI,QAAQ;IAChC,2BAA2B,IAAI,QAAQ;IACvC,WAAW,IAAI,MAAM;IACrB,YAAY,IAAI,MAAM;IAChB,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IACvC,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IACjE,uBAAuB,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAqBzG"}
1
+ {"version":3,"file":"indexeddb-db.d.ts","sourceRoot":"","sources":["../src/indexeddb-db.ts"],"names":[],"mappings":"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"}