@odla-ai/chapter 0.33.0 → 0.33.1

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.
package/README.md CHANGED
@@ -445,6 +445,11 @@ join-config, application tier validation, and checkout already read. Public
445
445
  application contract, and theme. Applicant requests therefore use local
446
446
  last-known-good state and never depend on the hub being reachable.
447
447
 
448
+ The first delivery is empty-history safe: Chapter checks a bounded aggregate
449
+ count before asking odla-db to order revision history. A freshly provisioned
450
+ follower therefore acknowledges revision 1 immediately instead of depending on
451
+ an ordered query over an empty result set.
452
+
448
453
  The hub owns a durable outbox: create/verify immutable Stripe Prices first,
449
454
  persist the desired revision, retry signed delivery, and mark it active only
450
455
  after the exact revision/digest receipt. Version history is for audit and
package/dist/index.d.cts CHANGED
@@ -310,6 +310,16 @@ interface DbOp {
310
310
  * member routes consume — so route logic is testable against an in-memory fake. */
311
311
  interface ChapterDb {
312
312
  query(q: Record<string, unknown>): Promise<Record<string, Array<Record<string, unknown>>>>;
313
+ /** Optional bounded aggregate supported by the real privileged odla-db
314
+ * client. Routes use it to prove a result set is non-empty before issuing
315
+ * an ordered lookup, while small test doubles may continue to omit it. */
316
+ aggregate?(ns: string, spec: {
317
+ count?: boolean;
318
+ }, opts?: {
319
+ where?: Record<string, unknown>;
320
+ }): Promise<{
321
+ count?: number;
322
+ }>;
313
323
  transact(ops: DbOp[], opts?: {
314
324
  mutationId?: string;
315
325
  }): Promise<{
package/dist/index.d.ts CHANGED
@@ -310,6 +310,16 @@ interface DbOp {
310
310
  * member routes consume — so route logic is testable against an in-memory fake. */
311
311
  interface ChapterDb {
312
312
  query(q: Record<string, unknown>): Promise<Record<string, Array<Record<string, unknown>>>>;
313
+ /** Optional bounded aggregate supported by the real privileged odla-db
314
+ * client. Routes use it to prove a result set is non-empty before issuing
315
+ * an ordered lookup, while small test doubles may continue to omit it. */
316
+ aggregate?(ns: string, spec: {
317
+ count?: boolean;
318
+ }, opts?: {
319
+ where?: Record<string, unknown>;
320
+ }): Promise<{
321
+ count?: number;
322
+ }>;
313
323
  transact(ops: DbOp[], opts?: {
314
324
  mutationId?: string;
315
325
  }): Promise<{
@@ -527,7 +527,16 @@ function asStored(row) {
527
527
  if (!row || typeof row.document !== "object" || row.document === null) return null;
528
528
  return row;
529
529
  }
530
+ async function hasSignupControl(db, targetId, environment, active) {
531
+ const where = { targetId, environment, ...active ? { active: true } : {} };
532
+ if (db.aggregate) {
533
+ return Number((await db.aggregate("signupControls", { count: true }, { where })).count ?? 0) > 0;
534
+ }
535
+ const rows2 = (await db.query({ signupControls: {} })).signupControls;
536
+ return Array.isArray(rows2) && rows2.some((row) => row.targetId === targetId && row.environment === environment && (!active || row.active === true));
537
+ }
530
538
  async function loadLatestSignupControl(db, targetId, environment) {
539
+ if (!await hasSignupControl(db, targetId, environment)) return null;
531
540
  const rows2 = (await db.query({ signupControls: { $: {
532
541
  where: { targetId, environment },
533
542
  order: { revision: "desc" },
@@ -537,6 +546,7 @@ async function loadLatestSignupControl(db, targetId, environment) {
537
546
  return asStored(latest);
538
547
  }
539
548
  async function loadActiveSignupControl(db, targetId, environment) {
549
+ if (!await hasSignupControl(db, targetId, environment, true)) return null;
540
550
  const rows2 = (await db.query({ signupControls: { $: {
541
551
  where: { targetId, environment, active: true },
542
552
  order: { revision: "desc" },