@mongorm/orm 0.1.1-beta.1 → 0.1.1-beta.3

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.
@@ -1,232 +0,0 @@
1
- /* oxlint-disable no-unused-expressions, no-unused-vars */
2
-
3
- import type { ObjectId } from 'mongodb';
4
-
5
- import type { Db, Infer, InferShape } from '../../src/index.js';
6
- import { createDatabase, orm } from '../../src/index.js';
7
-
8
- const userSchema = orm.schema({
9
- name: orm.string(),
10
- role: orm.enum(['admin', 'member']),
11
- });
12
-
13
- type User = Infer<typeof userSchema>;
14
- declare const userId: ObjectId;
15
- const user: User = { _id: userId, name: 'Ada', role: 'admin' };
16
- void user;
17
-
18
- // @ts-expect-error The enum remains a literal union.
19
- const invalidRole: User = { name: 'Ada', role: 'owner' };
20
- void invalidRole;
21
-
22
- // @ts-expect-error Unknown fields are not part of the inferred shape.
23
- const invalidUser: User = { name: 'Ada', role: 'member', active: true };
24
- void invalidUser;
25
-
26
- const parsedUser: InferShape<typeof userSchema> = userSchema.parse({
27
- name: 'Ada',
28
- role: 'member',
29
- });
30
- void parsedUser;
31
-
32
- const groupSchema = orm.schema({ name: orm.string() });
33
- const memberSchema = orm.schema({
34
- group: orm.ref(() => groupSchema).optional(),
35
- nullableGroup: orm.ref(() => groupSchema).nullable(),
36
- nullishGroup: orm.ref(() => groupSchema).nullish(),
37
- });
38
- const targetGroup: typeof groupSchema = memberSchema.refs.group.resolve();
39
- void targetGroup;
40
- const member: Infer<typeof memberSchema> = {
41
- _id: userId,
42
- group: null as unknown as ObjectId,
43
- nullableGroup: null,
44
- nullishGroup: undefined,
45
- };
46
- void member;
47
-
48
- declare const db: Db;
49
- const users = db.model('users', userSchema);
50
- await users.create({ name: 'Ada', role: 'member' });
51
-
52
- const defaultedSchema = orm.schema({ status: orm.string().default('pending') });
53
- const defaultedModel = db.model('defaulted', defaultedSchema);
54
- await defaultedModel.create({});
55
- const defaultedDocument: Infer<typeof defaultedSchema> = { _id: userId, status: 'pending' };
56
- void defaultedDocument;
57
- // @ts-expect-error Defaulted fields are required in persisted documents.
58
- const missingDefault: Infer<typeof defaultedSchema> = { _id: userId };
59
- void missingDefault;
60
-
61
- await users.find({ role: 'member' });
62
- await users.find({ role: { $in: ['admin', 'member'] } });
63
- await users.find({}).sort({ name: 'asc', role: 'desc' });
64
- await users.find({}).sort({ name: 'asc' }).skip(1);
65
- await users.find({}).sort({ name: 'asc' }).skip(1).limit(2);
66
- await users.find({ role: 'admin' }).count();
67
- await users.find().count(true);
68
- const firstPage = users.find().limit(2).cursor();
69
- for await (const firstUser of firstPage) firstUser.name;
70
- firstPage.next;
71
- const nextPage = await users
72
- .find()
73
- .limit(2)
74
- .cursor(firstPage.next ?? undefined);
75
- for await (const nextUser of nextPage) nextUser.name;
76
- // @ts-expect-error Cursor positions use ObjectId values.
77
- await users.find().limit(2).cursor('after');
78
- // @ts-expect-error Cursor pagination cannot be combined with skip.
79
- users.find().limit(2).skip(1).cursor();
80
- // @ts-expect-error Cursor pagination cannot use custom sorting yet.
81
- users.find().limit(2).sort({ name: 'asc' }).cursor();
82
- const selectedUsers = await users.find({}).select(['name', 'role']);
83
- selectedUsers[0].name;
84
- selectedUsers[0]._id;
85
- // @ts-expect-error Unselected fields are omitted from the result type.
86
- selectedUsers[0].age;
87
- const selectedUser = await users.find({}).select(['name']).first();
88
- selectedUser?.name;
89
- selectedUser?._id;
90
- // @ts-expect-error Unselected fields are omitted from the result type.
91
- selectedUser?.role;
92
- await users.find({}).select();
93
- await users.find({}).select().first();
94
- const firstUser = await users.find({}).first();
95
- firstUser?.name;
96
- // @ts-expect-error A first query resolves to a document and cannot chain list methods.
97
- users.find({}).first().limit(1);
98
- // @ts-expect-error Deleted query modes are only available for soft-delete schemas.
99
- users.find().deleted('only');
100
- // @ts-expect-error Permanent deletion is only available for soft-delete schemas.
101
- users.purge({});
102
-
103
- const profileSchema = orm.schema({
104
- profile: orm.object({
105
- website: orm.string(),
106
- location: orm.object({ city: orm.string() }),
107
- }),
108
- });
109
- const profiles = db.model('profiles', profileSchema);
110
- await profiles.find({}).select(['profile.website', 'profile.location.city']);
111
- // @ts-expect-error Nested select paths must refer to declared object fields.
112
- await profiles.find({}).select(['profile.location.country']);
113
-
114
- const zodFeatures = orm.schema({
115
- email: orm.email(),
116
- website: orm.url(),
117
- secret: orm.string().optional().hidden(),
118
- });
119
- void zodFeatures;
120
-
121
- const accountSchema = orm.schema({
122
- name: orm.string(),
123
- password: orm.string().hidden(),
124
- });
125
- const accounts = db.model('accounts', accountSchema);
126
- const visibleAccounts = await accounts.find();
127
- visibleAccounts[0].name;
128
- // @ts-expect-error Hidden fields are omitted from default results.
129
- visibleAccounts[0].password;
130
- const allAccounts = await accounts.find().show(['password']);
131
- allAccounts[0].password;
132
- const explicitAccount = await accounts.find({}).select(['name']).show(['password']).first();
133
- explicitAccount?.password;
134
- // @ts-expect-error _id is always included and is not a selectable field.
135
- await accounts.find().select(['_id']);
136
- // @ts-expect-error Only hidden fields can be shown.
137
- await accounts.find().show(['name']);
138
-
139
- const relationGroupSchema = orm.schema({ name: orm.string() });
140
- const relationUserSchema = orm.schema({
141
- name: orm.string(),
142
- groupId: orm.objectId().optional(),
143
- });
144
- const relatedUserSchema = relationUserSchema.relations({
145
- groupId: () => relationGroupSchema,
146
- });
147
- void relatedUserSchema.relationMap.groupId;
148
- // @ts-expect-error Relations require an ObjectId field on the local schema.
149
- groupSchema.relations({ name: () => relationUserSchema });
150
- const relatedUsers = db.model('related-users', relatedUserSchema);
151
- const populatedUsers = await relatedUsers.find().populate([{ ref: 'groupId', select: ['name'] }]);
152
- populatedUsers[0].groupId?.name;
153
- // @ts-expect-error Population replaces the local ObjectId with the populated document.
154
- const groupId: ObjectId = populatedUsers[0].groupId;
155
-
156
- const schema = orm
157
- .defineSchemas({
158
- users: relationUserSchema,
159
- groups: relationGroupSchema,
160
- })
161
- .defineRelations({
162
- users: { groupId: 'groups' },
163
- })
164
- .defineScopes({
165
- users: {
166
- detail: [{ ref: 'groupId', select: ['name'] }],
167
- },
168
- });
169
- const registeredDb = createDatabase({
170
- uri: 'mongodb://127.0.0.1:27017',
171
- database: 'mongorm_registry_test',
172
- schema,
173
- });
174
- await registeredDb.users.find({});
175
- await registeredDb.groups.find({});
176
- // @ts-expect-error Only registered plural schema names are exposed as models.
177
- await registeredDb.user.find({});
178
-
179
- const scopedUserSchema = relationUserSchema
180
- .relations({ groupId: () => relationGroupSchema })
181
- .scopes({ detail: [{ ref: 'groupId', select: ['name'] }] });
182
- const scopedUsers = db.model('scoped-users', scopedUserSchema);
183
- const detailedUsers = await scopedUsers.find().with('detail');
184
- detailedUsers[0].groupId?.name;
185
- // @ts-expect-error Scope names are inferred from Schema.scopes().
186
- scopedUsers.find().with('summary');
187
- // @ts-expect-error A query cannot combine a named scope with explicit population.
188
- scopedUsers
189
- .find()
190
- .with('detail')
191
- .populate([{ ref: 'groupId' }]);
192
- // @ts-expect-error A query cannot combine explicit population with a named scope.
193
- scopedUsers
194
- .find()
195
- .populate([{ ref: 'groupId' }])
196
- .with('detail');
197
-
198
- await users.find({
199
- $or: [{ role: 'admin' }, { name: { $regex: /^Ada/ } }],
200
- });
201
- await users.find({
202
- $and: [{ role: { $ne: 'member' } }, { name: { $exists: true } }],
203
- });
204
- await users.find({ name: 'Ada' });
205
- await users.update({ role: 'member' }, { name: 'Ada Lovelace' });
206
- await users.delete({ role: 'member' });
207
- // @ts-expect-error Filters are narrowed to the schema's fields.
208
- await users.find({ unknown: true });
209
- // @ts-expect-error Operator values remain narrowed to the field's literal union.
210
- await users.find({ role: { $in: ['owner'] } });
211
- // @ts-expect-error Updates cannot add unknown fields.
212
- await users.update({}, { unknown: true });
213
-
214
- const metricSchema = orm.schema({ age: orm.number() });
215
- const metrics = db.model('metrics', metricSchema);
216
- await metrics.find({ age: { $gte: 18 } });
217
- // @ts-expect-error Number operators reject string values.
218
- await metrics.find({ age: { $gte: 'adult' } });
219
- // @ts-expect-error Logical filters still validate each branch.
220
- await users.find({ $or: [{ role: 'owner' }] });
221
- // @ts-expect-error Sort fields are narrowed to schema fields.
222
- await users.find({}).sort({ unknown: 'asc' });
223
- // @ts-expect-error Sort directions are restricted to asc/desc.
224
- await users.find({}).sort({ name: 'ascending' });
225
- // @ts-expect-error Numeric MongoDB sort directions are intentionally not part of the API.
226
- await users.find({}).sort({ name: 1 });
227
- // @ts-expect-error Skip requires a number.
228
- await users.find({}).skip('1');
229
- // @ts-expect-error Limit requires a number.
230
- await users.find({}).limit('2');
231
- // @ts-expect-error Estimate must be a boolean.
232
- await users.find({}).count('true');
@@ -1,55 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { orm } from '../../src/index.js';
4
-
5
- describe('schema options', () => {
6
- it('adds managed timestamp and soft-delete fields', () => {
7
- const account = orm
8
- .schema({ name: orm.string() })
9
- .options({ timestamps: true, softdelete: true });
10
-
11
- const parsed = account.parse({ name: 'Ada' });
12
-
13
- expect(parsed.name).toBe('Ada');
14
- expect(parsed.createdAt).toBeInstanceOf(Date);
15
- expect(parsed.updatedAt).toBeInstanceOf(Date);
16
- expect(parsed.deletedAt).toBeNull();
17
- expect(account.optionsConfig).toEqual({ timestamps: true, softdelete: true });
18
- });
19
-
20
- it('rejects managed field name collisions', () => {
21
- expect(() => orm.schema({ createdAt: orm.string() }).options({ timestamps: true })).toThrow(
22
- 'managed by Mongorm',
23
- );
24
- expect(() => orm.schema({ deletedAt: orm.date() }).options({ softdelete: true })).toThrow(
25
- 'managed by Mongorm',
26
- );
27
- });
28
-
29
- it('uses defaults for optional input and required parsed output', () => {
30
- const account = orm.schema({ status: orm.string().default('pending') });
31
- const parsed = account.parse({});
32
-
33
- expect(parsed.status).toBe('pending');
34
- });
35
-
36
- it('stores typed MongoDB index definitions for explicit synchronization', () => {
37
- const account = orm
38
- .schema({ email: orm.string(), tenantId: orm.string() })
39
- .indexes([
40
- { fields: { tenantId: 1, email: 1 } },
41
- { fields: { email: 1 }, options: { unique: true, name: 'account_email_unique' } },
42
- ]);
43
-
44
- expect(account.indexDefinitions).toEqual([
45
- { fields: { tenantId: 1, email: 1 } },
46
- { fields: { email: 1 }, options: { unique: true, name: 'account_email_unique' } },
47
- ]);
48
- });
49
-
50
- it('rejects empty index definitions', () => {
51
- expect(() => orm.schema({ email: orm.string() }).indexes([{ fields: {} }])).toThrow(
52
- 'at least one field',
53
- );
54
- });
55
- });
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "strict": true,
7
- "skipLibCheck": true,
8
- "types": ["node"],
9
- "rootDir": ".",
10
- "noEmit": true
11
- },
12
- "include": ["src", "tests"]
13
- }
package/tsdown.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from 'tsdown';
2
-
3
- export default defineConfig({
4
- entry: 'src/index.ts',
5
- dts: true,
6
- format: ['esm'],
7
- clean: true,
8
- });
package/vitest.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: 'node',
6
- include: ['tests/**/*.test.ts'],
7
- },
8
- });