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 +32 -1
- package/dist/src/db/helpers.d.ts +8 -1
- package/dist/src/db/helpers.js +8 -1
- package/dist/src/db/testing.d.ts +115 -0
- package/dist/src/db/testing.js +137 -0
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/types/index.d.ts +1 -1
- package/package.json +5 -1
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
|
|
package/dist/src/db/helpers.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/src/db/helpers.js
CHANGED
|
@@ -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 };
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -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.
|
|
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": {
|