@atscript/db-mongo 0.1.103 → 0.1.105
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/dist/agg.cjs +1 -1
- package/dist/agg.mjs +1 -1
- package/dist/index.cjs +121 -6
- package/dist/index.d.cts +23 -5
- package/dist/index.d.mts +23 -5
- package/dist/index.mjs +122 -6
- package/dist/{mongo-filter-DBYaF9aH.mjs → mongo-filter-DceAGI-S.mjs} +9 -0
- package/dist/{mongo-filter-1EpqdD-T.cjs → mongo-filter-z_hLPMyv.cjs} +9 -0
- package/dist/plugin.cjs +238 -3
- package/dist/plugin.d.cts +5 -1
- package/dist/plugin.d.mts +5 -1
- package/dist/plugin.mjs +236 -1
- package/package.json +7 -7
- package/dist/index-Bl_O47fp.d.cts +0 -6
- package/dist/index-Bl_O47fp.d.mts +0 -6
- package/dist/plugin-Bq6hZMBA.cjs +0 -242
- package/dist/plugin-KVFAwoGw.mjs +0 -237
package/dist/plugin.cjs
CHANGED
|
@@ -2,6 +2,241 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
let _atscript_core = require("@atscript/core");
|
|
6
|
+
//#region src/plugin/annotations.ts
|
|
7
|
+
const analyzers = [
|
|
8
|
+
"lucene.standard",
|
|
9
|
+
"lucene.simple",
|
|
10
|
+
"lucene.whitespace",
|
|
11
|
+
"lucene.english",
|
|
12
|
+
"lucene.french",
|
|
13
|
+
"lucene.german",
|
|
14
|
+
"lucene.italian",
|
|
15
|
+
"lucene.portuguese",
|
|
16
|
+
"lucene.spanish",
|
|
17
|
+
"lucene.chinese",
|
|
18
|
+
"lucene.hindi",
|
|
19
|
+
"lucene.bengali",
|
|
20
|
+
"lucene.russian",
|
|
21
|
+
"lucene.arabic"
|
|
22
|
+
];
|
|
23
|
+
const tokenizations = [
|
|
24
|
+
"edgeGram",
|
|
25
|
+
"rightEdgeGram",
|
|
26
|
+
"nGram"
|
|
27
|
+
];
|
|
28
|
+
const searchStrategies = [
|
|
29
|
+
"compound",
|
|
30
|
+
"autocomplete",
|
|
31
|
+
"text"
|
|
32
|
+
];
|
|
33
|
+
const strategyDescription = "How `search()` matches a term against this index. Locks the query shape into the index — there is no query-time mode switching.\n\n- `compound` (default) → rank exact-word hits above prefix hits: a wildcard `text` clause **plus** one `autocomplete` clause per autocomplete field. Degrades to plain `text` when the index has no autocomplete field.\n- `autocomplete` → **prefix/typeahead only** — query just the autocomplete fields, no word-match ranking clause.\n- `text` → **word matching only** — a single `text` operator over all string-mapped fields (autocomplete fields are matched via their companion `string` mapping).\n\nTo use the same data with a different strategy, declare a second index and select it per request with `$index`.";
|
|
34
|
+
const fuzzyDescription = "Maximum typo tolerance, applied **at query time** to the search operator.\n\n- `0` (default) → no fuzzy matching (exact tokens).\n- `1` → allows small typos (e.g., `\"mongo\"` ≈ `\"mango\"`).\n- `2` → more typo tolerance (e.g., `\"mongodb\"` ≈ `\"mangodb\"`).\n\nAtlas only accepts an edit distance of `1` or `2`; `0` simply disables fuzzy. Can be overridden per request via the `$fuzzy` query control.";
|
|
35
|
+
/**
|
|
36
|
+
* MongoDB-specific annotations.
|
|
37
|
+
*
|
|
38
|
+
* Merged into the global config under `{ db: { mongo: ... } }` so they
|
|
39
|
+
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
40
|
+
*
|
|
41
|
+
* Annotations removed (now in core):
|
|
42
|
+
* - `@mongo.index.plain` → use `@db.index.plain`
|
|
43
|
+
* - `@mongo.index.unique` → use `@db.index.unique`
|
|
44
|
+
* - `@db.mongo.index.text` → use `@db.index.fulltext` (with optional weight arg)
|
|
45
|
+
* - `@db.mongo.patch.strategy` → use `@db.patch.strategy`
|
|
46
|
+
* - `@db.mongo.array.uniqueItems` → use `@expect.array.uniqueItems`
|
|
47
|
+
* - `@db.mongo.autoIndexes` → removed (use explicit syncIndexes() calls)
|
|
48
|
+
* - `@db.mongo.search.vector` → use `@db.search.vector` (generic, in @atscript/db/plugin)
|
|
49
|
+
* - `@db.mongo.search.filter` → use `@db.search.filter` (generic, in @atscript/db/plugin)
|
|
50
|
+
*/
|
|
51
|
+
const annotations = {
|
|
52
|
+
collection: new _atscript_core.AnnotationSpec({
|
|
53
|
+
description: "Marks an interface as a **MongoDB collection**.\n\n- Use together with `@db.table \"name\"` which provides the collection name.\n- Automatically injects a **non-optional** `_id` field if not explicitly defined.\n- `_id` must be of type **`string`**, **`number`**, or **`mongo.objectId`**.\n\n**Example:**\n```atscript\n@db.table \"users\"\n@db.mongo.collection\nexport interface User {\n _id: mongo.objectId\n email: string.email\n}\n```\n",
|
|
54
|
+
nodeType: ["interface"],
|
|
55
|
+
validate(token, args, doc) {
|
|
56
|
+
const parent = token.parentNode;
|
|
57
|
+
const struc = parent?.getDefinition();
|
|
58
|
+
const errors = [];
|
|
59
|
+
if ((0, _atscript_core.isInterface)(parent) && parent.props.has("_id") && (0, _atscript_core.isStructure)(struc)) {
|
|
60
|
+
const _id = parent.props.get("_id");
|
|
61
|
+
if (!!_id.token("optional")) errors.push({
|
|
62
|
+
message: `[db.mongo] _id can't be optional in Mongo Collection`,
|
|
63
|
+
severity: 1,
|
|
64
|
+
range: _id.token("identifier").range
|
|
65
|
+
});
|
|
66
|
+
const definition = _id.getDefinition();
|
|
67
|
+
if (!definition) return errors;
|
|
68
|
+
let wrongType = false;
|
|
69
|
+
if ((0, _atscript_core.isRef)(definition)) {
|
|
70
|
+
const def = doc.unwindType(definition.id, definition.chain)?.def;
|
|
71
|
+
if ((0, _atscript_core.isPrimitive)(def) && !["string", "number"].includes(def.config.type)) wrongType = true;
|
|
72
|
+
} else wrongType = true;
|
|
73
|
+
if (wrongType) errors.push({
|
|
74
|
+
message: `[db.mongo] _id must be of type string, number or mongo.objectId`,
|
|
75
|
+
severity: 1,
|
|
76
|
+
range: _id.token("identifier").range
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return errors;
|
|
80
|
+
},
|
|
81
|
+
modify(token, _args, _doc) {
|
|
82
|
+
const parent = token.parentNode;
|
|
83
|
+
const struc = parent?.getDefinition();
|
|
84
|
+
if ((0, _atscript_core.isInterface)(parent) && !parent.props.has("_id") && (0, _atscript_core.isStructure)(struc)) struc.addVirtualProp({
|
|
85
|
+
name: "_id",
|
|
86
|
+
type: "mongo.objectId",
|
|
87
|
+
documentation: "Mongodb Primary Key ObjectId"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}),
|
|
91
|
+
capped: new _atscript_core.AnnotationSpec({
|
|
92
|
+
description: "Creates a **capped collection** with a fixed maximum size.\n\n- Capped collections have fixed size and maintain insertion order.\n- Ideal for logs, event streams, and cache-like data.\n- Changing the cap size requires dropping and recreating the collection — use `@db.sync.method \"drop\"` to allow this.\n\n**Example:**\n```atscript\n@db.table \"logs\"\n@db.mongo.collection\n@db.mongo.capped 10485760, 10000\n@db.sync.method \"drop\"\nexport interface LogEntry {\n message: string\n timestamp: number\n}\n```\n",
|
|
93
|
+
nodeType: ["interface"],
|
|
94
|
+
multiple: false,
|
|
95
|
+
argument: [{
|
|
96
|
+
optional: false,
|
|
97
|
+
name: "size",
|
|
98
|
+
type: "number",
|
|
99
|
+
description: "Maximum size of the collection in **bytes**."
|
|
100
|
+
}, {
|
|
101
|
+
optional: true,
|
|
102
|
+
name: "max",
|
|
103
|
+
type: "number",
|
|
104
|
+
description: "Maximum number of documents in the collection. If omitted, only the byte size limit applies."
|
|
105
|
+
}]
|
|
106
|
+
}),
|
|
107
|
+
search: {
|
|
108
|
+
dynamic: new _atscript_core.AnnotationSpec({
|
|
109
|
+
description: "Creates a **dynamic MongoDB Search Index** that applies to the entire collection.\n\n- **Indexes all text fields automatically** (no need to specify fields).\n- Supports **language analyzers** for text tokenization.\n- Enables **fuzzy search** (typo tolerance) if needed.\n\n**Example:**\n```atscript\n@db.mongo.search.dynamic \"lucene.english\", 1\nexport interface MongoCollection {}\n```\n",
|
|
110
|
+
nodeType: ["interface"],
|
|
111
|
+
multiple: false,
|
|
112
|
+
argument: [{
|
|
113
|
+
optional: true,
|
|
114
|
+
name: "analyzer",
|
|
115
|
+
type: "string",
|
|
116
|
+
description: "The **text analyzer** for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, etc.",
|
|
117
|
+
values: analyzers
|
|
118
|
+
}, {
|
|
119
|
+
optional: true,
|
|
120
|
+
name: "fuzzy",
|
|
121
|
+
type: "number",
|
|
122
|
+
description: fuzzyDescription
|
|
123
|
+
}]
|
|
124
|
+
}),
|
|
125
|
+
static: new _atscript_core.AnnotationSpec({
|
|
126
|
+
description: "Defines a **MongoDB Atlas Search Index** for the collection. The props can refer to this index using `@db.mongo.search.text` annotation.\n\n- **Creates a named search index** for full-text search.\n- **Specify analyzers and fuzzy search** behavior at the index level.\n- **Fields must explicitly use `@db.mongo.search.text`** to be included in this search index.\n\n**Example:**\n```atscript\n@db.mongo.search.static \"lucene.english\", 1, \"mySearchIndex\"\nexport interface MongoCollection {}\n```\n",
|
|
127
|
+
nodeType: ["interface"],
|
|
128
|
+
multiple: true,
|
|
129
|
+
argument: [
|
|
130
|
+
{
|
|
131
|
+
optional: true,
|
|
132
|
+
name: "analyzer",
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "The text analyzer for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, `\"lucene.german\"`, etc.",
|
|
135
|
+
values: analyzers
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
optional: true,
|
|
139
|
+
name: "fuzzy",
|
|
140
|
+
type: "number",
|
|
141
|
+
description: fuzzyDescription
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
optional: true,
|
|
145
|
+
name: "indexName",
|
|
146
|
+
type: "string",
|
|
147
|
+
description: "The name of the search index. Fields must reference this name using `@db.mongo.search.text` or `@db.mongo.search.autocomplete`. If not set, defaults to `\"DEFAULT\"`."
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
optional: true,
|
|
151
|
+
name: "strategy",
|
|
152
|
+
type: "string",
|
|
153
|
+
description: strategyDescription,
|
|
154
|
+
values: searchStrategies
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}),
|
|
158
|
+
text: new _atscript_core.AnnotationSpec({
|
|
159
|
+
description: "Marks a field to be **included in a MongoDB Atlas Search Index** defined by `@db.mongo.search.static`.\n\n- **The field has to reference an existing search index name**.\n- If index name is not defined, a new search index with default attributes will be created.\n\n**Example:**\n```atscript\n@db.mongo.search.text \"lucene.english\", \"mySearchIndex\"\nfirstName: string\n```\n",
|
|
160
|
+
nodeType: ["prop"],
|
|
161
|
+
multiple: true,
|
|
162
|
+
argument: [{
|
|
163
|
+
optional: true,
|
|
164
|
+
name: "analyzer",
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "The text analyzer for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, `\"lucene.german\"`, etc.",
|
|
167
|
+
values: analyzers
|
|
168
|
+
}, {
|
|
169
|
+
optional: true,
|
|
170
|
+
name: "indexName",
|
|
171
|
+
type: "string",
|
|
172
|
+
description: "The **name of the search index** defined in `@db.mongo.search.static`. This links the field to the correct index. If not set, defaults to `\"DEFAULT\"`."
|
|
173
|
+
}]
|
|
174
|
+
}),
|
|
175
|
+
autocomplete: new _atscript_core.AnnotationSpec({
|
|
176
|
+
description: "Marks a field for **prefix / typeahead (as-you-type)** matching in a MongoDB Atlas Search Index.\n\n- Indexes the field as the Atlas **`autocomplete`** type, **and** double-maps it as `string` so exact-word hits still rank.\n- Lets `search()` match partial words: with the default `edgeGram` tokenization, `\"art\"` matches `\"Artem\"` **as you type** (no whole word required).\n- Use `nGram` tokenization for true mid-word (infix/substring) matching at higher index cost.\n- Like `@db.mongo.search.text`, the field joins the index named by `indexName` (or the default index).\n\n**Example:**\n```atscript\n@db.mongo.search.autocomplete \"users\"\nusername: string\n```\n",
|
|
177
|
+
nodeType: ["prop"],
|
|
178
|
+
multiple: true,
|
|
179
|
+
argument: [
|
|
180
|
+
{
|
|
181
|
+
optional: true,
|
|
182
|
+
name: "indexName",
|
|
183
|
+
type: "string",
|
|
184
|
+
description: "The **name of the search index** (defined by `@db.mongo.search.static`) this field joins. If not set, defaults to `\"DEFAULT\"`."
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
optional: true,
|
|
188
|
+
name: "tokenization",
|
|
189
|
+
type: "string",
|
|
190
|
+
description: "How the field is tokenized for partial matching:\n\n- `\"edgeGram\"` (default) → **prefix** matching from the start of each word (`\"art\"` → `\"Artem\"`).\n- `\"nGram\"` → **substring/infix** matching anywhere inside a word (`\"tem\"` → `\"Artem\"`); larger index, slower builds.\n- `\"rightEdgeGram\"` → **suffix** matching from the end of each word.",
|
|
191
|
+
values: tokenizations
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
optional: true,
|
|
195
|
+
name: "minGrams",
|
|
196
|
+
type: "number",
|
|
197
|
+
description: "Minimum number of characters per indexed sequence. Defaults to `2`."
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
optional: true,
|
|
201
|
+
name: "maxGrams",
|
|
202
|
+
type: "number",
|
|
203
|
+
description: "Maximum number of characters per indexed sequence. Defaults to `15`."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
optional: true,
|
|
207
|
+
name: "foldDiacritics",
|
|
208
|
+
type: "boolean",
|
|
209
|
+
description: "Whether to fold (ignore) diacritics so `\"café\"` matches `\"cafe\"`. Defaults to `true`."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
optional: true,
|
|
213
|
+
name: "analyzer",
|
|
214
|
+
type: "string",
|
|
215
|
+
description: "The text analyzer for the companion `string` mapping. Defaults to `\"lucene.standard\"`.",
|
|
216
|
+
values: analyzers
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/plugin/primitives.ts
|
|
224
|
+
const primitives = { mongo: { extensions: { objectId: {
|
|
225
|
+
type: "string",
|
|
226
|
+
documentation: "Represents a **MongoDB ObjectId**.\n\n- Stored as a **string** but can be converted to an ObjectId at runtime.\n- Useful for handling `_id` fields and queries that require ObjectId conversion.\n- Automatically converts string `_id` values into **MongoDB ObjectId** when needed.\n\n**Example:**\n```atscript\nuserId: mongo.objectId\n```\n",
|
|
227
|
+
annotations: { "expect.pattern": { pattern: "^[a-fA-F0-9]{24}$" } }
|
|
228
|
+
} } } };
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/plugin/index.ts
|
|
231
|
+
const MongoPlugin = () => ({
|
|
232
|
+
name: "mongo",
|
|
233
|
+
config() {
|
|
234
|
+
return {
|
|
235
|
+
primitives,
|
|
236
|
+
annotations: { db: { mongo: annotations } }
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
//#endregion
|
|
241
|
+
exports.MongoPlugin = MongoPlugin;
|
|
242
|
+
exports.default = MongoPlugin;
|
package/dist/plugin.d.cts
CHANGED
package/dist/plugin.d.mts
CHANGED
package/dist/plugin.mjs
CHANGED
|
@@ -1,2 +1,237 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnnotationSpec, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
|
|
2
|
+
//#region src/plugin/annotations.ts
|
|
3
|
+
const analyzers = [
|
|
4
|
+
"lucene.standard",
|
|
5
|
+
"lucene.simple",
|
|
6
|
+
"lucene.whitespace",
|
|
7
|
+
"lucene.english",
|
|
8
|
+
"lucene.french",
|
|
9
|
+
"lucene.german",
|
|
10
|
+
"lucene.italian",
|
|
11
|
+
"lucene.portuguese",
|
|
12
|
+
"lucene.spanish",
|
|
13
|
+
"lucene.chinese",
|
|
14
|
+
"lucene.hindi",
|
|
15
|
+
"lucene.bengali",
|
|
16
|
+
"lucene.russian",
|
|
17
|
+
"lucene.arabic"
|
|
18
|
+
];
|
|
19
|
+
const tokenizations = [
|
|
20
|
+
"edgeGram",
|
|
21
|
+
"rightEdgeGram",
|
|
22
|
+
"nGram"
|
|
23
|
+
];
|
|
24
|
+
const searchStrategies = [
|
|
25
|
+
"compound",
|
|
26
|
+
"autocomplete",
|
|
27
|
+
"text"
|
|
28
|
+
];
|
|
29
|
+
const strategyDescription = "How `search()` matches a term against this index. Locks the query shape into the index — there is no query-time mode switching.\n\n- `compound` (default) → rank exact-word hits above prefix hits: a wildcard `text` clause **plus** one `autocomplete` clause per autocomplete field. Degrades to plain `text` when the index has no autocomplete field.\n- `autocomplete` → **prefix/typeahead only** — query just the autocomplete fields, no word-match ranking clause.\n- `text` → **word matching only** — a single `text` operator over all string-mapped fields (autocomplete fields are matched via their companion `string` mapping).\n\nTo use the same data with a different strategy, declare a second index and select it per request with `$index`.";
|
|
30
|
+
const fuzzyDescription = "Maximum typo tolerance, applied **at query time** to the search operator.\n\n- `0` (default) → no fuzzy matching (exact tokens).\n- `1` → allows small typos (e.g., `\"mongo\"` ≈ `\"mango\"`).\n- `2` → more typo tolerance (e.g., `\"mongodb\"` ≈ `\"mangodb\"`).\n\nAtlas only accepts an edit distance of `1` or `2`; `0` simply disables fuzzy. Can be overridden per request via the `$fuzzy` query control.";
|
|
31
|
+
/**
|
|
32
|
+
* MongoDB-specific annotations.
|
|
33
|
+
*
|
|
34
|
+
* Merged into the global config under `{ db: { mongo: ... } }` so they
|
|
35
|
+
* live alongside core's `@db.table`, `@db.index.*`, etc.
|
|
36
|
+
*
|
|
37
|
+
* Annotations removed (now in core):
|
|
38
|
+
* - `@mongo.index.plain` → use `@db.index.plain`
|
|
39
|
+
* - `@mongo.index.unique` → use `@db.index.unique`
|
|
40
|
+
* - `@db.mongo.index.text` → use `@db.index.fulltext` (with optional weight arg)
|
|
41
|
+
* - `@db.mongo.patch.strategy` → use `@db.patch.strategy`
|
|
42
|
+
* - `@db.mongo.array.uniqueItems` → use `@expect.array.uniqueItems`
|
|
43
|
+
* - `@db.mongo.autoIndexes` → removed (use explicit syncIndexes() calls)
|
|
44
|
+
* - `@db.mongo.search.vector` → use `@db.search.vector` (generic, in @atscript/db/plugin)
|
|
45
|
+
* - `@db.mongo.search.filter` → use `@db.search.filter` (generic, in @atscript/db/plugin)
|
|
46
|
+
*/
|
|
47
|
+
const annotations = {
|
|
48
|
+
collection: new AnnotationSpec({
|
|
49
|
+
description: "Marks an interface as a **MongoDB collection**.\n\n- Use together with `@db.table \"name\"` which provides the collection name.\n- Automatically injects a **non-optional** `_id` field if not explicitly defined.\n- `_id` must be of type **`string`**, **`number`**, or **`mongo.objectId`**.\n\n**Example:**\n```atscript\n@db.table \"users\"\n@db.mongo.collection\nexport interface User {\n _id: mongo.objectId\n email: string.email\n}\n```\n",
|
|
50
|
+
nodeType: ["interface"],
|
|
51
|
+
validate(token, args, doc) {
|
|
52
|
+
const parent = token.parentNode;
|
|
53
|
+
const struc = parent?.getDefinition();
|
|
54
|
+
const errors = [];
|
|
55
|
+
if (isInterface(parent) && parent.props.has("_id") && isStructure(struc)) {
|
|
56
|
+
const _id = parent.props.get("_id");
|
|
57
|
+
if (!!_id.token("optional")) errors.push({
|
|
58
|
+
message: `[db.mongo] _id can't be optional in Mongo Collection`,
|
|
59
|
+
severity: 1,
|
|
60
|
+
range: _id.token("identifier").range
|
|
61
|
+
});
|
|
62
|
+
const definition = _id.getDefinition();
|
|
63
|
+
if (!definition) return errors;
|
|
64
|
+
let wrongType = false;
|
|
65
|
+
if (isRef(definition)) {
|
|
66
|
+
const def = doc.unwindType(definition.id, definition.chain)?.def;
|
|
67
|
+
if (isPrimitive(def) && !["string", "number"].includes(def.config.type)) wrongType = true;
|
|
68
|
+
} else wrongType = true;
|
|
69
|
+
if (wrongType) errors.push({
|
|
70
|
+
message: `[db.mongo] _id must be of type string, number or mongo.objectId`,
|
|
71
|
+
severity: 1,
|
|
72
|
+
range: _id.token("identifier").range
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return errors;
|
|
76
|
+
},
|
|
77
|
+
modify(token, _args, _doc) {
|
|
78
|
+
const parent = token.parentNode;
|
|
79
|
+
const struc = parent?.getDefinition();
|
|
80
|
+
if (isInterface(parent) && !parent.props.has("_id") && isStructure(struc)) struc.addVirtualProp({
|
|
81
|
+
name: "_id",
|
|
82
|
+
type: "mongo.objectId",
|
|
83
|
+
documentation: "Mongodb Primary Key ObjectId"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}),
|
|
87
|
+
capped: new AnnotationSpec({
|
|
88
|
+
description: "Creates a **capped collection** with a fixed maximum size.\n\n- Capped collections have fixed size and maintain insertion order.\n- Ideal for logs, event streams, and cache-like data.\n- Changing the cap size requires dropping and recreating the collection — use `@db.sync.method \"drop\"` to allow this.\n\n**Example:**\n```atscript\n@db.table \"logs\"\n@db.mongo.collection\n@db.mongo.capped 10485760, 10000\n@db.sync.method \"drop\"\nexport interface LogEntry {\n message: string\n timestamp: number\n}\n```\n",
|
|
89
|
+
nodeType: ["interface"],
|
|
90
|
+
multiple: false,
|
|
91
|
+
argument: [{
|
|
92
|
+
optional: false,
|
|
93
|
+
name: "size",
|
|
94
|
+
type: "number",
|
|
95
|
+
description: "Maximum size of the collection in **bytes**."
|
|
96
|
+
}, {
|
|
97
|
+
optional: true,
|
|
98
|
+
name: "max",
|
|
99
|
+
type: "number",
|
|
100
|
+
description: "Maximum number of documents in the collection. If omitted, only the byte size limit applies."
|
|
101
|
+
}]
|
|
102
|
+
}),
|
|
103
|
+
search: {
|
|
104
|
+
dynamic: new AnnotationSpec({
|
|
105
|
+
description: "Creates a **dynamic MongoDB Search Index** that applies to the entire collection.\n\n- **Indexes all text fields automatically** (no need to specify fields).\n- Supports **language analyzers** for text tokenization.\n- Enables **fuzzy search** (typo tolerance) if needed.\n\n**Example:**\n```atscript\n@db.mongo.search.dynamic \"lucene.english\", 1\nexport interface MongoCollection {}\n```\n",
|
|
106
|
+
nodeType: ["interface"],
|
|
107
|
+
multiple: false,
|
|
108
|
+
argument: [{
|
|
109
|
+
optional: true,
|
|
110
|
+
name: "analyzer",
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "The **text analyzer** for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, etc.",
|
|
113
|
+
values: analyzers
|
|
114
|
+
}, {
|
|
115
|
+
optional: true,
|
|
116
|
+
name: "fuzzy",
|
|
117
|
+
type: "number",
|
|
118
|
+
description: fuzzyDescription
|
|
119
|
+
}]
|
|
120
|
+
}),
|
|
121
|
+
static: new AnnotationSpec({
|
|
122
|
+
description: "Defines a **MongoDB Atlas Search Index** for the collection. The props can refer to this index using `@db.mongo.search.text` annotation.\n\n- **Creates a named search index** for full-text search.\n- **Specify analyzers and fuzzy search** behavior at the index level.\n- **Fields must explicitly use `@db.mongo.search.text`** to be included in this search index.\n\n**Example:**\n```atscript\n@db.mongo.search.static \"lucene.english\", 1, \"mySearchIndex\"\nexport interface MongoCollection {}\n```\n",
|
|
123
|
+
nodeType: ["interface"],
|
|
124
|
+
multiple: true,
|
|
125
|
+
argument: [
|
|
126
|
+
{
|
|
127
|
+
optional: true,
|
|
128
|
+
name: "analyzer",
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "The text analyzer for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, `\"lucene.german\"`, etc.",
|
|
131
|
+
values: analyzers
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
optional: true,
|
|
135
|
+
name: "fuzzy",
|
|
136
|
+
type: "number",
|
|
137
|
+
description: fuzzyDescription
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
optional: true,
|
|
141
|
+
name: "indexName",
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "The name of the search index. Fields must reference this name using `@db.mongo.search.text` or `@db.mongo.search.autocomplete`. If not set, defaults to `\"DEFAULT\"`."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
optional: true,
|
|
147
|
+
name: "strategy",
|
|
148
|
+
type: "string",
|
|
149
|
+
description: strategyDescription,
|
|
150
|
+
values: searchStrategies
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}),
|
|
154
|
+
text: new AnnotationSpec({
|
|
155
|
+
description: "Marks a field to be **included in a MongoDB Atlas Search Index** defined by `@db.mongo.search.static`.\n\n- **The field has to reference an existing search index name**.\n- If index name is not defined, a new search index with default attributes will be created.\n\n**Example:**\n```atscript\n@db.mongo.search.text \"lucene.english\", \"mySearchIndex\"\nfirstName: string\n```\n",
|
|
156
|
+
nodeType: ["prop"],
|
|
157
|
+
multiple: true,
|
|
158
|
+
argument: [{
|
|
159
|
+
optional: true,
|
|
160
|
+
name: "analyzer",
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "The text analyzer for tokenization. Defaults to `\"lucene.standard\"`.\n\n**Available options:** `\"lucene.standard\"`, `\"lucene.english\"`, `\"lucene.spanish\"`, `\"lucene.german\"`, etc.",
|
|
163
|
+
values: analyzers
|
|
164
|
+
}, {
|
|
165
|
+
optional: true,
|
|
166
|
+
name: "indexName",
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "The **name of the search index** defined in `@db.mongo.search.static`. This links the field to the correct index. If not set, defaults to `\"DEFAULT\"`."
|
|
169
|
+
}]
|
|
170
|
+
}),
|
|
171
|
+
autocomplete: new AnnotationSpec({
|
|
172
|
+
description: "Marks a field for **prefix / typeahead (as-you-type)** matching in a MongoDB Atlas Search Index.\n\n- Indexes the field as the Atlas **`autocomplete`** type, **and** double-maps it as `string` so exact-word hits still rank.\n- Lets `search()` match partial words: with the default `edgeGram` tokenization, `\"art\"` matches `\"Artem\"` **as you type** (no whole word required).\n- Use `nGram` tokenization for true mid-word (infix/substring) matching at higher index cost.\n- Like `@db.mongo.search.text`, the field joins the index named by `indexName` (or the default index).\n\n**Example:**\n```atscript\n@db.mongo.search.autocomplete \"users\"\nusername: string\n```\n",
|
|
173
|
+
nodeType: ["prop"],
|
|
174
|
+
multiple: true,
|
|
175
|
+
argument: [
|
|
176
|
+
{
|
|
177
|
+
optional: true,
|
|
178
|
+
name: "indexName",
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "The **name of the search index** (defined by `@db.mongo.search.static`) this field joins. If not set, defaults to `\"DEFAULT\"`."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
optional: true,
|
|
184
|
+
name: "tokenization",
|
|
185
|
+
type: "string",
|
|
186
|
+
description: "How the field is tokenized for partial matching:\n\n- `\"edgeGram\"` (default) → **prefix** matching from the start of each word (`\"art\"` → `\"Artem\"`).\n- `\"nGram\"` → **substring/infix** matching anywhere inside a word (`\"tem\"` → `\"Artem\"`); larger index, slower builds.\n- `\"rightEdgeGram\"` → **suffix** matching from the end of each word.",
|
|
187
|
+
values: tokenizations
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
optional: true,
|
|
191
|
+
name: "minGrams",
|
|
192
|
+
type: "number",
|
|
193
|
+
description: "Minimum number of characters per indexed sequence. Defaults to `2`."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
optional: true,
|
|
197
|
+
name: "maxGrams",
|
|
198
|
+
type: "number",
|
|
199
|
+
description: "Maximum number of characters per indexed sequence. Defaults to `15`."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
optional: true,
|
|
203
|
+
name: "foldDiacritics",
|
|
204
|
+
type: "boolean",
|
|
205
|
+
description: "Whether to fold (ignore) diacritics so `\"café\"` matches `\"cafe\"`. Defaults to `true`."
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
optional: true,
|
|
209
|
+
name: "analyzer",
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "The text analyzer for the companion `string` mapping. Defaults to `\"lucene.standard\"`.",
|
|
212
|
+
values: analyzers
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region src/plugin/primitives.ts
|
|
220
|
+
const primitives = { mongo: { extensions: { objectId: {
|
|
221
|
+
type: "string",
|
|
222
|
+
documentation: "Represents a **MongoDB ObjectId**.\n\n- Stored as a **string** but can be converted to an ObjectId at runtime.\n- Useful for handling `_id` fields and queries that require ObjectId conversion.\n- Automatically converts string `_id` values into **MongoDB ObjectId** when needed.\n\n**Example:**\n```atscript\nuserId: mongo.objectId\n```\n",
|
|
223
|
+
annotations: { "expect.pattern": { pattern: "^[a-fA-F0-9]{24}$" } }
|
|
224
|
+
} } } };
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/plugin/index.ts
|
|
227
|
+
const MongoPlugin = () => ({
|
|
228
|
+
name: "mongo",
|
|
229
|
+
config() {
|
|
230
|
+
return {
|
|
231
|
+
primitives,
|
|
232
|
+
annotations: { db: { mongo: annotations } }
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
//#endregion
|
|
2
237
|
export { MongoPlugin, MongoPlugin as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mongo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.105",
|
|
4
4
|
"description": "Mongodb plugin for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@atscript/core": "^0.1.
|
|
50
|
-
"@atscript/typescript": "^0.1.
|
|
49
|
+
"@atscript/core": "^0.1.76",
|
|
50
|
+
"@atscript/typescript": "^0.1.76",
|
|
51
51
|
"mongodb": "^6.17.0",
|
|
52
52
|
"mongodb-memory-server-core": "^10.0.0",
|
|
53
|
-
"unplugin-atscript": "^0.1.
|
|
53
|
+
"unplugin-atscript": "^0.1.76"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@atscript/core": "^0.1.
|
|
57
|
-
"@atscript/typescript": "^0.1.
|
|
56
|
+
"@atscript/core": "^0.1.76",
|
|
57
|
+
"@atscript/typescript": "^0.1.76",
|
|
58
58
|
"mongodb": "^6.17.0",
|
|
59
|
-
"@atscript/db": "^0.1.
|
|
59
|
+
"@atscript/db": "^0.1.105"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"postinstall": "asc -f dts",
|