@camstack/system 1.2.318 → 1.2.319

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.
@@ -3168,7 +3168,6 @@ var SystemBackupService = class {
3168
3168
  }
3169
3169
  if (present.length === 0) throw new Error(`createArchive: no locations from {${requested.join(", ")}} exist under ${this.dataDir}`);
3170
3170
  node_fs.mkdirSync(node_path.dirname(opts.archivePath), { recursive: true });
3171
- await this.checkpointDatabasesUnder(present);
3172
3171
  const entries = [];
3173
3172
  for (const loc of present) {
3174
3173
  throwIfCancelled(opts.signal);
@@ -3241,48 +3240,6 @@ var SystemBackupService = class {
3241
3240
  };
3242
3241
  }
3243
3242
  /**
3244
- * Best-effort `wal_checkpoint(TRUNCATE)` on every live SQLite database
3245
- * under the locations about to be archived. Discovers a DB by its
3246
- * `-wal` sidecar (only WAL-mode DBs have one, and only those need
3247
- * folding), opens a short-lived second connection — SQLite permits
3248
- * concurrent connections, and a checkpoint is a supported concurrent
3249
- * operation against the hub's live writer — checkpoints, and closes.
3250
- *
3251
- * Every failure is swallowed: a `SQLITE_BUSY`, a locked file, a
3252
- * missing native binding, or a non-DB `.db` file must not fail the
3253
- * backup. A residual WAL still restores correctly; the checkpoint is a
3254
- * consistency *improvement*, not a precondition.
3255
- */
3256
- async checkpointDatabasesUnder(locations) {
3257
- const dbPaths = /* @__PURE__ */ new Set();
3258
- for (const loc of locations) await collectWalDatabases(node_path.isAbsolute(loc) ? loc : node_path.join(this.dataDir, loc), dbPaths, this.exclusionPredicate());
3259
- if (dbPaths.size === 0) return;
3260
- let Database;
3261
- try {
3262
- Database = (await import("better-sqlite3")).default;
3263
- } catch (err) {
3264
- this.logger.warn("createArchive: better-sqlite3 unavailable — skipping WAL checkpoint", { meta: {
3265
- error: err instanceof Error ? err.message : String(err),
3266
- dbCount: dbPaths.size
3267
- } });
3268
- return;
3269
- }
3270
- for (const dbPath of dbPaths) try {
3271
- const db = new Database(dbPath);
3272
- try {
3273
- db.pragma("wal_checkpoint(PASSIVE)");
3274
- } finally {
3275
- db.close();
3276
- }
3277
- this.logger.debug("createArchive: checkpointed DB", { meta: { dbPath } });
3278
- } catch (err) {
3279
- this.logger.warn("createArchive: WAL checkpoint failed — archiving DB as-is", { meta: {
3280
- dbPath,
3281
- error: err instanceof Error ? err.message : String(err)
3282
- } });
3283
- }
3284
- }
3285
- /**
3286
3243
  * without extracting payload files. Streams the tar.gz, parses the
3287
3244
  * one entry we care about, then aborts.
3288
3245
  */
@@ -3402,45 +3359,14 @@ var SystemBackupService = class {
3402
3359
  }
3403
3360
  /**
3404
3361
  * Absolute-path form of {@link isExcludedFromBackup}, anchored on
3405
- * this service's dataDir. Shared by the manifest walk, the location
3406
- * stats and the WAL-checkpoint scan so all three agree with the tar
3407
- * filter about what a backup contains.
3362
+ * this service's dataDir. Shared by the manifest walk and the location
3363
+ * stats so both agree with the tar filter about what a backup contains.
3408
3364
  */
3409
3365
  exclusionPredicate() {
3410
3366
  return (abs) => isExcludedFromBackup(node_path.relative(this.dataDir, abs));
3411
3367
  }
3412
3368
  };
3413
3369
  /**
3414
- * Recursively collect every SQLite database under `abs` that is in WAL
3415
- * mode, identified by the presence of a `<db>-wal` sidecar. Only those
3416
- * need a checkpoint; a rollback-journal DB has nothing to fold. Errors
3417
- * (unreadable dir, broken symlink) are skipped silently — this feeds a
3418
- * best-effort step.
3419
- */
3420
- async function collectWalDatabases(abs, out, shouldSkip) {
3421
- if (shouldSkip?.(abs)) return;
3422
- let stat;
3423
- try {
3424
- stat = await node_fs_promises.stat(abs);
3425
- } catch {
3426
- return;
3427
- }
3428
- if (stat.isDirectory()) {
3429
- let children;
3430
- try {
3431
- children = await node_fs_promises.readdir(abs);
3432
- } catch {
3433
- return;
3434
- }
3435
- for (const child of children) await collectWalDatabases(node_path.join(abs, child), out, shouldSkip);
3436
- return;
3437
- }
3438
- if (abs.endsWith("-wal")) {
3439
- const dbPath = abs.slice(0, -4);
3440
- if (node_fs.existsSync(dbPath)) out.add(dbPath);
3441
- }
3442
- }
3443
- /**
3444
3370
  * Recursively walk a directory and produce one `ArchiveEntry` per
3445
3371
  * filesystem node. `dataDir` is used to compute the archive-relative
3446
3372
  * path. Symlinks are dereferenced (matches the `follow: true` flag we
@@ -3153,7 +3153,6 @@ var SystemBackupService = class {
3153
3153
  }
3154
3154
  if (present.length === 0) throw new Error(`createArchive: no locations from {${requested.join(", ")}} exist under ${this.dataDir}`);
3155
3155
  fs.mkdirSync(path$1.dirname(opts.archivePath), { recursive: true });
3156
- await this.checkpointDatabasesUnder(present);
3157
3156
  const entries = [];
3158
3157
  for (const loc of present) {
3159
3158
  throwIfCancelled(opts.signal);
@@ -3226,48 +3225,6 @@ var SystemBackupService = class {
3226
3225
  };
3227
3226
  }
3228
3227
  /**
3229
- * Best-effort `wal_checkpoint(TRUNCATE)` on every live SQLite database
3230
- * under the locations about to be archived. Discovers a DB by its
3231
- * `-wal` sidecar (only WAL-mode DBs have one, and only those need
3232
- * folding), opens a short-lived second connection — SQLite permits
3233
- * concurrent connections, and a checkpoint is a supported concurrent
3234
- * operation against the hub's live writer — checkpoints, and closes.
3235
- *
3236
- * Every failure is swallowed: a `SQLITE_BUSY`, a locked file, a
3237
- * missing native binding, or a non-DB `.db` file must not fail the
3238
- * backup. A residual WAL still restores correctly; the checkpoint is a
3239
- * consistency *improvement*, not a precondition.
3240
- */
3241
- async checkpointDatabasesUnder(locations) {
3242
- const dbPaths = /* @__PURE__ */ new Set();
3243
- for (const loc of locations) await collectWalDatabases(path$1.isAbsolute(loc) ? loc : path$1.join(this.dataDir, loc), dbPaths, this.exclusionPredicate());
3244
- if (dbPaths.size === 0) return;
3245
- let Database;
3246
- try {
3247
- Database = (await import("better-sqlite3")).default;
3248
- } catch (err) {
3249
- this.logger.warn("createArchive: better-sqlite3 unavailable — skipping WAL checkpoint", { meta: {
3250
- error: err instanceof Error ? err.message : String(err),
3251
- dbCount: dbPaths.size
3252
- } });
3253
- return;
3254
- }
3255
- for (const dbPath of dbPaths) try {
3256
- const db = new Database(dbPath);
3257
- try {
3258
- db.pragma("wal_checkpoint(PASSIVE)");
3259
- } finally {
3260
- db.close();
3261
- }
3262
- this.logger.debug("createArchive: checkpointed DB", { meta: { dbPath } });
3263
- } catch (err) {
3264
- this.logger.warn("createArchive: WAL checkpoint failed — archiving DB as-is", { meta: {
3265
- dbPath,
3266
- error: err instanceof Error ? err.message : String(err)
3267
- } });
3268
- }
3269
- }
3270
- /**
3271
3228
  * without extracting payload files. Streams the tar.gz, parses the
3272
3229
  * one entry we care about, then aborts.
3273
3230
  */
@@ -3387,45 +3344,14 @@ var SystemBackupService = class {
3387
3344
  }
3388
3345
  /**
3389
3346
  * Absolute-path form of {@link isExcludedFromBackup}, anchored on
3390
- * this service's dataDir. Shared by the manifest walk, the location
3391
- * stats and the WAL-checkpoint scan so all three agree with the tar
3392
- * filter about what a backup contains.
3347
+ * this service's dataDir. Shared by the manifest walk and the location
3348
+ * stats so both agree with the tar filter about what a backup contains.
3393
3349
  */
3394
3350
  exclusionPredicate() {
3395
3351
  return (abs) => isExcludedFromBackup(path$1.relative(this.dataDir, abs));
3396
3352
  }
3397
3353
  };
3398
3354
  /**
3399
- * Recursively collect every SQLite database under `abs` that is in WAL
3400
- * mode, identified by the presence of a `<db>-wal` sidecar. Only those
3401
- * need a checkpoint; a rollback-journal DB has nothing to fold. Errors
3402
- * (unreadable dir, broken symlink) are skipped silently — this feeds a
3403
- * best-effort step.
3404
- */
3405
- async function collectWalDatabases(abs, out, shouldSkip) {
3406
- if (shouldSkip?.(abs)) return;
3407
- let stat;
3408
- try {
3409
- stat = await fsp.stat(abs);
3410
- } catch {
3411
- return;
3412
- }
3413
- if (stat.isDirectory()) {
3414
- let children;
3415
- try {
3416
- children = await fsp.readdir(abs);
3417
- } catch {
3418
- return;
3419
- }
3420
- for (const child of children) await collectWalDatabases(path$1.join(abs, child), out, shouldSkip);
3421
- return;
3422
- }
3423
- if (abs.endsWith("-wal")) {
3424
- const dbPath = abs.slice(0, -4);
3425
- if (fs.existsSync(dbPath)) out.add(dbPath);
3426
- }
3427
- }
3428
- /**
3429
3355
  * Recursively walk a directory and produce one `ArchiveEntry` per
3430
3356
  * filesystem node. `dataDir` is used to compute the archive-relative
3431
3357
  * path. Symlinks are dereferenced (matches the `follow: true` flag we
@@ -0,0 +1,21 @@
1
+ import { ComposerApplyInput, ComposerApplyResult, IScopedLogger } from '@camstack/types';
2
+ /** An apply either waits, naming why, or goes on with the reconciled integration. */
3
+ export type ApplyGate = {
4
+ readonly kind: 'wait';
5
+ readonly reason: string;
6
+ } | {
7
+ readonly kind: 'go';
8
+ readonly integrationId: string;
9
+ };
10
+ export declare class ApplyWaits {
11
+ private readonly logger;
12
+ /** Consecutive applies refused because `core_blocks` had not loaded: warned once per run. */
13
+ private storeWaitApplies;
14
+ /** Consecutive applies refused because the Blocks integration had not reconciled: warned once per run. */
15
+ private integrationWaitApplies;
16
+ constructor(logger: IScopedLogger);
17
+ /** Whether this apply must act on nothing (and why), or may go on. */
18
+ check(input: ComposerApplyInput, liveCount: number): ApplyGate;
19
+ }
20
+ /** Every block `starting`, naming the wait; nothing acted. */
21
+ export declare function waitingResult(blockIds: readonly string[], reason: string): ComposerApplyResult;
@@ -0,0 +1,22 @@
1
+ import { ClaimGate, ClaimKey } from './claim-gate.js';
2
+ import { PlannedBlock } from './composer-plan.js';
3
+ import { LiveComposition } from './composer-verdict.js';
4
+ import { TrackedSourceRequest } from './source-tracker.js';
5
+ /** A release answered with one of these hands back nothing of ours: the gate forgets the claim. */
6
+ export declare const RELEASE_SETTLED_CODES: ReadonlySet<string>;
7
+ /**
8
+ * The source keys an operator action on `p` re-opens: every source its fields
9
+ * read, except its own target (re-resolved by `planExisting` already, never
10
+ * twice).
11
+ */
12
+ export declare function operatorResetKeys(p: PlannedBlock): readonly string[];
13
+ /** Every source the live set reads, with the view each own cap is read in. */
14
+ export declare function trackedSourceRequests(live: Iterable<LiveComposition>): readonly TrackedSourceRequest[];
15
+ /**
16
+ * Rule 3: the claims every live customization WANTS this apply; everything
17
+ * else the gate knows is a release candidate, handed back once two applies
18
+ * agree. A block that cannot plan — waiting, unreadable, failed, or with its
19
+ * target gone — HOLDS its claims (rule 4, rule 5): they are wanted as they
20
+ * are. A disabled block wants none (rule 7).
21
+ */
22
+ export declare function wantedClaims(live: ReadonlyMap<string, LiveComposition>, claimGate: Pick<ClaimGate, 'claimsOf'>): readonly ClaimKey[];
@@ -0,0 +1,22 @@
1
+ import { CapabilityLookup, IScopedLogger } from '@camstack/types';
2
+ import { ComposedDeviceHandle } from './composed-device.js';
3
+ import { PlannedBlock } from './composer-plan.js';
4
+ import { ComposedWriteTarget, CompositionEventEmitter, CompositionRuntime, DeadlineTimer } from './composition-runtime.js';
5
+ import { ComposerClaimsApi, ExistingTargetSeedRead } from './existing-target.js';
6
+ import { ClaimEvent } from './held-claims.js';
7
+ import { SourceTracker } from './source-tracker.js';
8
+ /** The composer dependencies a runtime and its write target are built from. */
9
+ export interface RuntimeFactoryDeps {
10
+ readonly logger: IScopedLogger;
11
+ readonly claims: ComposerClaimsApi;
12
+ readonly tracker: SourceTracker;
13
+ readonly events: CompositionEventEmitter;
14
+ readonly lookupCap: CapabilityLookup;
15
+ readonly now: () => number;
16
+ readonly newTimer: () => DeadlineTimer;
17
+ }
18
+ /** Where a block's own claim transitions are followed (C-1/N-1). */
19
+ export type ClaimEventSink = (deviceId: number, cap: string, owner: string, event: ClaimEvent) => void;
20
+ /** Where a block's runtime writes: its composed device, or a claim-backed target on the existing device. */
21
+ export declare function writerFor(deps: RuntimeFactoryDeps, p: PlannedBlock, device: ComposedDeviceHandle | null, onClaimEvent: ClaimEventSink): ComposedWriteTarget | null;
22
+ export declare function newCompositionRuntime(deps: RuntimeFactoryDeps, p: PlannedBlock, target: ComposedWriteTarget, seed?: ExistingTargetSeedRead): CompositionRuntime;
@@ -0,0 +1,22 @@
1
+ import { FieldClaim, IScopedLogger } from '@camstack/types';
2
+ import { PersistedComposedRow } from './composition-declarations.js';
3
+ import { ConfirmedSeed } from './confirmed-seed.js';
4
+ import { ComposerClaimsApi } from './existing-target.js';
5
+ export interface ComposedRowSeedDeps {
6
+ readonly logger: IScopedLogger;
7
+ /** The composed rows that already exist (`listOwnDevices`). */
8
+ readonly readPersisted: () => Promise<readonly PersistedComposedRow[]>;
9
+ readonly onSeeded: (rows: readonly PersistedComposedRow[]) => void;
10
+ }
11
+ /** The rows the composer owns, read ONCE before its first withdrawal decision. */
12
+ export declare function composedRowSeed(deps: ComposedRowSeedDeps): ConfirmedSeed<PersistedComposedRow>;
13
+ export interface ClaimIndexSeedDeps {
14
+ readonly logger: IScopedLogger;
15
+ readonly claims: ComposerClaimsApi;
16
+ readonly onSeeded: (claims: readonly FieldClaim[]) => void;
17
+ }
18
+ /**
19
+ * The claims the hub holds, read ONCE before the first release decision (rule
20
+ * 3). `not-loaded` is UNKNOWN (D315), never empty.
21
+ */
22
+ export declare function claimIndexSeed(deps: ClaimIndexSeedDeps): ConfirmedSeed<FieldClaim>;