@web-ts-toolkit/moo 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  Apache License
3
2
  Version 2.0, January 2004
4
3
  http://www.apache.org/licenses/
@@ -187,7 +186,7 @@
187
186
  same "printed page" as the copyright notice for easier
188
187
  identification within third-party archives.
189
188
 
190
- Copyright © 2017, Province of British Columbia, Canada.
189
+ Copyright Junmin Ahn
191
190
 
192
191
  Licensed under the Apache License, Version 2.0 (the "License");
193
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -14,9 +14,12 @@ This package includes:
14
14
  npm install mongoose @web-ts-toolkit/moo
15
15
  ```
16
16
 
17
- ## Usage
17
+ ## Documentation
18
18
 
19
- ### Schema helpers
19
+ - Full package documentation lives in `website/docs/packages/moo.md`.
20
+ - Use the Docusaurus site in `website` for the full examples.
21
+
22
+ ## Minimal Example
20
23
 
21
24
  ```ts
22
25
  import { Schema } from 'mongoose';
@@ -27,101 +30,3 @@ const userSchema = new Schema({
27
30
  username: uniqueEmptiableString('username'),
28
31
  });
29
32
  ```
30
-
31
- ### ObjectId checks
32
-
33
- ```ts
34
- import { isObjectId } from '@web-ts-toolkit/moo';
35
-
36
- if (!isObjectId(value)) {
37
- throw new Error('expected a valid MongoDB ObjectId');
38
- }
39
- ```
40
-
41
- ### Model function plugin
42
-
43
- ```ts
44
- import mongoose, { type Model } from 'mongoose';
45
- import {
46
- type ModelDocument,
47
- type ModelFunctionInstanceMethods,
48
- type ModelFunctionStaticMethods,
49
- modelFunctionPlugin,
50
- } from '@web-ts-toolkit/moo';
51
-
52
- type Cart = {
53
- name: string;
54
- price: number;
55
- };
56
-
57
- type CartDocument = ModelDocument<Cart, CartMethods>;
58
-
59
- type CartMethods = ModelFunctionInstanceMethods<'applyDiscount', [suffix: string, priceChange: number], CartDocument>;
60
-
61
- type CartModel = Model<Cart, {}, CartMethods> &
62
- ModelFunctionStaticMethods<'applyDiscount', CartDocument, [suffix: string, priceChange: number], CartDocument>;
63
-
64
- const cartSchema = new mongoose.Schema<Cart, CartModel, CartMethods>({
65
- name: { type: String, required: true },
66
- price: { type: Number, required: true },
67
- });
68
-
69
- cartSchema.plugin(modelFunctionPlugin, {
70
- fnName: 'applyDiscount',
71
- fn: (cart: CartDocument, suffix: string, priceChange: number) => {
72
- cart.name = `${cart.name}-${suffix}`;
73
- cart.price += priceChange;
74
- return cart;
75
- },
76
- });
77
- ```
78
-
79
- ### Cascade delete plugin
80
-
81
- ```ts
82
- import mongoose, { type Model, type Types } from 'mongoose';
83
- import {
84
- type CascadeDeleteDependencyMap,
85
- type CascadeDeleteDocumentMethods,
86
- type CascadeDeleteModelStatics,
87
- cascadeDeletePlugin,
88
- } from '@web-ts-toolkit/moo/plugins';
89
-
90
- const referenceModelName = 'Reference';
91
-
92
- type Reference = {
93
- name: string;
94
- };
95
-
96
- type File = {
97
- refs: Types.ObjectId[];
98
- };
99
-
100
- type FileMethods = CascadeDeleteDocumentMethods<typeof referenceModelName, Reference>;
101
-
102
- type FileModel = Model<File, {}, FileMethods> & CascadeDeleteModelStatics<typeof referenceModelName, Reference>;
103
-
104
- type FileDependents = CascadeDeleteDependencyMap<typeof referenceModelName, Reference>;
105
-
106
- const fileSchema = new mongoose.Schema<File, FileModel, FileMethods>({
107
- refs: [{ type: mongoose.Schema.Types.ObjectId, ref: referenceModelName }],
108
- });
109
-
110
- fileSchema.plugin(cascadeDeletePlugin, {
111
- model: referenceModelName,
112
- localField: 'refs',
113
- foreignField: '_id',
114
- });
115
-
116
- const File = mongoose.model<File, FileModel>('File', fileSchema);
117
-
118
- async function example(file: mongoose.HydratedDocument<File, FileMethods>) {
119
- const dependents = (await file.findDependents()) as FileDependents;
120
- const references = await file.findDependents(referenceModelName);
121
- const orphans = await File.findOrphans(referenceModelName);
122
-
123
- dependents.Reference;
124
- references?.[0]?.name;
125
- orphans?.[0]?.name;
126
- }
127
- ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@web-ts-toolkit/moo",
3
3
  "description": "Mongoose helpers for schema fields, ObjectId checks, and document plugins",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "keywords": [
6
6
  "mongoose",
7
7
  "mongodb",
@@ -30,6 +30,12 @@
30
30
  "require": "./schema.js",
31
31
  "default": "./schema.js"
32
32
  },
33
+ "./utils": {
34
+ "types": "./utils/index.d.ts",
35
+ "import": "./utils/index.mjs",
36
+ "require": "./utils/index.js",
37
+ "default": "./utils/index.js"
38
+ },
33
39
  "./plugins": {
34
40
  "types": "./plugins/index.d.ts",
35
41
  "import": "./plugins/index.mjs",
@@ -52,7 +58,10 @@
52
58
  "peerDependencies": {
53
59
  "mongoose": ">=8.0.0"
54
60
  },
55
- "author": "Junmin Dev",
61
+ "dependencies": {
62
+ "@web-ts-toolkit/utils": "0.3.0"
63
+ },
64
+ "author": "Junmin Ahn",
56
65
  "bugs": {
57
66
  "url": "https://github.com/egose/web-ts-toolkit/issues"
58
67
  },
@@ -32,22 +32,11 @@ __export(cascade_delete_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(cascade_delete_exports);
34
34
  var import_mongoose = __toESM(require("mongoose"));
35
- const deleteOneSupported = Number.parseInt(import_mongoose.default.version.split(".")[0] ?? "0", 10) >= 7;
36
- const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
37
- const isFunction = (value) => typeof value === "function";
38
- const getReferenceModel = (value) => {
39
- if (Array.isArray(value)) {
40
- return getReferenceModel(value[0]);
41
- }
42
- if (!isPlainObject(value) || typeof value.ref !== "string") {
43
- return null;
44
- }
45
- return value.ref;
46
- };
47
- const isReference = (value, modelName) => getReferenceModel(value) === modelName;
35
+ var import_utils = require("@web-ts-toolkit/utils");
36
+ var import_utils2 = require("../utils");
48
37
  const resolveFilter = (value, document) => {
49
- const result = isFunction(value) ? value(document) : value;
50
- if (!isPlainObject(result)) {
38
+ const result = (0, import_utils.isFunction)(value) ? value(document) : value;
39
+ if (!(0, import_utils.isPlainObject)(result)) {
51
40
  return null;
52
41
  }
53
42
  return result;
@@ -68,8 +57,18 @@ const applyQueryOptions = (query, options) => {
68
57
  }
69
58
  return builder;
70
59
  };
71
- const supportsDeleteOne = (value) => isPlainObject(value) && typeof value.deleteOne === "function";
72
- const supportsRemove = (value) => isPlainObject(value) && typeof value.remove === "function";
60
+ const supportsDeleteOne = (value) => (0, import_utils.isObject)(value) && typeof value.deleteOne === "function";
61
+ const supportsRemove = (value) => (0, import_utils.isObject)(value) && typeof value.remove === "function";
62
+ const mergeResults = (previousResults, modelName, currentResults) => {
63
+ const resultMap = (0, import_utils.isPlainObject)(previousResults) ? previousResults : {};
64
+ if (currentResults === null) {
65
+ return resultMap;
66
+ }
67
+ return {
68
+ ...resultMap,
69
+ [modelName]: currentResults
70
+ };
71
+ };
73
72
  function cascadeDeletePlugin(schema, options) {
74
73
  const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
75
74
  const findDependents = async function(queryOptions) {
@@ -86,7 +85,7 @@ function cascadeDeletePlugin(schema, options) {
86
85
  ...extraFilter
87
86
  };
88
87
  }
89
- if (!query || !isPlainObject(query)) {
88
+ if (!query || !(0, import_utils.isPlainObject)(query)) {
90
89
  return [];
91
90
  }
92
91
  return await applyQueryOptions(Target.find(query), queryOptions);
@@ -96,7 +95,7 @@ function cascadeDeletePlugin(schema, options) {
96
95
  const Target = import_mongoose.default.model(model);
97
96
  const schemaValue = Target.schema.obj[foreignField];
98
97
  if (!schemaValue) return null;
99
- const isMyRef = isReference(schemaValue, this.modelName);
98
+ const isMyRef = (0, import_utils2.isReference)(schemaValue, this.modelName);
100
99
  if (!isMyRef) return null;
101
100
  const ids = await this.distinct("_id");
102
101
  const extraFilter = resolveFilter(extraForeignFilter) ?? {};
@@ -120,23 +119,9 @@ function cascadeDeletePlugin(schema, options) {
120
119
  })
121
120
  );
122
121
  };
123
- if (deleteOneSupported) {
124
- schema.post("deleteOne", { document: true, query: false }, async function() {
125
- try {
126
- await deleteDependents.call(this);
127
- } catch (err) {
128
- console.error(err);
129
- }
130
- });
131
- } else {
132
- schema.post("remove", async function() {
133
- try {
134
- await deleteDependents.call(this);
135
- } catch (err) {
136
- console.error(err);
137
- }
138
- });
139
- }
122
+ schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
123
+ await deleteDependents.call(this);
124
+ });
140
125
  const methodFnName = "findDependents";
141
126
  const prevMethodFn = schema.methods[methodFnName];
142
127
  schema.method(methodFnName, async function methodFn(modelName) {
@@ -147,10 +132,7 @@ function cascadeDeletePlugin(schema, options) {
147
132
  return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
148
133
  }
149
134
  const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
150
- return {
151
- ...isPlainObject(previousResults) ? previousResults : {},
152
- [model]: await findDependents.call(this, {})
153
- };
135
+ return mergeResults(previousResults, model, await findDependents.call(this, {}));
154
136
  });
155
137
  const staticFnName = "findOrphans";
156
138
  const prevStaticFn = schema.statics[staticFnName];
@@ -163,13 +145,7 @@ function cascadeDeletePlugin(schema, options) {
163
145
  }
164
146
  const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
165
147
  const currentResults = await findOrphans.call(this, {});
166
- if (!currentResults) {
167
- return isPlainObject(previousResults) ? previousResults : {};
168
- }
169
- return {
170
- ...isPlainObject(previousResults) ? previousResults : {},
171
- [model]: currentResults
172
- };
148
+ return mergeResults(previousResults, model, currentResults);
173
149
  });
174
150
  }
175
151
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,17 +1,6 @@
1
1
  import mongoose from "mongoose";
2
- const deleteOneSupported = Number.parseInt(mongoose.version.split(".")[0] ?? "0", 10) >= 7;
3
- const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
4
- const isFunction = (value) => typeof value === "function";
5
- const getReferenceModel = (value) => {
6
- if (Array.isArray(value)) {
7
- return getReferenceModel(value[0]);
8
- }
9
- if (!isPlainObject(value) || typeof value.ref !== "string") {
10
- return null;
11
- }
12
- return value.ref;
13
- };
14
- const isReference = (value, modelName) => getReferenceModel(value) === modelName;
2
+ import { isFunction, isObject, isPlainObject } from "@web-ts-toolkit/utils";
3
+ import { isReference } from "../utils";
15
4
  const resolveFilter = (value, document) => {
16
5
  const result = isFunction(value) ? value(document) : value;
17
6
  if (!isPlainObject(result)) {
@@ -35,8 +24,18 @@ const applyQueryOptions = (query, options) => {
35
24
  }
36
25
  return builder;
37
26
  };
38
- const supportsDeleteOne = (value) => isPlainObject(value) && typeof value.deleteOne === "function";
39
- const supportsRemove = (value) => isPlainObject(value) && typeof value.remove === "function";
27
+ const supportsDeleteOne = (value) => isObject(value) && typeof value.deleteOne === "function";
28
+ const supportsRemove = (value) => isObject(value) && typeof value.remove === "function";
29
+ const mergeResults = (previousResults, modelName, currentResults) => {
30
+ const resultMap = isPlainObject(previousResults) ? previousResults : {};
31
+ if (currentResults === null) {
32
+ return resultMap;
33
+ }
34
+ return {
35
+ ...resultMap,
36
+ [modelName]: currentResults
37
+ };
38
+ };
40
39
  function cascadeDeletePlugin(schema, options) {
41
40
  const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
42
41
  const findDependents = async function(queryOptions) {
@@ -87,23 +86,9 @@ function cascadeDeletePlugin(schema, options) {
87
86
  })
88
87
  );
89
88
  };
90
- if (deleteOneSupported) {
91
- schema.post("deleteOne", { document: true, query: false }, async function() {
92
- try {
93
- await deleteDependents.call(this);
94
- } catch (err) {
95
- console.error(err);
96
- }
97
- });
98
- } else {
99
- schema.post("remove", async function() {
100
- try {
101
- await deleteDependents.call(this);
102
- } catch (err) {
103
- console.error(err);
104
- }
105
- });
106
- }
89
+ schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
90
+ await deleteDependents.call(this);
91
+ });
107
92
  const methodFnName = "findDependents";
108
93
  const prevMethodFn = schema.methods[methodFnName];
109
94
  schema.method(methodFnName, async function methodFn(modelName) {
@@ -114,10 +99,7 @@ function cascadeDeletePlugin(schema, options) {
114
99
  return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
115
100
  }
116
101
  const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
117
- return {
118
- ...isPlainObject(previousResults) ? previousResults : {},
119
- [model]: await findDependents.call(this, {})
120
- };
102
+ return mergeResults(previousResults, model, await findDependents.call(this, {}));
121
103
  });
122
104
  const staticFnName = "findOrphans";
123
105
  const prevStaticFn = schema.statics[staticFnName];
@@ -130,13 +112,7 @@ function cascadeDeletePlugin(schema, options) {
130
112
  }
131
113
  const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
132
114
  const currentResults = await findOrphans.call(this, {});
133
- if (!currentResults) {
134
- return isPlainObject(previousResults) ? previousResults : {};
135
- }
136
- return {
137
- ...isPlainObject(previousResults) ? previousResults : {},
138
- [model]: currentResults
139
- };
115
+ return mergeResults(previousResults, model, currentResults);
140
116
  });
141
117
  }
142
118
  export {
@@ -32,20 +32,23 @@ __export(model_function_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(model_function_exports);
34
34
  var import_mongoose = __toESM(require("mongoose"));
35
+ const invokePluginFunction = (fn, document, args) => {
36
+ return fn.call(import_mongoose.default, document, ...args);
37
+ };
35
38
  function modelFunctionPlugin(schema, options) {
36
39
  const { fnName, fn } = options;
37
40
  schema.static(fnName, function staticFn(doc, ...args) {
38
- return fn.call(import_mongoose.default, doc, ...args);
41
+ return invokePluginFunction(fn, doc, args);
39
42
  });
40
43
  schema.method(fnName, function methodFn(...args) {
41
- return fn.call(import_mongoose.default, this, ...args);
44
+ return invokePluginFunction(fn, this, args);
42
45
  });
43
46
  schema.static(
44
47
  `${fnName}ById`,
45
48
  async function staticByIdFn(docId, ...args) {
46
49
  const model = await this.findById(docId);
47
50
  if (!model) return null;
48
- return fn.call(import_mongoose.default, model, ...args);
51
+ return invokePluginFunction(fn, model, args);
49
52
  }
50
53
  );
51
54
  }
@@ -1,18 +1,21 @@
1
1
  import mongoose from "mongoose";
2
+ const invokePluginFunction = (fn, document, args) => {
3
+ return fn.call(mongoose, document, ...args);
4
+ };
2
5
  function modelFunctionPlugin(schema, options) {
3
6
  const { fnName, fn } = options;
4
7
  schema.static(fnName, function staticFn(doc, ...args) {
5
- return fn.call(mongoose, doc, ...args);
8
+ return invokePluginFunction(fn, doc, args);
6
9
  });
7
10
  schema.method(fnName, function methodFn(...args) {
8
- return fn.call(mongoose, this, ...args);
11
+ return invokePluginFunction(fn, this, args);
9
12
  });
10
13
  schema.static(
11
14
  `${fnName}ById`,
12
15
  async function staticByIdFn(docId, ...args) {
13
16
  const model = await this.findById(docId);
14
17
  if (!model) return null;
15
- return fn.call(mongoose, model, ...args);
18
+ return invokePluginFunction(fn, model, args);
16
19
  }
17
20
  );
18
21
  }
@@ -0,0 +1,12 @@
1
+ import { Schema } from 'mongoose';
2
+
3
+ type ReferenceShape = {
4
+ ref?: unknown;
5
+ refPath?: unknown;
6
+ type?: unknown;
7
+ };
8
+ declare const isSchema: (value: unknown) => value is Schema;
9
+ declare const isObjectIdType: (value: unknown) => boolean;
10
+ declare const isReference: (value: unknown, ref?: string) => value is ReferenceShape;
11
+
12
+ export { type ReferenceShape, isObjectIdType, isReference, isSchema };
@@ -0,0 +1,12 @@
1
+ import { Schema } from 'mongoose';
2
+
3
+ type ReferenceShape = {
4
+ ref?: unknown;
5
+ refPath?: unknown;
6
+ type?: unknown;
7
+ };
8
+ declare const isSchema: (value: unknown) => value is Schema;
9
+ declare const isObjectIdType: (value: unknown) => boolean;
10
+ declare const isReference: (value: unknown, ref?: string) => value is ReferenceShape;
11
+
12
+ export { type ReferenceShape, isObjectIdType, isReference, isSchema };
package/utils/index.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var utils_exports = {};
20
+ __export(utils_exports, {
21
+ isObjectIdType: () => isObjectIdType,
22
+ isReference: () => isReference,
23
+ isSchema: () => isSchema
24
+ });
25
+ module.exports = __toCommonJS(utils_exports);
26
+ var import_mongoose = require("mongoose");
27
+ var import_utils = require("@web-ts-toolkit/utils");
28
+ const isSchema = (value) => value instanceof import_mongoose.Schema;
29
+ const isObjectIdType = (value) => value === "ObjectId" || value === import_mongoose.Schema.Types.ObjectId;
30
+ const hasReferenceTarget = (value) => (0, import_utils.isString)(value.ref) || (0, import_utils.isFunction)(value.ref) || (0, import_utils.isString)(value.refPath);
31
+ const isReference = (value, ref) => {
32
+ if (Array.isArray(value)) {
33
+ return value.length > 0 ? isReference(value[0], ref) : false;
34
+ }
35
+ if (!(0, import_utils.isPlainObject)(value) || !isObjectIdType(value.type)) {
36
+ return false;
37
+ }
38
+ if (ref === void 0) {
39
+ return hasReferenceTarget(value);
40
+ }
41
+ return value.ref === ref;
42
+ };
43
+ // Annotate the CommonJS export names for ESM import in node:
44
+ 0 && (module.exports = {
45
+ isObjectIdType,
46
+ isReference,
47
+ isSchema
48
+ });
@@ -0,0 +1,22 @@
1
+ import { Schema } from "mongoose";
2
+ import { isFunction, isPlainObject, isString } from "@web-ts-toolkit/utils";
3
+ const isSchema = (value) => value instanceof Schema;
4
+ const isObjectIdType = (value) => value === "ObjectId" || value === Schema.Types.ObjectId;
5
+ const hasReferenceTarget = (value) => isString(value.ref) || isFunction(value.ref) || isString(value.refPath);
6
+ const isReference = (value, ref) => {
7
+ if (Array.isArray(value)) {
8
+ return value.length > 0 ? isReference(value[0], ref) : false;
9
+ }
10
+ if (!isPlainObject(value) || !isObjectIdType(value.type)) {
11
+ return false;
12
+ }
13
+ if (ref === void 0) {
14
+ return hasReferenceTarget(value);
15
+ }
16
+ return value.ref === ref;
17
+ };
18
+ export {
19
+ isObjectIdType,
20
+ isReference,
21
+ isSchema
22
+ };