@atscript/db-mongo 0.1.102 → 0.1.103

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.
@@ -0,0 +1,6 @@
1
+ import { TAtscriptPlugin } from "@atscript/core";
2
+
3
+ //#region src/plugin/index.d.ts
4
+ declare const MongoPlugin: () => TAtscriptPlugin;
5
+ //#endregion
6
+ export { MongoPlugin as t };
@@ -0,0 +1,6 @@
1
+ import { TAtscriptPlugin } from "@atscript/core";
2
+
3
+ //#region src/plugin/index.d.ts
4
+ declare const MongoPlugin: () => TAtscriptPlugin;
5
+ //#endregion
6
+ export { MongoPlugin as t };
package/dist/index.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_mongo_filter = require("./mongo-filter-1EpqdD-T.cjs");
3
+ const require_plugin = require("./plugin-Bq6hZMBA.cjs");
3
4
  let _atscript_db = require("@atscript/db");
4
5
  let mongodb = require("mongodb");
5
6
  //#region src/lib/projection-dedupe.ts
@@ -2163,6 +2164,7 @@ function createAdapter(connection, _options) {
2163
2164
  //#endregion
2164
2165
  exports.CollectionPatcher = CollectionPatcher;
2165
2166
  exports.MongoAdapter = MongoAdapter;
2167
+ exports.MongoPlugin = require_plugin.MongoPlugin;
2166
2168
  exports.buildMongoFilter = require_mongo_filter.buildMongoFilter;
2167
2169
  exports.createAdapter = createAdapter;
2168
2170
  exports.validateMongoIdPlugin = validateMongoIdPlugin;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as MongoPlugin } from "./index-Bl_O47fp.cjs";
1
2
  import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
3
  import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
3
4
  import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
@@ -407,4 +408,4 @@ declare const validateMongoIdPlugin: TValidatorPlugin;
407
408
  //#region src/lib/index.d.ts
408
409
  declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
409
410
  //#endregion
410
- export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
411
+ export { CollectionPatcher, MongoAdapter, MongoPlugin, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as MongoPlugin } from "./index-Bl_O47fp.mjs";
1
2
  import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
3
  import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
3
4
  import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
@@ -407,4 +408,4 @@ declare const validateMongoIdPlugin: TValidatorPlugin;
407
408
  //#region src/lib/index.d.ts
408
409
  declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
409
410
  //#endregion
410
- export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
411
+ export { CollectionPatcher, MongoAdapter, MongoPlugin, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { t as buildMongoFilter } from "./mongo-filter-DBYaF9aH.mjs";
2
+ import { t as MongoPlugin } from "./plugin-KVFAwoGw.mjs";
2
3
  import { AtscriptDbView, BaseDbAdapter, DbError, DbSpace, computeInsights, getDbFieldOp, getKeyProps, resolveDesignType } from "@atscript/db";
3
4
  import { MongoClient, MongoServerError, ObjectId } from "mongodb";
4
5
  //#region src/lib/projection-dedupe.ts
@@ -2160,4 +2161,4 @@ function createAdapter(connection, _options) {
2160
2161
  return new DbSpace(() => new MongoAdapter(db, client));
2161
2162
  }
2162
2163
  //#endregion
2163
- export { CollectionPatcher, MongoAdapter, buildMongoFilter, createAdapter, validateMongoIdPlugin };
2164
+ export { CollectionPatcher, MongoAdapter, MongoPlugin, buildMongoFilter, createAdapter, validateMongoIdPlugin };
@@ -0,0 +1,242 @@
1
+ let _atscript_core = require("@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 _atscript_core.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 ((0, _atscript_core.isInterface)(parent) && parent.props.has("_id") && (0, _atscript_core.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 ((0, _atscript_core.isRef)(definition)) {
66
+ const def = doc.unwindType(definition.id, definition.chain)?.def;
67
+ if ((0, _atscript_core.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 ((0, _atscript_core.isInterface)(parent) && !parent.props.has("_id") && (0, _atscript_core.isStructure)(struc)) struc.addVirtualProp({
81
+ name: "_id",
82
+ type: "mongo.objectId",
83
+ documentation: "Mongodb Primary Key ObjectId"
84
+ });
85
+ }
86
+ }),
87
+ capped: new _atscript_core.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 _atscript_core.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 _atscript_core.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 _atscript_core.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 _atscript_core.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
237
+ Object.defineProperty(exports, "MongoPlugin", {
238
+ enumerable: true,
239
+ get: function() {
240
+ return MongoPlugin;
241
+ }
242
+ });
@@ -0,0 +1,237 @@
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
237
+ export { MongoPlugin as t };
package/dist/plugin.cjs CHANGED
@@ -2,241 +2,6 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
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;
5
+ const require_plugin = require("./plugin-Bq6hZMBA.cjs");
6
+ exports.MongoPlugin = require_plugin.MongoPlugin;
7
+ exports.default = require_plugin.MongoPlugin;
package/dist/plugin.d.cts CHANGED
@@ -1,6 +1,2 @@
1
- import { TAtscriptPlugin } from "@atscript/core";
2
-
3
- //#region src/plugin/index.d.ts
4
- declare const MongoPlugin: () => TAtscriptPlugin;
5
- //#endregion
1
+ import { t as MongoPlugin } from "./index-Bl_O47fp.cjs";
6
2
  export { MongoPlugin, MongoPlugin as default };
package/dist/plugin.d.mts CHANGED
@@ -1,6 +1,2 @@
1
- import { TAtscriptPlugin } from "@atscript/core";
2
-
3
- //#region src/plugin/index.d.ts
4
- declare const MongoPlugin: () => TAtscriptPlugin;
5
- //#endregion
1
+ import { t as MongoPlugin } from "./index-Bl_O47fp.mjs";
6
2
  export { MongoPlugin, MongoPlugin as default };
package/dist/plugin.mjs CHANGED
@@ -1,237 +1,2 @@
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
1
+ import { t as MongoPlugin } from "./plugin-KVFAwoGw.mjs";
237
2
  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.102",
3
+ "version": "0.1.103",
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.74",
50
- "@atscript/typescript": "^0.1.74",
49
+ "@atscript/core": "^0.1.75",
50
+ "@atscript/typescript": "^0.1.75",
51
51
  "mongodb": "^6.17.0",
52
52
  "mongodb-memory-server-core": "^10.0.0",
53
- "unplugin-atscript": "^0.1.74"
53
+ "unplugin-atscript": "^0.1.75"
54
54
  },
55
55
  "peerDependencies": {
56
- "@atscript/core": "^0.1.74",
57
- "@atscript/typescript": "^0.1.74",
56
+ "@atscript/core": "^0.1.75",
57
+ "@atscript/typescript": "^0.1.75",
58
58
  "mongodb": "^6.17.0",
59
- "@atscript/db": "^0.1.102"
59
+ "@atscript/db": "^0.1.103"
60
60
  },
61
61
  "scripts": {
62
62
  "postinstall": "asc -f dts",