@noodleseed/one 0.119.0 → 0.119.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.
@@ -51,6 +51,21 @@ export declare function dayKey(now: Date): string;
51
51
  * accidental burst forgives itself within the same visit, where a solvency bound wants the whole day.
52
52
  */
53
53
  export declare function hourKey(now: Date): string;
54
- export declare function windowKey(now: Date, window?: 'day' | 'hour'): string;
54
+ /**
55
+ * The row identity for a request, and the reason the hour is folded into the **key** rather than the day.
56
+ *
57
+ * The counter table's `day` column is a real `date`, so an hour-resolution value cannot go there: it
58
+ * fails at the database with `invalid input syntax for type date`, which is exactly how the per-address
59
+ * tier took production down after passing every test — the shared suite only exercised the daily window
60
+ * and the route tests used a mocked counter, so no test ever put an hourly request through real SQL.
61
+ *
62
+ * Partitioning by date and distinguishing the hour inside the key keeps both windows in one table with
63
+ * no migration and no ambiguity: a daily row and an hourly row can never collide, because the hourly key
64
+ * carries a suffix the daily key cannot produce.
65
+ */
66
+ export declare function counterRow(request: Pick<CounterRequest, 'key' | 'window'>, now: Date): {
67
+ readonly key: string;
68
+ readonly day: string;
69
+ };
55
70
  export declare function nextReset(now: Date, window?: 'day' | 'hour'): Date;
56
71
  //# sourceMappingURL=counter-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"counter-store.d.ts","sourceRoot":"","sources":["../src/counter-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,8FAA8F;IAC9F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrE,mDAAmD;IACnD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAEzC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,KAAK,GAAG,MAAc,GAAG,MAAM,CAE3E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,KAAK,GAAG,MAAc,GAAG,IAAI,CAUzE"}
1
+ {"version":3,"file":"counter-store.d.ts","sourceRoot":"","sources":["../src/counter-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,8FAA8F;IAC9F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrE,mDAAmD;IACnD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAEzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,QAAQ,CAAC,EAC/C,GAAG,EAAE,IAAI,GACR;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKhD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,KAAK,GAAG,MAAc,GAAG,IAAI,CAUzE"}
@@ -17,8 +17,23 @@ export function dayKey(now) {
17
17
  export function hourKey(now) {
18
18
  return now.toISOString().slice(0, 13);
19
19
  }
20
- export function windowKey(now, window = 'day') {
21
- return window === 'hour' ? hourKey(now) : dayKey(now);
20
+ /**
21
+ * The row identity for a request, and the reason the hour is folded into the **key** rather than the day.
22
+ *
23
+ * The counter table's `day` column is a real `date`, so an hour-resolution value cannot go there: it
24
+ * fails at the database with `invalid input syntax for type date`, which is exactly how the per-address
25
+ * tier took production down after passing every test — the shared suite only exercised the daily window
26
+ * and the route tests used a mocked counter, so no test ever put an hourly request through real SQL.
27
+ *
28
+ * Partitioning by date and distinguishing the hour inside the key keeps both windows in one table with
29
+ * no migration and no ambiguity: a daily row and an hourly row can never collide, because the hourly key
30
+ * carries a suffix the daily key cannot produce.
31
+ */
32
+ export function counterRow(request, now) {
33
+ return {
34
+ key: request.window === 'hour' ? `${request.key}@${hourKey(now)}` : request.key,
35
+ day: dayKey(now),
36
+ };
22
37
  }
23
38
  export function nextReset(now, window = 'day') {
24
39
  const next = new Date(now);
@@ -1 +1 @@
1
- {"version":3,"file":"counter-store.js","sourceRoot":"","sources":["../src/counter-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA2CH,qFAAqF;AACrF,MAAM,UAAU,MAAM,CAAC,GAAS;IAC9B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAS;IAC/B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAS,EAAE,SAAyB,KAAK;IACjE,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAS,EAAE,SAAyB,KAAK;IACjE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"counter-store.js","sourceRoot":"","sources":["../src/counter-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA2CH,qFAAqF;AACrF,MAAM,UAAU,MAAM,CAAC,GAAS;IAC9B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAS;IAC/B,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACxB,OAA+C,EAC/C,GAAS;IAET,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;QAC/E,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAS,EAAE,SAAyB,KAAK;IACjE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-counter-store.d.ts","sourceRoot":"","sources":["../src/in-memory-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EAIvB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;;IACjE,QAAQ,CAAC,OAAO,SAAS;IAGnB,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAYpE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAGpD"}
1
+ {"version":3,"file":"in-memory-counter-store.d.ts","sourceRoot":"","sources":["../src/in-memory-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;;IACjE,QAAQ,CAAC,OAAO,SAAS;IAGnB,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAapE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAGpD"}
@@ -1,4 +1,4 @@
1
- import { dayKey, nextReset, windowKey, } from './counter-store.js';
1
+ import { counterRow, dayKey, nextReset, } from './counter-store.js';
2
2
  /**
3
3
  * Process-local counters for local development and tests.
4
4
  *
@@ -10,7 +10,8 @@ export class InMemoryDailyCounterStore {
10
10
  durable = false;
11
11
  #used = new Map();
12
12
  async consume(request, now) {
13
- const key = `${windowKey(now, request.window)}:${request.key}`;
13
+ const row = counterRow(request, now);
14
+ const key = `${row.day}:${row.key}`;
14
15
  const amount = request.amount ?? 1;
15
16
  const used = this.#used.get(key) ?? 0;
16
17
  const resetAt = nextReset(now, request.window);
@@ -1 +1 @@
1
- {"version":3,"file":"in-memory-counter-store.js","sourceRoot":"","sources":["../src/in-memory-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,MAAM,EACN,SAAS,EACT,SAAS,GACV,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,OAAO,yBAAyB;IAC3B,OAAO,GAAG,KAAK,CAAC;IAChB,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,KAAK,CAAC,OAAO,CAAC,OAAuB,EAAE,GAAS;QAC9C,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,GAAS;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;CACF"}
1
+ {"version":3,"file":"in-memory-counter-store.js","sourceRoot":"","sources":["../src/in-memory-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,UAAU,EAEV,MAAM,EACN,SAAS,GACV,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,OAAO,yBAAyB;IAC3B,OAAO,GAAG,KAAK,CAAC;IAChB,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,KAAK,CAAC,OAAO,CAAC,OAAuB,EAAE,GAAS;QAC9C,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,GAAS;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-counter-store.d.ts","sourceRoot":"","sources":["../src/postgres-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EAIvB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;;IACjE,QAAQ,CAAC,OAAO,QAAQ;gBAGZ,IAAI,EAAE,IAAI;IAIhB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7B,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAgCpE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAOpD"}
1
+ {"version":3,"file":"postgres-counter-store.d.ts","sourceRoot":"","sources":["../src/postgres-counter-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EAEnB,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;;IACjE,QAAQ,CAAC,OAAO,QAAQ;gBAGZ,IAAI,EAAE,IAAI;IAIhB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7B,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IA0CpE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAOpD"}
@@ -1,4 +1,4 @@
1
- import { dayKey, nextReset, windowKey, } from './counter-store.js';
1
+ import { counterRow, dayKey, nextReset, } from './counter-store.js';
2
2
  /**
3
3
  * Durable daily counters shared by every service instance.
4
4
  *
@@ -27,7 +27,7 @@ export class PostgresDailyCounterStore {
27
27
  }
28
28
  async consume(request, now) {
29
29
  const amount = request.amount ?? 1;
30
- const day = windowKey(now, request.window);
30
+ const { key: counterKey, day } = counterRow(request, now);
31
31
  const resetAt = nextReset(now, request.window);
32
32
  // A zero limit can never be satisfied; skip the round trip and report the switched-off surface.
33
33
  if (request.limit <= 0) {
@@ -42,18 +42,24 @@ export class PostgresDailyCounterStore {
42
42
  ON CONFLICT (counter_key, day) DO UPDATE
43
43
  SET used = admission_daily_counters.used + $3::bigint, updated_at = now()
44
44
  WHERE admission_daily_counters.used + $3::bigint <= $4::bigint
45
- RETURNING used`, [request.key, day, amount, request.limit]);
45
+ RETURNING used`, [counterKey, day, amount, request.limit]);
46
46
  const row = result.rows[0];
47
47
  if (row === undefined) {
48
48
  return {
49
49
  allowed: false,
50
- used: await this.peek(request.key, now),
50
+ // The stored key, not the requested one: an hourly row carries its window in the key, and
51
+ // reading the raw key would report a denied visitor as having used nothing.
52
+ used: await this.#usedFor(counterKey, day),
51
53
  limit: request.limit,
52
54
  resetAt,
53
55
  };
54
56
  }
55
57
  return { allowed: true, used: Number(row.used), limit: request.limit, resetAt };
56
58
  }
59
+ async #usedFor(counterKey, day) {
60
+ const result = await this.#pool.query('SELECT used FROM admission_daily_counters WHERE counter_key = $1 AND day = $2', [counterKey, day]);
61
+ return result.rows[0] === undefined ? 0 : Number(result.rows[0].used);
62
+ }
57
63
  async peek(key, now) {
58
64
  const result = await this.#pool.query('SELECT used FROM admission_daily_counters WHERE counter_key = $1 AND day = $2', [key, dayKey(now)]);
59
65
  return result.rows[0] === undefined ? 0 : Number(result.rows[0].used);
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-counter-store.js","sourceRoot":"","sources":["../src/postgres-counter-store.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,MAAM,EACN,SAAS,EACT,SAAS,GACV,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,OAAO,yBAAyB;IAC3B,OAAO,GAAG,IAAI,CAAC;IACf,KAAK,CAAO;IAErB,YAAY,IAAU;QACpB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;;;;;;;;KAQtB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAuB,EAAE,GAAS;QAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,gGAAgG;QAChG,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACpE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK;QACnC,iGAAiG;QACjG,8FAA8F;QAC9F,+CAA+C;QAC/C;;;;;sBAKgB,EAChB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAC1C,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;gBACvC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO;aACR,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,GAAS;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CACnC,+EAA+E,EAC/E,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CACnB,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;CACF"}
1
+ {"version":3,"file":"postgres-counter-store.js","sourceRoot":"","sources":["../src/postgres-counter-store.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,UAAU,EAEV,MAAM,EACN,SAAS,GACV,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,OAAO,yBAAyB;IAC3B,OAAO,GAAG,IAAI,CAAC;IACf,KAAK,CAAO;IAErB,YAAY,IAAU;QACpB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;;;;;;;;KAQtB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAuB,EAAE,GAAS;QAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/C,gGAAgG;QAChG,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACpE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK;QACnC,iGAAiG;QACjG,8FAA8F;QAC9F,+CAA+C;QAC/C;;;;;sBAKgB,EAChB,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CACzC,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,0FAA0F;gBAC1F,4EAA4E;gBAC5E,IAAI,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC;gBAC1C,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO;aACR,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAClF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,UAAkB,EAAE,GAAW;QAC5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CACnC,+EAA+E,EAC/E,CAAC,UAAU,EAAE,GAAG,CAAC,CAClB,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,GAAS;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CACnC,+EAA+E,EAC/E,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CACnB,CAAC;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC;CACF"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodle-borg/agent-kit",
3
- "version": "0.68.0",
3
+ "version": "0.69.0",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  "@noodle-borg/admission-limits": "0.0.0",
27
27
  "@noodle-borg/compiler": "0.0.0",
28
28
  "@noodle-borg/runtime": "0.0.0",
29
- "@noodleseed/assistant": "1.15.0"
29
+ "@noodleseed/assistant": "1.16.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^24.13.1",
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "@modelcontextprotocol/sdk": "^1.29.0",
30
30
  "@noodle-borg/admission-limits": "0.0.0",
31
- "@noodle-borg/agent-kit": "0.68.0",
31
+ "@noodle-borg/agent-kit": "0.69.0",
32
32
  "@noodle-borg/app-package": "0.0.0",
33
33
  "@noodle-borg/assistant-gateway": "0.0.0",
34
34
  "@noodle-borg/auth": "0.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/assistant",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Embed the Noodle Seed customer-branded assistant in your web app with a Web Component, managed or custom React UI, DOM-free client, and backend session helper. Authoring and deploying the server is @noodleseed/one.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/one",
3
- "version": "0.119.0",
3
+ "version": "0.119.1",
4
4
  "private": false,
5
5
  "description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
6
6
  "license": "Apache-2.0",
@@ -227,7 +227,7 @@
227
227
  "@jitl/quickjs-wasmfile-release-sync": "0.32.0",
228
228
  "@modelcontextprotocol/client": "2.0.0",
229
229
  "@modelcontextprotocol/server": "2.0.0",
230
- "@noodle-borg/agent-kit": "0.68.0",
230
+ "@noodle-borg/agent-kit": "0.69.0",
231
231
  "@noodle-borg/app-audit": "0.0.0",
232
232
  "@noodle-borg/auth": "0.0.0",
233
233
  "@noodle-borg/authoring": "0.0.0",