@prisma-next/extension-paradedb 0.5.0-dev.81 → 0.5.0-dev.82

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
@@ -4,21 +4,21 @@ ParadeDB full-text search extension pack for Prisma Next.
4
4
 
5
5
  ## Overview
6
6
 
7
- This extension pack adds support for [ParadeDB](https://docs.paradedb.com/) BM25 full-text search indexes in the contract authoring layer. It keeps BM25 metadata inside extension-owned index `config` payloads, so `contract.json` and `contract.d.ts` carry the full search schema without hard-coding ParadeDB types into core SQL index IR.
7
+ This extension pack registers a `'bm25'` index type with the SQL family's index-type registry, so contracts can author BM25 full-text search indexes via the standard `constraints.index(...)` surface and the Postgres adapter emits `CREATE INDEX ... USING bm25 WITH (...)` DDL.
8
8
 
9
- This is the **contract-only foundation**. Query-plane support (`@@@` operator, `pdb.*` functions) and migration-plane support (`CREATE INDEX ... USING bm25` DDL generation) are planned as follow-up work.
9
+ The v1 surface covers the `key_field` storage parameter only. Per-field tokenizer and column configuration is deferred to expression-index support.
10
10
 
11
11
  ## Responsibilities
12
12
 
13
- - **BM25 Index Authoring**: Typed `bm25.*` field builders for defining BM25 indexes in `contract.ts`
14
- - **Tokenizer Catalog**: `TokenizerId` type union covering all 12 built-in ParadeDB tokenizers
15
- - **Extension Descriptor**: Declares `paradedb/bm25` capability for contract-level feature detection
16
- - **Pack Ref Export**: Ships a pure `/pack` entrypoint for TypeScript contract authoring
13
+ - **bm25 index registration**: declares a `'bm25'` entry via `defineIndexTypes()` carrying an arktype validator for the bm25 options shape
14
+ - **Extension descriptor**: declares the `paradedb/bm25` capability for contract-level feature detection
15
+ - **Pack ref export**: ships a pure `/pack` entrypoint for TypeScript contract authoring
17
16
 
18
17
  ## Dependencies
19
18
 
20
- - **`@prisma-next/contract`**: Core contract types
21
- - **`@prisma-next/contract-authoring`**: Column type descriptor interface
19
+ - **`@prisma-next/sql-contract`**: index-type registry primitive
20
+ - **`@prisma-next/contract`** / **`@prisma-next/contract-authoring`**: core contract types
21
+ - **`arktype`**: option-shape validation
22
22
 
23
23
  ## Installation
24
24
 
@@ -28,15 +28,14 @@ pnpm add @prisma-next/extension-paradedb
28
28
 
29
29
  ## Usage
30
30
 
31
- ### Contract Definition
31
+ ### Contract definition
32
32
 
33
- Define BM25 indexes on your tables using `bm25` field builders plus `bm25Index()`:
33
+ Author bm25 indexes via the standard `constraints.index(...)` surface; the registered `'bm25'` entry narrows `options` per-`type`:
34
34
 
35
35
  ```typescript
36
- import { int4Column, textColumn, jsonbColumn } from '@prisma-next/adapter-postgres/column-types';
36
+ import { int4Column, textColumn } from '@prisma-next/adapter-postgres/column-types';
37
37
  import sqlFamily from '@prisma-next/family-sql/pack';
38
38
  import { defineContract, field, model } from '@prisma-next/sql-contract-ts/contract-builder';
39
- import { bm25, bm25Index } from '@prisma-next/extension-paradedb/index-types';
40
39
  import paradedb from '@prisma-next/extension-paradedb/pack';
41
40
  import postgres from '@prisma-next/target-postgres/pack';
42
41
 
@@ -48,121 +47,40 @@ export const contract = defineContract({
48
47
  Item: model('Item', {
49
48
  fields: {
50
49
  id: field.column(int4Column).id(),
51
- description: field.column(textColumn),
52
- category: field.column(textColumn),
53
- rating: field.column(int4Column),
54
- metadata: field.column(jsonbColumn),
50
+ body: field.column(textColumn),
55
51
  },
56
- }).sql({
52
+ }).sql(({ cols, constraints }) => ({
57
53
  table: 'items',
58
54
  indexes: [
59
- bm25Index({
60
- keyField: 'id',
61
- fields: [
62
- bm25.text('description', { tokenizer: 'simple', stemmer: 'english' }),
63
- bm25.text('category'),
64
- bm25.numeric('rating'),
65
- bm25.json('metadata', { tokenizer: 'ngram', min: 2, max: 3 }),
66
- ],
67
- name: 'search_idx',
55
+ constraints.index([cols.body], {
56
+ name: 'item_body_bm25_idx',
57
+ type: 'bm25',
58
+ options: { key_field: 'id' },
68
59
  }),
69
60
  ],
70
- }),
61
+ })),
71
62
  },
72
63
  });
73
64
  ```
74
65
 
75
- ### Field Builders
66
+ ### key_field
76
67
 
77
- The `bm25` namespace provides typed field builders that produce `Bm25FieldConfig` objects:
78
-
79
- | Builder | Description | Tokenizer support |
80
- |---------|-------------|-------------------|
81
- | `bm25.text(column, opts?)` | Text field (`text`, `varchar`) | Yes — any tokenizer + stemmer, remove_emojis |
82
- | `bm25.numeric(column)` | Numeric field (filterable, sortable) | No |
83
- | `bm25.boolean(column)` | Boolean field | No |
84
- | `bm25.json(column, opts?)` | JSON/JSONB field | Yes — tokenizer + ngram params |
85
- | `bm25.datetime(column)` | Timestamp/date field | No |
86
- | `bm25.range(column)` | Range field | No |
87
- | `bm25.expression(sql, opts)` | Raw SQL expression | Yes — `alias` required |
88
-
89
- ### Expression-Based Fields
90
-
91
- For computed or JSON sub-field indexing, use `bm25.expression()` with a raw SQL string:
92
-
93
- ```typescript
94
- .index(
95
- bm25Index({
96
- keyField: 'id',
97
- fields: [
98
- bm25.text('description'),
99
- bm25.expression("description || ' ' || category", {
100
- alias: 'concat',
101
- tokenizer: 'simple',
102
- }),
103
- bm25.expression("(metadata->>'color')", {
104
- alias: 'meta_color',
105
- tokenizer: 'ngram',
106
- min: 2,
107
- max: 3,
108
- }),
109
- ],
110
- }),
111
- )
112
- ```
113
-
114
- ### keyField Behavior
115
-
116
- ParadeDB BM25 indexes require a `key_field` — a unique column that identifies each document:
117
-
118
- - **Required**: Set `keyField` explicitly in `bm25Index(...)`.
119
- - **Recommended**: Use the table primary key in most cases.
120
- - **Override**: You can choose another unique column when needed.
121
-
122
- ```typescript
123
- .index(bm25Index({ keyField: 'id', fields: [bm25.text('body')] }))
124
- .index(bm25Index({ keyField: 'uuid', fields: [bm25.text('body')] }))
125
- ```
126
-
127
- ## Tokenizers
128
-
129
- All 12 built-in ParadeDB tokenizers are available via the `TokenizerId` type:
130
-
131
- | Tokenizer | Description |
132
- |-----------|-------------|
133
- | `unicode_words` | Default. Unicode word boundaries (UAX #29). Lowercases. |
134
- | `simple` | Splits on non-alphanumeric. Lowercases. |
135
- | `ngram` | Character n-grams of configurable length. |
136
- | `icu` | ICU Unicode standard segmentation. Multilingual. |
137
- | `regex_pattern` | Regex-based tokenization. |
138
- | `source_code` | camelCase / snake_case splitting. |
139
- | `literal` | No splitting. Exact match, sort, aggregation. |
140
- | `literal_normalized` | Literal + lowercase + token filters. |
141
- | `whitespace` | Whitespace splitting + lowercase. |
142
- | `chinese_compatible` | CJK-aware word segmentation. |
143
- | `jieba` | Chinese segmentation via Jieba. |
144
- | `lindera` | Japanese/Korean/Chinese via Lindera. |
68
+ ParadeDB BM25 indexes require a `key_field` a unique column that identifies each document. It is required, must be a string, and is typically (but not always) the table's primary key.
145
69
 
146
70
  ## Capabilities
147
71
 
148
- The extension declares:
149
-
150
- - `paradedb/bm25`: Indicates support for BM25 full-text search indexes
151
-
152
- ## Not Yet Implemented
72
+ - `paradedb/bm25` — indicates support for BM25 full-text search indexes
153
73
 
154
- The following are planned for follow-up work:
74
+ ## Not yet implemented
155
75
 
156
- - **Query plane**: `@@@` operator support in the sql-orm query builder
157
- - **Query plane**: `pdb.*` query builder functions (match, term, phrase, fuzzy, etc.)
158
- - **Migration plane**: `CREATE INDEX ... USING bm25` DDL generation from contract diffs
159
- - **Runtime**: Scoring, aggregation, and highlight functions
160
- - **Database dependencies**: `CREATE EXTENSION pg_search` via migration planner
76
+ - Per-column / per-expression tokenizer configuration (deferred to expression-index support)
77
+ - `@@@` operator and `pdb.*` query builder functions
78
+ - `CREATE EXTENSION pg_search` via migration planner
79
+ - Scoring, aggregation, and highlight functions
161
80
 
162
81
  ## References
163
82
 
164
83
  - [ParadeDB documentation](https://docs.paradedb.com/)
165
84
  - [ParadeDB CREATE INDEX](https://docs.paradedb.com/documentation/indexing/create-index)
166
- - [ParadeDB Tokenizers](https://docs.paradedb.com/documentation/tokenizers/overview)
167
- - [pg_search source](https://github.com/paradedb/paradedb/tree/main/pg_search)
85
+ - [ADR 210 — Index-type registry](../../../docs/architecture%20docs/adrs/ADR%20210%20-%20Index-type%20registry.md)
168
86
  - [Prisma Next Architecture Overview](../../../docs/Architecture%20Overview.md)
@@ -1,3 +1,5 @@
1
+ import * as _$_prisma_next_sql_contract_index_types0 from "@prisma-next/sql-contract/index-types";
2
+
1
3
  //#region src/core/descriptor-meta.d.ts
2
4
  declare const paradedbPackMeta: {
3
5
  readonly kind: "extension";
@@ -10,6 +12,11 @@ declare const paradedbPackMeta: {
10
12
  readonly 'paradedb/bm25': true;
11
13
  };
12
14
  };
15
+ readonly indexTypes: _$_prisma_next_sql_contract_index_types0.IndexTypeBuilder<Record<never, never> & Record<"bm25", {
16
+ readonly options: {
17
+ key_field: string;
18
+ };
19
+ }>>;
13
20
  };
14
21
  //#endregion
15
22
  export { paradedbPackMeta };
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/descriptor-meta.ts"],"mappings":";cAEa,gBAAA;EAAA"}
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/descriptor-meta.ts"],"mappings":";;;cAGa,gBAAA;EAAA"}
package/dist/control.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as paradedbPackMeta } from "./descriptor-meta-B6-NIHhB.mjs";
1
+ import { t as paradedbPackMeta } from "./descriptor-meta-BF_fQseQ.mjs";
2
2
  export { paradedbPackMeta };
@@ -1,3 +1,4 @@
1
+ import { t as paradedbIndexTypes } from "./index-types-BWFfNqUb.mjs";
1
2
  //#endregion
2
3
  //#region src/core/descriptor-meta.ts
3
4
  const paradedbPackMeta = {
@@ -6,9 +7,10 @@ const paradedbPackMeta = {
6
7
  familyId: "sql",
7
8
  targetId: "postgres",
8
9
  version: "0.0.1",
9
- capabilities: { postgres: { "paradedb/bm25": true } }
10
+ capabilities: { postgres: { "paradedb/bm25": true } },
11
+ indexTypes: paradedbIndexTypes
10
12
  };
11
13
  //#endregion
12
14
  export { paradedbPackMeta as t };
13
15
 
14
- //# sourceMappingURL=descriptor-meta-B6-NIHhB.mjs.map
16
+ //# sourceMappingURL=descriptor-meta-BF_fQseQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor-meta-BF_fQseQ.mjs","names":[],"sources":["../src/core/constants.ts","../src/core/descriptor-meta.ts"],"sourcesContent":["/**\n * Extension ID for ParadeDB pg_search.\n */\nexport const PARADEDB_EXTENSION_ID = 'paradedb' as const;\n","import { paradedbIndexTypes } from '../types/index-types';\nimport { PARADEDB_EXTENSION_ID } from './constants';\n\nexport const paradedbPackMeta = {\n kind: 'extension',\n id: PARADEDB_EXTENSION_ID,\n familyId: 'sql',\n targetId: 'postgres',\n version: '0.0.1',\n capabilities: {\n postgres: {\n 'paradedb/bm25': true,\n },\n },\n indexTypes: paradedbIndexTypes,\n} as const;\n"],"mappings":";;;ACGA,MAAa,mBAAmB;CAC9B,MAAM;CACN,IAAI;CACJ,UAAU;CACV,UAAU;CACV,SAAS;CACT,cAAc,EACZ,UAAU,EACR,iBAAiB,MAClB,EACF;CACD,YAAY;CACb"}
@@ -0,0 +1,11 @@
1
+ import { defineIndexTypes } from "@prisma-next/sql-contract/index-types";
2
+ import { type } from "arktype";
3
+ //#region src/types/index-types.ts
4
+ const paradedbIndexTypes = defineIndexTypes().add("bm25", { options: type({
5
+ "+": "reject",
6
+ key_field: "string"
7
+ }) });
8
+ //#endregion
9
+ export { paradedbIndexTypes as t };
10
+
11
+ //# sourceMappingURL=index-types-BWFfNqUb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-types-BWFfNqUb.mjs","names":[],"sources":["../src/types/index-types.ts"],"sourcesContent":["import { defineIndexTypes } from '@prisma-next/sql-contract/index-types';\nimport { type } from 'arktype';\n\nexport const paradedbIndexTypes = defineIndexTypes().add('bm25', {\n options: type({\n '+': 'reject',\n key_field: 'string',\n }),\n});\n\nexport type IndexTypes = typeof paradedbIndexTypes.IndexTypes;\nexport type Bm25IndexOptions = IndexTypes['bm25']['options'];\n"],"mappings":";;;AAGA,MAAa,qBAAqB,kBAAkB,CAAC,IAAI,QAAQ,EAC/D,SAAS,KAAK;CACZ,KAAK;CACL,WAAW;CACZ,CAAC,EACH,CAAC"}
@@ -1,101 +1,13 @@
1
- import { IndexDef } from "@prisma-next/contract-authoring";
1
+ import * as _$_prisma_next_sql_contract_index_types0 from "@prisma-next/sql-contract/index-types";
2
2
 
3
- //#region src/core/constants.d.ts
4
- /**
5
- * Built-in ParadeDB tokenizer IDs.
6
- * These correspond to the `pdb.*` casting syntax in `CREATE INDEX ... USING bm25`.
7
- */
8
- type TokenizerId = 'unicode_words' | 'simple' | 'ngram' | 'icu' | 'regex_pattern' | 'source_code' | 'literal' | 'literal_normalized' | 'whitespace' | 'chinese_compatible' | 'jieba' | 'lindera';
9
- //#endregion
10
3
  //#region src/types/index-types.d.ts
11
- /**
12
- * BM25 field config for a table column.
13
- */
14
- type Bm25ColumnFieldConfig = {
15
- readonly column: string;
16
- readonly expression?: never;
17
- readonly tokenizer?: string;
18
- readonly tokenizerParams?: Record<string, unknown>;
19
- readonly alias?: string;
20
- };
21
- /**
22
- * BM25 field config for a SQL expression.
23
- */
24
- type Bm25ExpressionFieldConfig = {
25
- readonly expression: string;
26
- readonly column?: never;
27
- readonly alias: string;
28
- readonly tokenizer?: string;
29
- readonly tokenizerParams?: Record<string, unknown>;
30
- };
31
- /**
32
- * BM25 field config union.
33
- */
34
- type Bm25FieldConfig = Bm25ColumnFieldConfig | Bm25ExpressionFieldConfig;
35
- /**
36
- * BM25 index configuration payload stored in `IndexDef.config`.
37
- */
38
- type Bm25IndexConfig = {
39
- readonly keyField: string;
40
- readonly fields: readonly Bm25FieldConfig[];
41
- };
42
- /**
43
- * Options for a BM25 text field (text, varchar columns).
44
- */
45
- type Bm25TextFieldOptions = {
46
- readonly tokenizer?: TokenizerId | (string & {});
47
- readonly stemmer?: string;
48
- readonly alias?: string;
49
- readonly remove_emojis?: boolean;
50
- };
51
- /**
52
- * Options for a BM25 JSON field (json, jsonb columns).
53
- */
54
- type Bm25JsonFieldOptions = {
55
- readonly tokenizer?: TokenizerId | (string & {});
56
- readonly alias?: string; /** Ngram-specific params when tokenizer is 'ngram'. */
57
- readonly min?: number;
58
- readonly max?: number;
59
- };
60
- /**
61
- * Options for a BM25 expression-based field.
62
- */
63
- type Bm25ExpressionFieldOptions = {
64
- readonly alias: string;
65
- readonly tokenizer?: TokenizerId | (string & {});
66
- readonly min?: number;
67
- readonly max?: number;
68
- readonly stemmer?: string;
69
- readonly pattern?: string;
70
- };
71
- /**
72
- * Options for constructing a BM25 index definition.
73
- */
74
- type Bm25IndexOptions = {
75
- readonly keyField: string;
76
- readonly fields: readonly Bm25FieldConfig[];
77
- readonly name?: string;
78
- };
79
- /**
80
- * Typed BM25 field builders.
81
- * These produce `Bm25FieldConfig` objects for use in `bm25Index()`.
82
- */
83
- declare const bm25: {
84
- /** Text field with optional tokenizer config. */readonly text: (column: string, opts?: Bm25TextFieldOptions) => Bm25FieldConfig; /** Numeric field (filterable, sortable in BM25). */
85
- readonly numeric: (column: string) => Bm25FieldConfig; /** Boolean field. */
86
- readonly boolean: (column: string) => Bm25FieldConfig; /** JSON/JSONB field with optional tokenizer config. */
87
- readonly json: (column: string, opts?: Bm25JsonFieldOptions) => Bm25FieldConfig; /** Datetime (timestamp/date) field. */
88
- readonly datetime: (column: string) => Bm25FieldConfig; /** Range field. */
89
- readonly range: (column: string) => Bm25FieldConfig; /** Raw SQL expression field. `alias` is required. */
90
- readonly expression: (sql: string, opts: Bm25ExpressionFieldOptions) => Bm25FieldConfig;
91
- };
92
- /**
93
- * Creates a generic index definition with a ParadeDB BM25 payload.
94
- *
95
- * `columns` only includes real table columns so core index validation remains
96
- * target-agnostic. Expression fields stay in extension-owned `config.fields`.
97
- */
98
- declare function bm25Index(opts: Bm25IndexOptions): IndexDef;
4
+ declare const paradedbIndexTypes: _$_prisma_next_sql_contract_index_types0.IndexTypeBuilder<Record<never, never> & Record<"bm25", {
5
+ readonly options: {
6
+ key_field: string;
7
+ };
8
+ }>>;
9
+ type IndexTypes = typeof paradedbIndexTypes.IndexTypes;
10
+ type Bm25IndexOptions = IndexTypes['bm25']['options'];
99
11
  //#endregion
100
- export { type Bm25ColumnFieldConfig, type Bm25ExpressionFieldConfig, type Bm25ExpressionFieldOptions, type Bm25FieldConfig, type Bm25IndexConfig, type Bm25IndexOptions, type Bm25JsonFieldOptions, type Bm25TextFieldOptions, type TokenizerId, bm25, bm25Index };
12
+ export { type Bm25IndexOptions, type IndexTypes, paradedbIndexTypes };
101
13
  //# sourceMappingURL=index-types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-types.d.mts","names":[],"sources":["../src/core/constants.ts","../src/types/index-types.ts"],"mappings":";;;;;;;KASY,WAAA;;;;AAAZ;;KCHY,qBAAA;EAAA,SACD,MAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;EAAA,SACA,eAAA,GAAkB,MAAA;EAAA,SAClB,KAAA;AAAA;;;;KAMC,yBAAA;EAAA,SACD,UAAA;EAAA,SACA,MAAA;EAAA,SACA,KAAA;EAAA,SACA,SAAA;EAAA,SACA,eAAA,GAAkB,MAAA;AAAA;;AAL7B;;KAWY,eAAA,GAAkB,qBAAA,GAAwB,yBAAA;;;;KAK1C,eAAA;EAAA,SACD,QAAA;EAAA,SACA,MAAA,WAAiB,eAAA;AAAA;;;AAP5B;KAaY,oBAAA;EAAA,SACD,SAAA,GAAY,WAAA;EAAA,SACZ,OAAA;EAAA,SACA,KAAA;EAAA,SACA,aAAA;AAAA;;;;KAMC,oBAAA;EAAA,SACD,SAAA,GAAY,WAAA;EAAA,SACZ,KAAA,WAlBgC;EAAA,SAoBhC,GAAA;EAAA,SACA,GAAA;AAAA;;;;KAMC,0BAAA;EAAA,SACD,KAAA;EAAA,SACA,SAAA,GAAY,WAAA;EAAA,SACZ,GAAA;EAAA,SACA,GAAA;EAAA,SACA,OAAA;EAAA,SACA,OAAA;AAAA;;;;KAWC,gBAAA;EAAA,SACD,QAAA;EAAA,SACA,MAAA,WAAiB,eAAA;EAAA,SACjB,IAAA;AAAA;AApBX;;;;AAAA,cA2Ba,IAAA;EAzBF,iFA2BU,IAAA,GAAS,oBAAA,KAAuB,eAAA,EA1B1C;EAAA,sCAsCgB,eAAA,EApChB;EAAA,sCAyCgB,eAAA,EAxCT;EAAA,gCA6CG,IAAA,GAAS,oBAAA,KAAuB,eAAA,EAlCzC;EAAA,uCA2CgB,eAAA;sCAKH,eAAA,EA/Cd;EAAA,mCAoDa,IAAA,EAAQ,0BAAA,KAA6B,eAAA;AAAA;;;;AA3C7D;;;iBA+DgB,SAAA,CAAU,IAAA,EAAM,gBAAA,GAAmB,QAAA"}
1
+ {"version":3,"file":"index-types.d.mts","names":[],"sources":["../src/types/index-types.ts"],"mappings":";;;cAGa,kBAAA,EAAkB,wCAAA,CAAA,gBAAA,CAAA,MAAA,iBAAA,MAAA;EAAA;;;;KAOnB,UAAA,UAAoB,kBAAA,CAAmB,UAAA;AAAA,KACvC,gBAAA,GAAmB,UAAA"}
@@ -1,91 +1,2 @@
1
- //#region src/types/index-types.ts
2
- /**
3
- * Typed BM25 field builders.
4
- * These produce `Bm25FieldConfig` objects for use in `bm25Index()`.
5
- */
6
- const bm25 = {
7
- /** Text field with optional tokenizer config. */
8
- text(column, opts) {
9
- return {
10
- column,
11
- ...buildTokenizerConfig(opts?.tokenizer, {
12
- stemmer: opts?.stemmer,
13
- remove_emojis: opts?.remove_emojis
14
- }),
15
- ...opts?.alias !== void 0 && { alias: opts.alias }
16
- };
17
- },
18
- /** Numeric field (filterable, sortable in BM25). */
19
- numeric(column) {
20
- return { column };
21
- },
22
- /** Boolean field. */
23
- boolean(column) {
24
- return { column };
25
- },
26
- /** JSON/JSONB field with optional tokenizer config. */
27
- json(column, opts) {
28
- return {
29
- column,
30
- ...buildTokenizerConfig(opts?.tokenizer, {
31
- min: opts?.min,
32
- max: opts?.max
33
- }),
34
- ...opts?.alias !== void 0 && { alias: opts.alias }
35
- };
36
- },
37
- /** Datetime (timestamp/date) field. */
38
- datetime(column) {
39
- return { column };
40
- },
41
- /** Range field. */
42
- range(column) {
43
- return { column };
44
- },
45
- /** Raw SQL expression field. `alias` is required. */
46
- expression(sql, opts) {
47
- return {
48
- expression: sql,
49
- alias: opts.alias,
50
- ...buildTokenizerConfig(opts.tokenizer, {
51
- min: opts.min,
52
- max: opts.max,
53
- stemmer: opts.stemmer,
54
- pattern: opts.pattern
55
- })
56
- };
57
- }
58
- };
59
- /**
60
- * Creates a generic index definition with a ParadeDB BM25 payload.
61
- *
62
- * `columns` only includes real table columns so core index validation remains
63
- * target-agnostic. Expression fields stay in extension-owned `config.fields`.
64
- */
65
- function bm25Index(opts) {
66
- return {
67
- columns: opts.fields.flatMap((field) => "column" in field ? [field.column] : []),
68
- ...opts.name !== void 0 && { name: opts.name },
69
- using: "bm25",
70
- config: {
71
- keyField: opts.keyField,
72
- fields: opts.fields
73
- }
74
- };
75
- }
76
- /**
77
- * Builds `{ tokenizer, tokenizerParams? }` from a tokenizer ID and a bag of params.
78
- * Filters out undefined values and omits `tokenizerParams` when empty.
79
- */
80
- function buildTokenizerConfig(tokenizer, params) {
81
- if (!tokenizer) return {};
82
- const filtered = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
83
- return {
84
- tokenizer,
85
- ...Object.keys(filtered).length > 0 && { tokenizerParams: filtered }
86
- };
87
- }
88
- //#endregion
89
- export { bm25, bm25Index };
90
-
91
- //# sourceMappingURL=index-types.mjs.map
1
+ import { t as paradedbIndexTypes } from "./index-types-BWFfNqUb.mjs";
2
+ export { paradedbIndexTypes };
package/dist/pack.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as _$_prisma_next_sql_contract_index_types0 from "@prisma-next/sql-contract/index-types";
2
+
1
3
  //#region src/exports/pack.d.ts
2
4
  declare const paradedbPack: {
3
5
  readonly kind: "extension";
@@ -10,6 +12,11 @@ declare const paradedbPack: {
10
12
  readonly 'paradedb/bm25': true;
11
13
  };
12
14
  };
15
+ readonly indexTypes: _$_prisma_next_sql_contract_index_types0.IndexTypeBuilder<Record<never, never> & Record<"bm25", {
16
+ readonly options: {
17
+ key_field: string;
18
+ };
19
+ }>>;
13
20
  };
14
21
  //#endregion
15
22
  export { paradedbPack as default };
@@ -1 +1 @@
1
- {"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";cAEM,YAAA;EAAA"}
1
+ {"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";;;cAEM,YAAA;EAAA"}
package/dist/pack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as paradedbPackMeta } from "./descriptor-meta-B6-NIHhB.mjs";
1
+ import { t as paradedbPackMeta } from "./descriptor-meta-BF_fQseQ.mjs";
2
2
  //#region src/exports/pack.ts
3
3
  const paradedbPack = paradedbPackMeta;
4
4
  //#endregion
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@prisma-next/extension-paradedb",
3
- "version": "0.5.0-dev.81",
3
+ "version": "0.5.0-dev.82",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "dependencies": {
8
- "@prisma-next/contract": "0.5.0-dev.81",
9
- "@prisma-next/contract-authoring": "0.5.0-dev.81"
8
+ "arktype": "^2.1.25",
9
+ "@prisma-next/sql-contract": "0.5.0-dev.82",
10
+ "@prisma-next/contract-authoring": "0.5.0-dev.82",
11
+ "@prisma-next/contract": "0.5.0-dev.82"
10
12
  },
11
13
  "devDependencies": {
12
14
  "tsdown": "0.22.0",
13
15
  "typescript": "5.9.3",
14
16
  "vitest": "4.1.5",
15
- "@prisma-next/tsdown": "0.0.0",
16
- "@prisma-next/tsconfig": "0.0.0"
17
+ "@prisma-next/tsconfig": "0.0.0",
18
+ "@prisma-next/tsdown": "0.0.0"
17
19
  },
18
20
  "files": [
19
21
  "dist",
@@ -2,21 +2,3 @@
2
2
  * Extension ID for ParadeDB pg_search.
3
3
  */
4
4
  export const PARADEDB_EXTENSION_ID = 'paradedb' as const;
5
-
6
- /**
7
- * Built-in ParadeDB tokenizer IDs.
8
- * These correspond to the `pdb.*` casting syntax in `CREATE INDEX ... USING bm25`.
9
- */
10
- export type TokenizerId =
11
- | 'unicode_words'
12
- | 'simple'
13
- | 'ngram'
14
- | 'icu'
15
- | 'regex_pattern'
16
- | 'source_code'
17
- | 'literal'
18
- | 'literal_normalized'
19
- | 'whitespace'
20
- | 'chinese_compatible'
21
- | 'jieba'
22
- | 'lindera';
@@ -1,3 +1,4 @@
1
+ import { paradedbIndexTypes } from '../types/index-types';
1
2
  import { PARADEDB_EXTENSION_ID } from './constants';
2
3
 
3
4
  export const paradedbPackMeta = {
@@ -11,4 +12,5 @@ export const paradedbPackMeta = {
11
12
  'paradedb/bm25': true,
12
13
  },
13
14
  },
15
+ indexTypes: paradedbIndexTypes,
14
16
  } as const;
@@ -1,12 +1,2 @@
1
- export type { TokenizerId } from '../core/constants';
2
- export type {
3
- Bm25ColumnFieldConfig,
4
- Bm25ExpressionFieldConfig,
5
- Bm25ExpressionFieldOptions,
6
- Bm25FieldConfig,
7
- Bm25IndexConfig,
8
- Bm25IndexOptions,
9
- Bm25JsonFieldOptions,
10
- Bm25TextFieldOptions,
11
- } from '../types/index-types';
12
- export { bm25, bm25Index } from '../types/index-types';
1
+ export type { Bm25IndexOptions, IndexTypes } from '../types/index-types';
2
+ export { paradedbIndexTypes } from '../types/index-types';
@@ -1,179 +1,12 @@
1
- import type { IndexDef } from '@prisma-next/contract-authoring';
2
- import type { TokenizerId } from '../core/constants';
3
-
4
- /**
5
- * BM25 field config for a table column.
6
- */
7
- export type Bm25ColumnFieldConfig = {
8
- readonly column: string;
9
- readonly expression?: never;
10
- readonly tokenizer?: string;
11
- readonly tokenizerParams?: Record<string, unknown>;
12
- readonly alias?: string;
13
- };
14
-
15
- /**
16
- * BM25 field config for a SQL expression.
17
- */
18
- export type Bm25ExpressionFieldConfig = {
19
- readonly expression: string;
20
- readonly column?: never;
21
- readonly alias: string;
22
- readonly tokenizer?: string;
23
- readonly tokenizerParams?: Record<string, unknown>;
24
- };
25
-
26
- /**
27
- * BM25 field config union.
28
- */
29
- export type Bm25FieldConfig = Bm25ColumnFieldConfig | Bm25ExpressionFieldConfig;
30
-
31
- /**
32
- * BM25 index configuration payload stored in `IndexDef.config`.
33
- */
34
- export type Bm25IndexConfig = {
35
- readonly keyField: string;
36
- readonly fields: readonly Bm25FieldConfig[];
37
- };
38
-
39
- /**
40
- * Options for a BM25 text field (text, varchar columns).
41
- */
42
- export type Bm25TextFieldOptions = {
43
- readonly tokenizer?: TokenizerId | (string & {});
44
- readonly stemmer?: string;
45
- readonly alias?: string;
46
- readonly remove_emojis?: boolean;
47
- };
48
-
49
- /**
50
- * Options for a BM25 JSON field (json, jsonb columns).
51
- */
52
- export type Bm25JsonFieldOptions = {
53
- readonly tokenizer?: TokenizerId | (string & {});
54
- readonly alias?: string;
55
- /** Ngram-specific params when tokenizer is 'ngram'. */
56
- readonly min?: number;
57
- readonly max?: number;
58
- };
59
-
60
- /**
61
- * Options for a BM25 expression-based field.
62
- */
63
- export type Bm25ExpressionFieldOptions = {
64
- readonly alias: string;
65
- readonly tokenizer?: TokenizerId | (string & {});
66
- readonly min?: number;
67
- readonly max?: number;
68
- readonly stemmer?: string;
69
- readonly pattern?: string;
70
- };
71
-
72
- type TokenizerConfig = {
73
- readonly tokenizer?: string;
74
- readonly tokenizerParams?: Record<string, unknown>;
75
- };
76
-
77
- /**
78
- * Options for constructing a BM25 index definition.
79
- */
80
- export type Bm25IndexOptions = {
81
- readonly keyField: string;
82
- readonly fields: readonly Bm25FieldConfig[];
83
- readonly name?: string;
84
- };
85
-
86
- /**
87
- * Typed BM25 field builders.
88
- * These produce `Bm25FieldConfig` objects for use in `bm25Index()`.
89
- */
90
- export const bm25 = {
91
- /** Text field with optional tokenizer config. */
92
- text(column: string, opts?: Bm25TextFieldOptions): Bm25FieldConfig {
93
- return {
94
- column,
95
- ...buildTokenizerConfig(opts?.tokenizer, {
96
- stemmer: opts?.stemmer,
97
- remove_emojis: opts?.remove_emojis,
98
- }),
99
- ...(opts?.alias !== undefined && { alias: opts.alias }),
100
- };
101
- },
102
-
103
- /** Numeric field (filterable, sortable in BM25). */
104
- numeric(column: string): Bm25FieldConfig {
105
- return { column };
106
- },
107
-
108
- /** Boolean field. */
109
- boolean(column: string): Bm25FieldConfig {
110
- return { column };
111
- },
112
-
113
- /** JSON/JSONB field with optional tokenizer config. */
114
- json(column: string, opts?: Bm25JsonFieldOptions): Bm25FieldConfig {
115
- return {
116
- column,
117
- ...buildTokenizerConfig(opts?.tokenizer, { min: opts?.min, max: opts?.max }),
118
- ...(opts?.alias !== undefined && { alias: opts.alias }),
119
- };
120
- },
121
-
122
- /** Datetime (timestamp/date) field. */
123
- datetime(column: string): Bm25FieldConfig {
124
- return { column };
125
- },
126
-
127
- /** Range field. */
128
- range(column: string): Bm25FieldConfig {
129
- return { column };
130
- },
131
-
132
- /** Raw SQL expression field. `alias` is required. */
133
- expression(sql: string, opts: Bm25ExpressionFieldOptions): Bm25FieldConfig {
134
- return {
135
- expression: sql,
136
- alias: opts.alias,
137
- ...buildTokenizerConfig(opts.tokenizer, {
138
- min: opts.min,
139
- max: opts.max,
140
- stemmer: opts.stemmer,
141
- pattern: opts.pattern,
142
- }),
143
- };
144
- },
145
- } as const;
146
-
147
- /**
148
- * Creates a generic index definition with a ParadeDB BM25 payload.
149
- *
150
- * `columns` only includes real table columns so core index validation remains
151
- * target-agnostic. Expression fields stay in extension-owned `config.fields`.
152
- */
153
- export function bm25Index(opts: Bm25IndexOptions): IndexDef {
154
- return {
155
- columns: opts.fields.flatMap((field) => ('column' in field ? [field.column] : [])),
156
- ...(opts.name !== undefined && { name: opts.name }),
157
- using: 'bm25',
158
- config: {
159
- keyField: opts.keyField,
160
- fields: opts.fields,
161
- } satisfies Bm25IndexConfig,
162
- };
163
- }
164
-
165
- /**
166
- * Builds `{ tokenizer, tokenizerParams? }` from a tokenizer ID and a bag of params.
167
- * Filters out undefined values and omits `tokenizerParams` when empty.
168
- */
169
- function buildTokenizerConfig(
170
- tokenizer: string | undefined,
171
- params: Record<string, unknown>,
172
- ): TokenizerConfig {
173
- if (!tokenizer) return {};
174
- const filtered = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined));
175
- return {
176
- tokenizer,
177
- ...(Object.keys(filtered).length > 0 && { tokenizerParams: filtered }),
178
- };
179
- }
1
+ import { defineIndexTypes } from '@prisma-next/sql-contract/index-types';
2
+ import { type } from 'arktype';
3
+
4
+ export const paradedbIndexTypes = defineIndexTypes().add('bm25', {
5
+ options: type({
6
+ '+': 'reject',
7
+ key_field: 'string',
8
+ }),
9
+ });
10
+
11
+ export type IndexTypes = typeof paradedbIndexTypes.IndexTypes;
12
+ export type Bm25IndexOptions = IndexTypes['bm25']['options'];
@@ -1 +0,0 @@
1
- {"version":3,"file":"descriptor-meta-B6-NIHhB.mjs","names":[],"sources":["../src/core/constants.ts","../src/core/descriptor-meta.ts"],"sourcesContent":["/**\n * Extension ID for ParadeDB pg_search.\n */\nexport const PARADEDB_EXTENSION_ID = 'paradedb' as const;\n\n/**\n * Built-in ParadeDB tokenizer IDs.\n * These correspond to the `pdb.*` casting syntax in `CREATE INDEX ... USING bm25`.\n */\nexport type TokenizerId =\n | 'unicode_words'\n | 'simple'\n | 'ngram'\n | 'icu'\n | 'regex_pattern'\n | 'source_code'\n | 'literal'\n | 'literal_normalized'\n | 'whitespace'\n | 'chinese_compatible'\n | 'jieba'\n | 'lindera';\n","import { PARADEDB_EXTENSION_ID } from './constants';\n\nexport const paradedbPackMeta = {\n kind: 'extension',\n id: PARADEDB_EXTENSION_ID,\n familyId: 'sql',\n targetId: 'postgres',\n version: '0.0.1',\n capabilities: {\n postgres: {\n 'paradedb/bm25': true,\n },\n },\n} as const;\n"],"mappings":";;ACEA,MAAa,mBAAmB;CAC9B,MAAM;CACN,IAAI;CACJ,UAAU;CACV,UAAU;CACV,SAAS;CACT,cAAc,EACZ,UAAU,EACR,iBAAiB,MAClB,EACF;CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-types.mjs","names":[],"sources":["../src/types/index-types.ts"],"sourcesContent":["import type { IndexDef } from '@prisma-next/contract-authoring';\nimport type { TokenizerId } from '../core/constants';\n\n/**\n * BM25 field config for a table column.\n */\nexport type Bm25ColumnFieldConfig = {\n readonly column: string;\n readonly expression?: never;\n readonly tokenizer?: string;\n readonly tokenizerParams?: Record<string, unknown>;\n readonly alias?: string;\n};\n\n/**\n * BM25 field config for a SQL expression.\n */\nexport type Bm25ExpressionFieldConfig = {\n readonly expression: string;\n readonly column?: never;\n readonly alias: string;\n readonly tokenizer?: string;\n readonly tokenizerParams?: Record<string, unknown>;\n};\n\n/**\n * BM25 field config union.\n */\nexport type Bm25FieldConfig = Bm25ColumnFieldConfig | Bm25ExpressionFieldConfig;\n\n/**\n * BM25 index configuration payload stored in `IndexDef.config`.\n */\nexport type Bm25IndexConfig = {\n readonly keyField: string;\n readonly fields: readonly Bm25FieldConfig[];\n};\n\n/**\n * Options for a BM25 text field (text, varchar columns).\n */\nexport type Bm25TextFieldOptions = {\n readonly tokenizer?: TokenizerId | (string & {});\n readonly stemmer?: string;\n readonly alias?: string;\n readonly remove_emojis?: boolean;\n};\n\n/**\n * Options for a BM25 JSON field (json, jsonb columns).\n */\nexport type Bm25JsonFieldOptions = {\n readonly tokenizer?: TokenizerId | (string & {});\n readonly alias?: string;\n /** Ngram-specific params when tokenizer is 'ngram'. */\n readonly min?: number;\n readonly max?: number;\n};\n\n/**\n * Options for a BM25 expression-based field.\n */\nexport type Bm25ExpressionFieldOptions = {\n readonly alias: string;\n readonly tokenizer?: TokenizerId | (string & {});\n readonly min?: number;\n readonly max?: number;\n readonly stemmer?: string;\n readonly pattern?: string;\n};\n\ntype TokenizerConfig = {\n readonly tokenizer?: string;\n readonly tokenizerParams?: Record<string, unknown>;\n};\n\n/**\n * Options for constructing a BM25 index definition.\n */\nexport type Bm25IndexOptions = {\n readonly keyField: string;\n readonly fields: readonly Bm25FieldConfig[];\n readonly name?: string;\n};\n\n/**\n * Typed BM25 field builders.\n * These produce `Bm25FieldConfig` objects for use in `bm25Index()`.\n */\nexport const bm25 = {\n /** Text field with optional tokenizer config. */\n text(column: string, opts?: Bm25TextFieldOptions): Bm25FieldConfig {\n return {\n column,\n ...buildTokenizerConfig(opts?.tokenizer, {\n stemmer: opts?.stemmer,\n remove_emojis: opts?.remove_emojis,\n }),\n ...(opts?.alias !== undefined && { alias: opts.alias }),\n };\n },\n\n /** Numeric field (filterable, sortable in BM25). */\n numeric(column: string): Bm25FieldConfig {\n return { column };\n },\n\n /** Boolean field. */\n boolean(column: string): Bm25FieldConfig {\n return { column };\n },\n\n /** JSON/JSONB field with optional tokenizer config. */\n json(column: string, opts?: Bm25JsonFieldOptions): Bm25FieldConfig {\n return {\n column,\n ...buildTokenizerConfig(opts?.tokenizer, { min: opts?.min, max: opts?.max }),\n ...(opts?.alias !== undefined && { alias: opts.alias }),\n };\n },\n\n /** Datetime (timestamp/date) field. */\n datetime(column: string): Bm25FieldConfig {\n return { column };\n },\n\n /** Range field. */\n range(column: string): Bm25FieldConfig {\n return { column };\n },\n\n /** Raw SQL expression field. `alias` is required. */\n expression(sql: string, opts: Bm25ExpressionFieldOptions): Bm25FieldConfig {\n return {\n expression: sql,\n alias: opts.alias,\n ...buildTokenizerConfig(opts.tokenizer, {\n min: opts.min,\n max: opts.max,\n stemmer: opts.stemmer,\n pattern: opts.pattern,\n }),\n };\n },\n} as const;\n\n/**\n * Creates a generic index definition with a ParadeDB BM25 payload.\n *\n * `columns` only includes real table columns so core index validation remains\n * target-agnostic. Expression fields stay in extension-owned `config.fields`.\n */\nexport function bm25Index(opts: Bm25IndexOptions): IndexDef {\n return {\n columns: opts.fields.flatMap((field) => ('column' in field ? [field.column] : [])),\n ...(opts.name !== undefined && { name: opts.name }),\n using: 'bm25',\n config: {\n keyField: opts.keyField,\n fields: opts.fields,\n } satisfies Bm25IndexConfig,\n };\n}\n\n/**\n * Builds `{ tokenizer, tokenizerParams? }` from a tokenizer ID and a bag of params.\n * Filters out undefined values and omits `tokenizerParams` when empty.\n */\nfunction buildTokenizerConfig(\n tokenizer: string | undefined,\n params: Record<string, unknown>,\n): TokenizerConfig {\n if (!tokenizer) return {};\n const filtered = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined));\n return {\n tokenizer,\n ...(Object.keys(filtered).length > 0 && { tokenizerParams: filtered }),\n };\n}\n"],"mappings":";;;;;AAyFA,MAAa,OAAO;;CAElB,KAAK,QAAgB,MAA8C;EACjE,OAAO;GACL;GACA,GAAG,qBAAqB,MAAM,WAAW;IACvC,SAAS,MAAM;IACf,eAAe,MAAM;IACtB,CAAC;GACF,GAAI,MAAM,UAAU,KAAA,KAAa,EAAE,OAAO,KAAK,OAAO;GACvD;;;CAIH,QAAQ,QAAiC;EACvC,OAAO,EAAE,QAAQ;;;CAInB,QAAQ,QAAiC;EACvC,OAAO,EAAE,QAAQ;;;CAInB,KAAK,QAAgB,MAA8C;EACjE,OAAO;GACL;GACA,GAAG,qBAAqB,MAAM,WAAW;IAAE,KAAK,MAAM;IAAK,KAAK,MAAM;IAAK,CAAC;GAC5E,GAAI,MAAM,UAAU,KAAA,KAAa,EAAE,OAAO,KAAK,OAAO;GACvD;;;CAIH,SAAS,QAAiC;EACxC,OAAO,EAAE,QAAQ;;;CAInB,MAAM,QAAiC;EACrC,OAAO,EAAE,QAAQ;;;CAInB,WAAW,KAAa,MAAmD;EACzE,OAAO;GACL,YAAY;GACZ,OAAO,KAAK;GACZ,GAAG,qBAAqB,KAAK,WAAW;IACtC,KAAK,KAAK;IACV,KAAK,KAAK;IACV,SAAS,KAAK;IACd,SAAS,KAAK;IACf,CAAC;GACH;;CAEJ;;;;;;;AAQD,SAAgB,UAAU,MAAkC;CAC1D,OAAO;EACL,SAAS,KAAK,OAAO,SAAS,UAAW,YAAY,QAAQ,CAAC,MAAM,OAAO,GAAG,EAAE,CAAE;EAClF,GAAI,KAAK,SAAS,KAAA,KAAa,EAAE,MAAM,KAAK,MAAM;EAClD,OAAO;EACP,QAAQ;GACN,UAAU,KAAK;GACf,QAAQ,KAAK;GACd;EACF;;;;;;AAOH,SAAS,qBACP,WACA,QACiB;CACjB,IAAI,CAAC,WAAW,OAAO,EAAE;CACzB,MAAM,WAAW,OAAO,YAAY,OAAO,QAAQ,OAAO,CAAC,QAAQ,GAAG,OAAO,MAAM,KAAA,EAAU,CAAC;CAC9F,OAAO;EACL;EACA,GAAI,OAAO,KAAK,SAAS,CAAC,SAAS,KAAK,EAAE,iBAAiB,UAAU;EACtE"}