cloudflare-next-intl 0.8.0 → 0.8.2

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
@@ -523,7 +523,38 @@ const rows = await withPublicDb((db) => db.select().from(bonds).limit(10));
523
523
  helpers (`excluded`, `onConflictSet`, `ago`, `currentDate`, `windowCount`,
524
524
  `unnestLateral`, `ascNullsLast`, `alwaysTrue`, `lateral`, `aliasColumn`,
525
525
  `minOf`, `maxOf`, `roundReal`, `multiply`, `scalarFromCte`) for building
526
- upsert/window/lateral-join queries without dropping to raw SQL.
526
+ upsert/window/lateral-join queries without dropping to raw SQL, plus a
527
+ re-export of `drizzle-orm`'s common query-building primitives (`eq`, `and`,
528
+ `or`, `asc`, `desc`, `gte`, `gt`, `lte`, `lt`, `isNull`, `isNotNull`, `count`,
529
+ `sum`, `max`, `min`, `sql`) — so code that only builds queries against the
530
+ `DrizzleDb` handle from `withPublicDb`/`withUserDb` doesn't need its own
531
+ `drizzle-orm` import for these. Anything not listed here (schema definitions,
532
+ `drizzle-orm/pg-core` column builders, relations) still comes from
533
+ `drizzle-orm` directly — this package re-exports the query-operator surface
534
+ only, not the whole library.
535
+
536
+ #### Testing code that calls `withPublicDb`/`withUserDb`
537
+
538
+ `cloudflare-next-intl/dbTesting` exports a fake `DrizzleDb` so repository/unit
539
+ tests don't need a real Postgres connection:
540
+
541
+ ```typescript
542
+ import { makeFakeDb, rowsResult } from "cloudflare-next-intl/dbTesting";
543
+
544
+ const db = makeFakeDb([rowsResult([{ id: 1 }])]);
545
+ const rows = await db.select().from(bonds).limit(10);
546
+ // rows === [{ id: 1 }]
547
+ ```
548
+
549
+ `makeFakeDb(results)` takes an ordered queue of `rowsResult(rows)` (for
550
+ `select`/`insert`/`update`/`delete`) and `executeResult(rows)` (for
551
+ `execute(...)`) entries, consumed one per terminal call in the exact order
552
+ your code issues them. Every intermediate chain call (`.where(...)`,
553
+ `.values(...)`, `.orderBy(...)`, etc.) is recorded with its exact arguments,
554
+ inspectable via `db.calls[i].chain.argsOf('where')` — so a test can assert not
555
+ just "select was called" but "the second select's `.where(...)` argument was
556
+ X". Handles `db.$with(name).as(builder)` / `db.with(...).select(...)`
557
+ CTE-style queries the same way the real Drizzle client does.
527
558
 
528
559
  ## License
529
560
 
@@ -1,4 +1,11 @@
1
- import { type SQL, type Table } from 'drizzle-orm';
1
+ import { sql, eq, and, or, asc, desc, gte, gt, lte, lt, isNull, isNotNull, count, sum, max, min, type SQL, type Table } from 'drizzle-orm';
2
+ /**
3
+ * Re-exported `drizzle-orm` query-building primitives, so code that only
4
+ * calls `withPublicDb`/`withUserDb` and builds queries against the returned
5
+ * `DrizzleDb` never needs its own `drizzle-orm` import for common predicates,
6
+ * ordering, and aggregates.
7
+ */
8
+ export { eq, and, or, asc, desc, gte, gt, lte, lt, isNull, isNotNull, count, sum, max, min, sql };
2
9
  /**
3
10
  * Type-safe helper returning `excluded.<db_column_name>` SQL expressions for a Drizzle table.
4
11
  *
@@ -1,4 +1,11 @@
1
- import { getTableColumns, getTableName, sql } from 'drizzle-orm';
1
+ import { getTableColumns, getTableName, sql, eq, and, or, asc, desc, gte, gt, lte, lt, isNull, isNotNull, count, sum, max, min, } from 'drizzle-orm';
2
+ /**
3
+ * Re-exported `drizzle-orm` query-building primitives, so code that only
4
+ * calls `withPublicDb`/`withUserDb` and builds queries against the returned
5
+ * `DrizzleDb` never needs its own `drizzle-orm` import for common predicates,
6
+ * ordering, and aggregates.
7
+ */
8
+ export { eq, and, or, asc, desc, gte, gt, lte, lt, isNull, isNotNull, count, sum, max, min, sql };
2
9
  /**
3
10
  * Type-safe helper returning `excluded.<db_column_name>` SQL expressions for a Drizzle table.
4
11
  *
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Test double for {@link DrizzleDb} — the handle `withPublicDb`/`withUserDb`
3
+ * pass to your callback. Use it in repository/unit tests to avoid a real
4
+ * Postgres connection: build one with {@link makeFakeDb}, queue canned
5
+ * results with {@link rowsResult}/{@link executeResult}, then pass it
6
+ * directly wherever your code expects a `DrizzleDb`.
7
+ *
8
+ * Real repositories call chains like `db.select({...}).from(t).where(w)...`
9
+ * where every intermediate call returns `this` and the final awaited value
10
+ * resolves to a canned row array — plus `db.execute(sql...)` resolving to
11
+ * `{ rows: [...] }`, and `db.$with(name).as(builder)` / `db.with(...).select(...)`
12
+ * for CTE-based queries.
13
+ *
14
+ * Each fake db instance is given an ordered queue of results (`resultQueue`)
15
+ * consumed one-per-terminal-call in the exact sequence your code issues
16
+ * them — select/insert/update/delete/execute all share one queue, so call
17
+ * order in your source is what determines which fixture a call gets.
18
+ *
19
+ * Every intermediate chain call (`.where(...)`, `.values(...)`, `.set(...)`,
20
+ * `.orderBy(...)`, etc.) is also recorded with its exact arguments on the
21
+ * chain object returned from `select`/`insert`/`update`/`delete`, and that
22
+ * chain object is attached to the matching entry in `db.calls` — so a test
23
+ * can assert not just "select was called twice" but "the second select's
24
+ * `.where(...)` argument was X", closing the gap where a fake that only
25
+ * echoes canned rows back can't detect a broken predicate/sort/value.
26
+ */
27
+ type Row = Record<string, unknown>;
28
+ interface QueuedResult {
29
+ kind: 'rows' | 'execute';
30
+ rows: Row[];
31
+ }
32
+ /** Queues a result for a `select`/`insert`/`update`/`delete` chain. */
33
+ declare function rowsResult(rows: Row[]): QueuedResult;
34
+ /** Queues a result for an `execute(...)` call. */
35
+ declare function executeResult(rows: Row[]): QueuedResult;
36
+ interface ChainCall {
37
+ method: string;
38
+ args: unknown[];
39
+ }
40
+ /**
41
+ * Lazy like real drizzle query builders: intermediate calls just record
42
+ * themselves and return `this`; the queue is only consumed when the chain
43
+ * is actually awaited (`.then()`), which is also when a CTE built via
44
+ * `db.$with(name).as(builder)` is skipped — a CTE definition is never
45
+ * itself awaited standalone, only the final `db.with(...).select(...)`
46
+ * chain that references it is.
47
+ */
48
+ declare class ChainableQuery implements PromiseLike<Row[]> {
49
+ private readonly takeRows;
50
+ /** Every intermediate call this chain received, in call order, with its exact arguments. */
51
+ readonly chainCalls: ChainCall[];
52
+ constructor(takeRows: () => Row[]);
53
+ private record;
54
+ /** Returns the arguments of this chain's first call to `method` (e.g. `"where"`, `"values"`). */
55
+ argsOf(method: string): unknown[] | undefined;
56
+ from(...args: unknown[]): this;
57
+ leftJoin(...args: unknown[]): this;
58
+ innerJoin(...args: unknown[]): this;
59
+ where(...args: unknown[]): this;
60
+ orderBy(...args: unknown[]): this;
61
+ groupBy(...args: unknown[]): this;
62
+ limit(...args: unknown[]): this;
63
+ offset(...args: unknown[]): this;
64
+ values(...args: unknown[]): this;
65
+ set(...args: unknown[]): this;
66
+ returning(...args: unknown[]): this;
67
+ as(...args: unknown[]): this;
68
+ then<TResult1 = Row[], TResult2 = never>(onfulfilled?: ((value: Row[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
69
+ }
70
+ /**
71
+ * Fake {@link DrizzleDb} backed by a queue of canned results. Build one with
72
+ * {@link makeFakeDb} rather than calling this constructor directly.
73
+ */
74
+ declare class FakeDrizzleDb {
75
+ private readonly queue;
76
+ /** Top-level calls captured for assertions — each entry's `chain` is the same `ChainableQuery` returned to the caller, so its recorded `.where()`/`.values()`/etc. arguments are inspectable via `chain.argsOf(...)`. */
77
+ readonly calls: {
78
+ method: string;
79
+ args: unknown[];
80
+ chain?: ChainableQuery;
81
+ }[];
82
+ constructor(queue: QueuedResult[]);
83
+ private takeRows;
84
+ private makeChain;
85
+ select(...args: unknown[]): ChainableQuery;
86
+ insert(...args: unknown[]): ChainableQuery;
87
+ update(...args: unknown[]): ChainableQuery;
88
+ delete(...args: unknown[]): ChainableQuery;
89
+ execute(...args: unknown[]): Promise<{
90
+ rows: Row[];
91
+ }>;
92
+ /** CTE definition — never awaited on its own, so it must not touch the queue. */
93
+ $with(_name: string): {
94
+ as: (builder: unknown) => unknown;
95
+ };
96
+ with(..._ctes: unknown[]): {
97
+ select: (...args: unknown[]) => ChainableQuery;
98
+ };
99
+ }
100
+ /**
101
+ * Builds a fake {@link DrizzleDb} that resolves each terminal call
102
+ * (`select`/`insert`/`update`/`delete`/`execute`) to the next entry in
103
+ * `results`, in the exact order your code under test calls them.
104
+ *
105
+ * @param results Queued results, one per terminal call, built with
106
+ * {@link rowsResult}/{@link executeResult}.
107
+ * @returns A fake db to pass wherever your code expects a `DrizzleDb`.
108
+ *
109
+ * @example
110
+ * const db = makeFakeDb([rowsResult([{ id: 1 }])]);
111
+ * const rows = await db.select().from(bonds);
112
+ */
113
+ export declare function makeFakeDb(results: QueuedResult[]): FakeDrizzleDb;
114
+ export { rowsResult, executeResult };
115
+ export type { Row, ChainableQuery, FakeDrizzleDb };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Test double for {@link DrizzleDb} — the handle `withPublicDb`/`withUserDb`
3
+ * pass to your callback. Use it in repository/unit tests to avoid a real
4
+ * Postgres connection: build one with {@link makeFakeDb}, queue canned
5
+ * results with {@link rowsResult}/{@link executeResult}, then pass it
6
+ * directly wherever your code expects a `DrizzleDb`.
7
+ *
8
+ * Real repositories call chains like `db.select({...}).from(t).where(w)...`
9
+ * where every intermediate call returns `this` and the final awaited value
10
+ * resolves to a canned row array — plus `db.execute(sql...)` resolving to
11
+ * `{ rows: [...] }`, and `db.$with(name).as(builder)` / `db.with(...).select(...)`
12
+ * for CTE-based queries.
13
+ *
14
+ * Each fake db instance is given an ordered queue of results (`resultQueue`)
15
+ * consumed one-per-terminal-call in the exact sequence your code issues
16
+ * them — select/insert/update/delete/execute all share one queue, so call
17
+ * order in your source is what determines which fixture a call gets.
18
+ *
19
+ * Every intermediate chain call (`.where(...)`, `.values(...)`, `.set(...)`,
20
+ * `.orderBy(...)`, etc.) is also recorded with its exact arguments on the
21
+ * chain object returned from `select`/`insert`/`update`/`delete`, and that
22
+ * chain object is attached to the matching entry in `db.calls` — so a test
23
+ * can assert not just "select was called twice" but "the second select's
24
+ * `.where(...)` argument was X", closing the gap where a fake that only
25
+ * echoes canned rows back can't detect a broken predicate/sort/value.
26
+ */
27
+ /** Queues a result for a `select`/`insert`/`update`/`delete` chain. */
28
+ function rowsResult(rows) {
29
+ return { kind: 'rows', rows };
30
+ }
31
+ /** Queues a result for an `execute(...)` call. */
32
+ function executeResult(rows) {
33
+ return { kind: 'execute', rows };
34
+ }
35
+ /**
36
+ * Lazy like real drizzle query builders: intermediate calls just record
37
+ * themselves and return `this`; the queue is only consumed when the chain
38
+ * is actually awaited (`.then()`), which is also when a CTE built via
39
+ * `db.$with(name).as(builder)` is skipped — a CTE definition is never
40
+ * itself awaited standalone, only the final `db.with(...).select(...)`
41
+ * chain that references it is.
42
+ */
43
+ class ChainableQuery {
44
+ constructor(takeRows) {
45
+ this.takeRows = takeRows;
46
+ /** Every intermediate call this chain received, in call order, with its exact arguments. */
47
+ this.chainCalls = [];
48
+ }
49
+ record(method, args) {
50
+ this.chainCalls.push({ method, args });
51
+ return this;
52
+ }
53
+ /** Returns the arguments of this chain's first call to `method` (e.g. `"where"`, `"values"`). */
54
+ argsOf(method) {
55
+ return this.chainCalls.find((c) => c.method === method)?.args;
56
+ }
57
+ from(...args) { return this.record('from', args); }
58
+ leftJoin(...args) { return this.record('leftJoin', args); }
59
+ innerJoin(...args) { return this.record('innerJoin', args); }
60
+ where(...args) { return this.record('where', args); }
61
+ orderBy(...args) { return this.record('orderBy', args); }
62
+ groupBy(...args) { return this.record('groupBy', args); }
63
+ limit(...args) { return this.record('limit', args); }
64
+ offset(...args) { return this.record('offset', args); }
65
+ values(...args) { return this.record('values', args); }
66
+ set(...args) { return this.record('set', args); }
67
+ returning(...args) { return this.record('returning', args); }
68
+ as(...args) { return this.record('as', args); }
69
+ then(onfulfilled, onrejected) {
70
+ return Promise.resolve().then(() => this.takeRows()).then(onfulfilled, onrejected);
71
+ }
72
+ }
73
+ /**
74
+ * Fake {@link DrizzleDb} backed by a queue of canned results. Build one with
75
+ * {@link makeFakeDb} rather than calling this constructor directly.
76
+ */
77
+ class FakeDrizzleDb {
78
+ constructor(queue) {
79
+ /** Top-level calls captured for assertions — each entry's `chain` is the same `ChainableQuery` returned to the caller, so its recorded `.where()`/`.values()`/etc. arguments are inspectable via `chain.argsOf(...)`. */
80
+ this.calls = [];
81
+ this.queue = [...queue];
82
+ }
83
+ takeRows(method) {
84
+ const next = this.queue.shift();
85
+ if (!next)
86
+ throw new Error(`FakeDrizzleDb: no queued result left for ${method}()`);
87
+ return next.rows;
88
+ }
89
+ makeChain(method, args) {
90
+ const chain = new ChainableQuery(() => this.takeRows(method));
91
+ this.calls.push({ method, args, chain });
92
+ return chain;
93
+ }
94
+ select(...args) {
95
+ return this.makeChain('select', args);
96
+ }
97
+ insert(...args) {
98
+ return this.makeChain('insert', args);
99
+ }
100
+ update(...args) {
101
+ return this.makeChain('update', args);
102
+ }
103
+ delete(...args) {
104
+ return this.makeChain('delete', args);
105
+ }
106
+ async execute(...args) {
107
+ this.calls.push({ method: 'execute', args });
108
+ const next = this.queue.shift();
109
+ if (!next)
110
+ throw new Error('FakeDrizzleDb: no queued result left for execute()');
111
+ return { rows: next.rows };
112
+ }
113
+ /** CTE definition — never awaited on its own, so it must not touch the queue. */
114
+ $with(_name) {
115
+ return { as: (builder) => builder };
116
+ }
117
+ with(..._ctes) {
118
+ return { select: (...args) => this.select(...args) };
119
+ }
120
+ }
121
+ /**
122
+ * Builds a fake {@link DrizzleDb} that resolves each terminal call
123
+ * (`select`/`insert`/`update`/`delete`/`execute`) to the next entry in
124
+ * `results`, in the exact order your code under test calls them.
125
+ *
126
+ * @param results Queued results, one per terminal call, built with
127
+ * {@link rowsResult}/{@link executeResult}.
128
+ * @returns A fake db to pass wherever your code expects a `DrizzleDb`.
129
+ *
130
+ * @example
131
+ * const db = makeFakeDb([rowsResult([{ id: 1 }])]);
132
+ * const rows = await db.select().from(bonds);
133
+ */
134
+ export function makeFakeDb(results) {
135
+ return new FakeDrizzleDb(results);
136
+ }
137
+ export { rowsResult, executeResult };
@@ -4,4 +4,3 @@ export * from './server';
4
4
  export * from './client';
5
5
  export * from './theme_switcher';
6
6
  export * from './types';
7
- export * from './db';
package/dist/src/index.js CHANGED
@@ -4,4 +4,3 @@ export * from './server';
4
4
  export * from './client';
5
5
  export * from './theme_switcher';
6
6
  export * from './types';
7
- export * from './db';
@@ -1 +1 @@
1
- export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, } from './types';
1
+ export type { CookieAttributes, LocalePrefixMode, Locales, ReturnType, RoutingConfig, TranslationEntry, TranslationObject, TranslatorReturnType, Alternates, changeFrequency, IntlSitemap, CookieConsentRoutingConfig, CookieConsentAnalyticsConfig, CookieConsentCloudflareContext, CookieConsentGetCloudflareContext, DbRoutingConfig, SupabaseDbConfig, } from './types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudflare-next-intl",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Optimized Next Intl Package Special for App Router and Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -184,6 +184,10 @@
184
184
  "./dbHelpers": {
185
185
  "types": "./dist/src/db/helpers.d.ts",
186
186
  "import": "./dist/src/db/helpers.js"
187
+ },
188
+ "./dbTesting": {
189
+ "types": "./dist/src/db/testing.d.ts",
190
+ "import": "./dist/src/db/testing.js"
187
191
  }
188
192
  },
189
193
  "scripts": {