turbine-orm 0.24.0 → 0.26.0
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 +26 -1
- package/dist/cjs/cli/index.js +83 -0
- package/dist/cjs/client.js +14 -0
- package/dist/cjs/dialect.js +24 -0
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/mssql.js +3 -1
- package/dist/cjs/query/batched-loader.js +392 -0
- package/dist/cjs/query/builder.js +548 -60
- package/dist/cjs/query/utils.js +36 -0
- package/dist/cjs/serverless.js +35 -3
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +83 -0
- package/dist/client.d.ts +40 -1
- package/dist/client.js +14 -0
- package/dist/dialect.d.ts +11 -0
- package/dist/dialect.js +24 -0
- package/dist/index-advisor.d.ts +83 -0
- package/dist/index-advisor.js +0 -0
- package/dist/mssql.js +3 -1
- package/dist/query/batched-loader.d.ts +120 -0
- package/dist/query/batched-loader.js +386 -0
- package/dist/query/builder.d.ts +118 -1
- package/dist/query/builder.js +549 -61
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +37 -2
- package/dist/query/utils.d.ts +16 -0
- package/dist/query/utils.js +35 -0
- package/dist/serverless.d.ts +32 -4
- package/dist/serverless.js +35 -3
- package/package.json +1 -1
package/dist/query/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `import { … } from './query/index.js'` is a drop-in replacement for the
|
|
6
6
|
* former monolithic `import { … } from './query.js'`.
|
|
7
7
|
*/
|
|
8
|
-
export type { AggregateArgs, AggregateResult, ArrayFilter, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, HavingClause, JsonFilter, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, SelectResult, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
|
|
8
|
+
export type { AggregateArgs, AggregateResult, ArrayFilter, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, HavingClause, JsonFilter, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, SelectResult, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
|
|
9
9
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
11
|
export type { SqlCacheEntry } from './utils.js';
|
package/dist/query/types.d.ts
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
* All exported type and interface definitions for the query builder module.
|
|
5
5
|
*/
|
|
6
6
|
export type OrderDirection = 'asc' | 'desc';
|
|
7
|
+
/**
|
|
8
|
+
* How a query resolves its `with` relations.
|
|
9
|
+
*
|
|
10
|
+
* - `'join'` (default) — one SQL statement with correlated
|
|
11
|
+
* `json_agg(json_build_object(...))` subqueries. One round-trip; an index
|
|
12
|
+
* seek per parent row when the child FK is indexed.
|
|
13
|
+
* - `'batched'` — run the base query, then ONE flat follow-up query per
|
|
14
|
+
* relation (`WHERE fk = ANY($1)`), stitching children client-side. D levels
|
|
15
|
+
* cost D extra round-trips, but each is a single key-set lookup and rows come
|
|
16
|
+
* back flat — a win when FK columns are unindexed or result sets are huge.
|
|
17
|
+
*
|
|
18
|
+
* Precedence: per-query arg > client `relationLoadStrategy` config > `'join'`.
|
|
19
|
+
*/
|
|
20
|
+
export type RelationLoadStrategy = 'join' | 'batched';
|
|
7
21
|
/** Operator object for advanced where filtering */
|
|
8
22
|
export interface WhereOperator<V = unknown> {
|
|
9
23
|
/**
|
|
@@ -36,7 +50,21 @@ export interface WhereOperator<V = unknown> {
|
|
|
36
50
|
* - A text search filter object ({ search, config? })
|
|
37
51
|
* - A vector distance filter object ({ distance: { to, metric, lt } }) for pgvector columns
|
|
38
52
|
*/
|
|
39
|
-
export type WhereValue<V = unknown> = V | WhereOperator<V> | JsonFilter | ArrayFilter | TextSearchFilter | VectorFilter | null;
|
|
53
|
+
export type WhereValue<V = unknown> = (V extends Array<infer U> ? TypedRelationFilter<U> : V extends Date ? V : V extends object ? V | TypedToOneFilter<V> | WhereClause<V> : V) | WhereOperator<V> | JsonFilter | ArrayFilter | TextSearchFilter | VectorFilter | null;
|
|
54
|
+
/** Relation filter on a to-many relation property. */
|
|
55
|
+
export interface TypedRelationFilter<U> {
|
|
56
|
+
some?: WhereClause<U>;
|
|
57
|
+
every?: WhereClause<U>;
|
|
58
|
+
none?: WhereClause<U>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Relation filter on a to-one relation property. A bare object on a to-one
|
|
62
|
+
* relation key is also accepted at runtime (implicit `is`, Prisma-compatible).
|
|
63
|
+
*/
|
|
64
|
+
export interface TypedToOneFilter<V> {
|
|
65
|
+
is?: WhereClause<V>;
|
|
66
|
+
isNot?: WhereClause<V>;
|
|
67
|
+
}
|
|
40
68
|
/**
|
|
41
69
|
* Where clause type: each field can be a plain value, null, or operator object.
|
|
42
70
|
* Special keys: OR for disjunctive conditions.
|
|
@@ -192,7 +220,7 @@ export type FieldResult<T, S extends Record<string, boolean> | undefined, O exte
|
|
|
192
220
|
* Short-circuits to plain WithResult when neither select nor omit is provided,
|
|
193
221
|
* preserving exact type equality with the pre-narrowing era.
|
|
194
222
|
*/
|
|
195
|
-
export type QueryResult<T, R extends object, W, S extends Record<string, boolean> | undefined, O extends Record<string, boolean> | undefined> = S extends undefined ? O extends undefined ? WithResult<T, R, W> : O extends Record<string, boolean> ? Omit<WithResult<T, R, W>, Extract<keyof T, TrueKeys<O
|
|
223
|
+
export type QueryResult<T, R extends object, W, S extends Record<string, boolean> | undefined, O extends Record<string, boolean> | undefined> = S extends undefined ? O extends undefined ? WithResult<T, R, W> : O extends Record<string, boolean> ? Omit<WithResult<T, R, W>, Exclude<Extract<keyof T, TrueKeys<O>>, keyof W>> : WithResult<T, R, W> : S extends Record<string, boolean> ? Pick<WithResult<T, R, W>, Extract<keyof T, TrueKeys<S>> | Exclude<keyof WithResult<T, R, W>, keyof T> | Extract<keyof W, keyof WithResult<T, R, W>>> : WithResult<T, R, W>;
|
|
196
224
|
export interface FindUniqueArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> {
|
|
197
225
|
where: WhereClause<T>;
|
|
198
226
|
select?: S;
|
|
@@ -200,6 +228,8 @@ export interface FindUniqueArgs<T, R extends object = {}, W extends TypedWithCla
|
|
|
200
228
|
with?: W;
|
|
201
229
|
/** Query timeout in milliseconds. Rejects with an error if exceeded. */
|
|
202
230
|
timeout?: number;
|
|
231
|
+
/** Override the client's relation-loading strategy for this query. See {@link RelationLoadStrategy}. */
|
|
232
|
+
relationLoadStrategy?: RelationLoadStrategy;
|
|
203
233
|
}
|
|
204
234
|
export interface FindManyArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> {
|
|
205
235
|
where?: WhereClause<T>;
|
|
@@ -217,6 +247,8 @@ export interface FindManyArgs<T, R extends object = {}, W extends TypedWithClaus
|
|
|
217
247
|
distinct?: (keyof T & string)[];
|
|
218
248
|
/** Query timeout in milliseconds. Rejects with an error if exceeded. */
|
|
219
249
|
timeout?: number;
|
|
250
|
+
/** Override the client's relation-loading strategy for this query. See {@link RelationLoadStrategy}. */
|
|
251
|
+
relationLoadStrategy?: RelationLoadStrategy;
|
|
220
252
|
}
|
|
221
253
|
export interface FindManyStreamArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> extends FindManyArgs<T, R, W, S, O> {
|
|
222
254
|
/**
|
|
@@ -505,6 +537,9 @@ export interface RelationFilter {
|
|
|
505
537
|
some?: Record<string, unknown>;
|
|
506
538
|
every?: Record<string, unknown>;
|
|
507
539
|
none?: Record<string, unknown>;
|
|
540
|
+
/** To-one relation match (bare objects on to-one keys are implicit `is`). */
|
|
541
|
+
is?: Record<string, unknown>;
|
|
542
|
+
isNot?: Record<string, unknown>;
|
|
508
543
|
}
|
|
509
544
|
/** JSONB query operators for where clauses */
|
|
510
545
|
export interface JsonFilter {
|
package/dist/query/utils.d.ts
CHANGED
|
@@ -65,3 +65,19 @@ export declare const OPERATOR_KEYS: Set<string>;
|
|
|
65
65
|
* For multi-column: `"alias"."col_a" = "parent"."ref_a" AND "alias"."col_b" = "parent"."ref_b"`
|
|
66
66
|
*/
|
|
67
67
|
export declare function buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
|
|
68
|
+
/**
|
|
69
|
+
* Parse a database date-time string deterministically.
|
|
70
|
+
*
|
|
71
|
+
* Postgres `timestamp` (without time zone) values arrive with no offset —
|
|
72
|
+
* both from the driver and from `json_agg`/`json_build_object` subquery JSON
|
|
73
|
+
* (`2026-07-07T17:15:41.896`). JavaScript's `new Date()` interprets such
|
|
74
|
+
* strings in the SERVER'S LOCAL TIME ZONE, so the same row parses to a
|
|
75
|
+
* different instant depending on where the code runs. The universal ORM
|
|
76
|
+
* convention (Prisma, Rails, Django) is to treat offset-less timestamps as
|
|
77
|
+
* UTC — that is also the only interpretation that round-trips: Postgres
|
|
78
|
+
* stores exactly the wall-clock fields you sent.
|
|
79
|
+
*
|
|
80
|
+
* Strings that carry an explicit offset (`timestamptz` output) are parsed
|
|
81
|
+
* as-is.
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseDbDate(value: string): Date;
|
package/dist/query/utils.js
CHANGED
|
@@ -129,3 +129,38 @@ export function buildCorrelation(leftRef, leftColumns, rightRef, rightColumns) {
|
|
|
129
129
|
.map((col, i) => `${leftRef}.${quoteIdent(col)} = ${rightRef}.${quoteIdent(rightCols[i])}`)
|
|
130
130
|
.join(' AND ');
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Matches an explicit timezone suffix on a date-time string: a trailing `Z`
|
|
134
|
+
* or a `±HH`, `±HHMM`, `±HH:MM` offset.
|
|
135
|
+
*/
|
|
136
|
+
const TZ_SUFFIX_RE = /(?:Z|[+-]\d{2}(?::?\d{2})?)$/;
|
|
137
|
+
/**
|
|
138
|
+
* Parse a database date-time string deterministically.
|
|
139
|
+
*
|
|
140
|
+
* Postgres `timestamp` (without time zone) values arrive with no offset —
|
|
141
|
+
* both from the driver and from `json_agg`/`json_build_object` subquery JSON
|
|
142
|
+
* (`2026-07-07T17:15:41.896`). JavaScript's `new Date()` interprets such
|
|
143
|
+
* strings in the SERVER'S LOCAL TIME ZONE, so the same row parses to a
|
|
144
|
+
* different instant depending on where the code runs. The universal ORM
|
|
145
|
+
* convention (Prisma, Rails, Django) is to treat offset-less timestamps as
|
|
146
|
+
* UTC — that is also the only interpretation that round-trips: Postgres
|
|
147
|
+
* stores exactly the wall-clock fields you sent.
|
|
148
|
+
*
|
|
149
|
+
* Strings that carry an explicit offset (`timestamptz` output) are parsed
|
|
150
|
+
* as-is.
|
|
151
|
+
*/
|
|
152
|
+
export function parseDbDate(value) {
|
|
153
|
+
// Date-only values (`2026-07-07`, from `date` columns in json_agg output)
|
|
154
|
+
// have no time to zone-pin — and their `-07` tail must not be read as an
|
|
155
|
+
// offset. JS parses bare ISO dates as UTC midnight already.
|
|
156
|
+
if (!value.includes(':'))
|
|
157
|
+
return new Date(value);
|
|
158
|
+
if (TZ_SUFFIX_RE.test(value)) {
|
|
159
|
+
// JS Date can't parse colon-less (`-0430`) or bare-hour (`+02`) offsets —
|
|
160
|
+
// normalize both to `±HH:MM`. Postgres emits the bare-hour form for
|
|
161
|
+
// whole-hour zones in some text outputs.
|
|
162
|
+
return new Date(value.replace(/([+-]\d{2})(\d{2})$/, '$1:$2').replace(/([+-]\d{2})$/, '$1:00'));
|
|
163
|
+
}
|
|
164
|
+
// normalize `YYYY-MM-DD HH:MM:SS` (driver form) to ISO before pinning UTC
|
|
165
|
+
return new Date(`${value.replace(' ', 'T')}Z`);
|
|
166
|
+
}
|
package/dist/serverless.d.ts
CHANGED
|
@@ -94,12 +94,29 @@ export interface TurbineHttpOptions extends Pick<TurbineConfig, 'logging' | 'def
|
|
|
94
94
|
* manage its own `pg.Pool`. The caller retains ownership of the pool's
|
|
95
95
|
* lifecycle — `db.disconnect()` is a no-op.
|
|
96
96
|
*
|
|
97
|
+
* ## Typed table accessors
|
|
98
|
+
*
|
|
99
|
+
* By default `turbineHttp` returns the base {@link TurbineClient}, so you
|
|
100
|
+
* reach tables through `db.table('users')`. To get the *generated*, fully
|
|
101
|
+
* typed accessors (`db.users.findMany()`) — identical to what the TCP-path
|
|
102
|
+
* `turbine()` factory gives you — pass your generated client type as the
|
|
103
|
+
* `TClient` type argument. The runtime object is the same; the generated
|
|
104
|
+
* subclass only adds `declare readonly` accessor typings, and the base
|
|
105
|
+
* constructor already creates those accessors at runtime for every table in
|
|
106
|
+
* the schema, so the assertion is sound (not a lie about the shape).
|
|
107
|
+
*
|
|
108
|
+
* This closes the "identical typed code across transports" gap: the edge
|
|
109
|
+
* client is now as typed as the direct one, with no `as` casts at the call
|
|
110
|
+
* site.
|
|
111
|
+
*
|
|
112
|
+
* @typeParam TClient - The generated `TurbineClient` subclass (from
|
|
113
|
+
* `./generated/turbine`). Defaults to the base client for back-compat.
|
|
97
114
|
* @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
|
|
98
115
|
* @param schema - Introspected or hand-written schema metadata
|
|
99
116
|
* @param options - Optional logging / defaultLimit / warnOnUnlimited
|
|
100
|
-
* @returns A TurbineClient instance
|
|
117
|
+
* @returns A TurbineClient instance (typed as `TClient`)
|
|
101
118
|
*
|
|
102
|
-
* @example
|
|
119
|
+
* @example Untyped (back-compat) — reach tables via `db.table(...)`
|
|
103
120
|
* ```ts
|
|
104
121
|
* import { Pool } from '@neondatabase/serverless';
|
|
105
122
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
@@ -107,8 +124,19 @@ export interface TurbineHttpOptions extends Pick<TurbineConfig, 'logging' | 'def
|
|
|
107
124
|
*
|
|
108
125
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
109
126
|
* const db = turbineHttp(pool, SCHEMA);
|
|
110
|
-
*
|
|
111
127
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
112
128
|
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Typed — generated accessors, identical to the TCP client
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
133
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
134
|
+
* import type { TurbineClient } from './generated/turbine';
|
|
135
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
136
|
+
*
|
|
137
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
138
|
+
* const db = turbineHttp<TurbineClient>(pool, SCHEMA);
|
|
139
|
+
* const users = await db.users.findMany({ limit: 10 }); // fully typed, no cast
|
|
140
|
+
* ```
|
|
113
141
|
*/
|
|
114
|
-
export declare function turbineHttp(pool: PgCompatPool, schema: SchemaMetadata, options?: TurbineHttpOptions):
|
|
142
|
+
export declare function turbineHttp<TClient extends TurbineClient = TurbineClient>(pool: PgCompatPool, schema: SchemaMetadata, options?: TurbineHttpOptions): TClient;
|
package/dist/serverless.js
CHANGED
|
@@ -86,12 +86,29 @@ import { TurbineClient } from './client.js';
|
|
|
86
86
|
* manage its own `pg.Pool`. The caller retains ownership of the pool's
|
|
87
87
|
* lifecycle — `db.disconnect()` is a no-op.
|
|
88
88
|
*
|
|
89
|
+
* ## Typed table accessors
|
|
90
|
+
*
|
|
91
|
+
* By default `turbineHttp` returns the base {@link TurbineClient}, so you
|
|
92
|
+
* reach tables through `db.table('users')`. To get the *generated*, fully
|
|
93
|
+
* typed accessors (`db.users.findMany()`) — identical to what the TCP-path
|
|
94
|
+
* `turbine()` factory gives you — pass your generated client type as the
|
|
95
|
+
* `TClient` type argument. The runtime object is the same; the generated
|
|
96
|
+
* subclass only adds `declare readonly` accessor typings, and the base
|
|
97
|
+
* constructor already creates those accessors at runtime for every table in
|
|
98
|
+
* the schema, so the assertion is sound (not a lie about the shape).
|
|
99
|
+
*
|
|
100
|
+
* This closes the "identical typed code across transports" gap: the edge
|
|
101
|
+
* client is now as typed as the direct one, with no `as` casts at the call
|
|
102
|
+
* site.
|
|
103
|
+
*
|
|
104
|
+
* @typeParam TClient - The generated `TurbineClient` subclass (from
|
|
105
|
+
* `./generated/turbine`). Defaults to the base client for back-compat.
|
|
89
106
|
* @param pool - Any pg-compatible pool (Neon, Vercel Postgres, etc.)
|
|
90
107
|
* @param schema - Introspected or hand-written schema metadata
|
|
91
108
|
* @param options - Optional logging / defaultLimit / warnOnUnlimited
|
|
92
|
-
* @returns A TurbineClient instance
|
|
109
|
+
* @returns A TurbineClient instance (typed as `TClient`)
|
|
93
110
|
*
|
|
94
|
-
* @example
|
|
111
|
+
* @example Untyped (back-compat) — reach tables via `db.table(...)`
|
|
95
112
|
* ```ts
|
|
96
113
|
* import { Pool } from '@neondatabase/serverless';
|
|
97
114
|
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
@@ -99,10 +116,25 @@ import { TurbineClient } from './client.js';
|
|
|
99
116
|
*
|
|
100
117
|
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
101
118
|
* const db = turbineHttp(pool, SCHEMA);
|
|
102
|
-
*
|
|
103
119
|
* const users = await db.table('users').findMany({ limit: 10 });
|
|
104
120
|
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example Typed — generated accessors, identical to the TCP client
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { Pool } from '@neondatabase/serverless';
|
|
125
|
+
* import { turbineHttp } from 'turbine-orm/serverless';
|
|
126
|
+
* import type { TurbineClient } from './generated/turbine';
|
|
127
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
128
|
+
*
|
|
129
|
+
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
130
|
+
* const db = turbineHttp<TurbineClient>(pool, SCHEMA);
|
|
131
|
+
* const users = await db.users.findMany({ limit: 10 }); // fully typed, no cast
|
|
132
|
+
* ```
|
|
105
133
|
*/
|
|
106
134
|
export function turbineHttp(pool, schema, options = {}) {
|
|
135
|
+
// The generated subclass only layers `declare readonly` accessor typings
|
|
136
|
+
// over the base client; the base constructor materializes those same
|
|
137
|
+
// accessors at runtime (Object.defineProperty per schema table). So the
|
|
138
|
+
// returned instance genuinely has TClient's shape — the assertion is safe.
|
|
107
139
|
return new TurbineClient({ pool, ...options }, schema);
|
|
108
140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Postgres-native TypeScript ORM — runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. One dependency, no WASM engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|