@prostojs/mate 0.4.0 → 0.5.1

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/index.cjs CHANGED
@@ -1,11 +1,44 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
+ //#region src/module-identity.ts
4
+ const MATE_MODULE_IDENTITY_KEY = Symbol.for("prostojs:mate:module-identity");
5
+ function getModulePath() {
6
+ try {
7
+ return require("url").pathToFileURL(__filename).href;
8
+ } catch {
9
+ return;
10
+ }
11
+ }
12
+ function formatIdentity(identity) {
13
+ const version = identity.version ? `v${identity.version}` : "unknown version";
14
+ return identity.path ? `${version} at ${identity.path}` : version;
15
+ }
16
+ /**
17
+ * Stamps `globalThis` with this copy's identity and warns if another
18
+ * copy of @prostojs/mate was already loaded.
19
+ *
20
+ * Invoked once at module scope from index.ts, so importing the package
21
+ * performs the check.
22
+ */
23
+ function warnOnDuplicateMate() {
24
+ const g = globalThis;
25
+ const identity = {};
26
+ identity.version = "0.5.0";
27
+ const path = getModulePath();
28
+ if (path) identity.path = path;
29
+ const first = g[MATE_MODULE_IDENTITY_KEY];
30
+ if (first) console.warn(`[mate] A second copy of @prostojs/mate was loaded (${formatIdentity(first)}, now ${formatIdentity(identity)}). Decorator metadata is stored per-copy and will NOT be visible across copies. Check your bundler/dedupe settings.`);
31
+ else g[MATE_MODULE_IDENTITY_KEY] = identity;
32
+ }
33
+
34
+ //#endregion
3
35
  //#region src/utils/helpers.ts
36
+ const hasOwn = Object.prototype.hasOwnProperty;
4
37
  function getConstructor(instance) {
5
38
  return isConstructor(instance) ? instance : instance.constructor ? instance.constructor : Object.getPrototypeOf(instance).constructor;
6
39
  }
7
40
  function isConstructor(v) {
8
- return typeof v === "function" && Object.getOwnPropertyDescriptor(v, "prototype") !== void 0 && Object.getOwnPropertyDescriptor(v, "caller") === void 0 && !!v.name;
41
+ return typeof v === "function" && hasOwn.call(v, "prototype") && !hasOwn.call(v, "caller") && !!v.name;
9
42
  }
10
43
 
11
44
  //#endregion
@@ -76,9 +109,11 @@ var Mate = class {
76
109
  _cleanup() {
77
110
  Reflect._cleanup?.call(Reflect);
78
111
  this._readCache = void 0;
112
+ this._readCacheNonCtor = void 0;
79
113
  }
80
114
  set(args, key, value, isArray) {
81
115
  this._readCache = void 0;
116
+ this._readCacheNonCtor = void 0;
82
117
  let level = "CLASS";
83
118
  const newArgs = args.level === "CLASS" ? { target: getConstructor(args.target) } : args.level === "PROP" ? {
84
119
  target: args.target,
@@ -124,11 +159,19 @@ var Mate = class {
124
159
  Reflect.defineMetadata(this.workspace, meta, newArgs.target, newArgs.propKey);
125
160
  }
126
161
  read(target, propKey) {
127
- const constructor = isConstructor(target) ? target : getConstructor(target);
128
- const proto = constructor.prototype;
129
162
  const propKeyStr = propKey;
130
- const ctorCache = this._readCache?.get(constructor);
131
- if (ctorCache?.has(propKeyStr)) return ctorCache.get(propKeyStr);
163
+ const ctorCacheRoot = this._readCache;
164
+ if (ctorCacheRoot !== void 0 && typeof target === "function") {
165
+ const hit = readCached(ctorCacheRoot.get(target), propKeyStr);
166
+ if (hit !== MISS) return hit;
167
+ }
168
+ const isConstr = isConstructor(target);
169
+ const constructor = isConstr ? target : getConstructor(target);
170
+ if (!isConstr) {
171
+ const hit = readCached(ctorCacheRoot?.get(constructor) ?? this._readCacheNonCtor?.get(constructor), propKeyStr);
172
+ if (hit !== MISS) return hit;
173
+ }
174
+ const proto = constructor.prototype;
132
175
  let ownMeta = Reflect.getOwnMetadata(this.workspace, typeof propKey === "string" ? proto : constructor, propKey);
133
176
  if (ownMeta && propKey === void 0 && ownMeta.params === void 0) {
134
177
  const parent = Object.getPrototypeOf(constructor);
@@ -160,7 +203,7 @@ var Mate = class {
160
203
  }
161
204
  }
162
205
  }
163
- const cache = this._readCache ??= /* @__PURE__ */ new WeakMap();
206
+ const cache = isConstr || typeof constructor !== "function" || isConstructor(constructor) ? this._readCache ??= /* @__PURE__ */ new WeakMap() : this._readCacheNonCtor ??= /* @__PURE__ */ new WeakMap();
164
207
  let tc = cache.get(constructor);
165
208
  if (!tc) {
166
209
  tc = /* @__PURE__ */ new Map();
@@ -197,8 +240,20 @@ var Mate = class {
197
240
  }
198
241
  };
199
242
  const fnProto = Object.getPrototypeOf(Function);
243
+ const MISS = Symbol("mate.read.miss");
244
+ function readCached(tc, propKey) {
245
+ if (tc === void 0) return MISS;
246
+ const v = tc.get(propKey);
247
+ return v !== void 0 || tc.has(propKey) ? v : MISS;
248
+ }
249
+
250
+ //#endregion
251
+ //#region src/index.ts
252
+ warnOnDuplicateMate();
200
253
 
201
254
  //#endregion
255
+ exports.MATE_MODULE_IDENTITY_KEY = MATE_MODULE_IDENTITY_KEY;
202
256
  exports.Mate = Mate;
203
257
  exports.getConstructor = getConstructor;
204
- exports.isConstructor = isConstructor;
258
+ exports.isConstructor = isConstructor;
259
+ exports.warnOnDuplicateMate = warnOnDuplicateMate;
package/dist/index.d.ts CHANGED
@@ -46,7 +46,19 @@ declare class Mate<TClass extends TObject = TMateClassMeta<TMateParamMeta>, TPro
46
46
  protected workspace: string;
47
47
  protected options: TMateOptions<TClass, TProp>;
48
48
  protected logger: TConsoleBase;
49
+ /**
50
+ * Read cache keyed by the resolved constructor. Invariant: every function
51
+ * key here passed `isConstructor` when stored, so a hit on a function
52
+ * `target` itself proves `target` is that constructor — `read` probes
53
+ * it before running `isConstructor`.
54
+ */
49
55
  protected _readCache?: WeakMap<object, Map<string | undefined, unknown>>;
56
+ /**
57
+ * Read cache for resolved "constructors" that are functions failing
58
+ * `isConstructor` (e.g. an anonymous class reached through an instance's
59
+ * `.constructor`). Kept apart so they cannot satisfy the fast probe.
60
+ */
61
+ protected _readCacheNonCtor?: WeakMap<object, Map<string | undefined, unknown>>;
50
62
  constructor(workspace: string, options?: TMateOptions<TClass, TProp>);
51
63
  /**
52
64
  * Cleanup function to reset metadata
@@ -70,8 +82,33 @@ declare class Mate<TClass extends TObject = TMateClassMeta<TMateParamMeta>, TPro
70
82
  }
71
83
  type ArrayElementType<T> = T extends (infer E)[] ? E : never;
72
84
  //#endregion
85
+ //#region dts-build/module-identity.d.ts
86
+ /**
87
+ * Duplicate-copy detection for @prostojs/mate
88
+ *
89
+ * Mate stores class metadata in WeakMaps keyed by constructor reference.
90
+ * If a bundler loads two copies of mate (or of a decorated class),
91
+ * decorators run against one copy while lookups hit the other, and
92
+ * metadata silently "disappears". The first loaded copy stamps
93
+ * `globalThis` under a global symbol; any subsequent copy finds the
94
+ * stamp and warns.
95
+ */
96
+ interface TMateModuleIdentity {
97
+ version?: string;
98
+ path?: string;
99
+ }
100
+ declare const MATE_MODULE_IDENTITY_KEY: unique symbol;
101
+ /**
102
+ * Stamps `globalThis` with this copy's identity and warns if another
103
+ * copy of @prostojs/mate was already loaded.
104
+ *
105
+ * Invoked once at module scope from index.ts, so importing the package
106
+ * performs the check.
107
+ */
108
+ declare function warnOnDuplicateMate(): void;
109
+ //#endregion
73
110
  //#region dts-build/utils/helpers.d.ts
74
111
  declare function getConstructor<T = TAny>(instance: T): TFunction;
75
112
  declare function isConstructor<T = TAny>(v: T): v is T & TClassConstructor;
76
113
  //#endregion
77
- export { Mate, TMateClassMeta, TMateOptions, TMateParamMeta, TMatePropMeta, TMergedDecoratorArgs, getConstructor, isConstructor };
114
+ export { MATE_MODULE_IDENTITY_KEY, Mate, TMateClassMeta, TMateModuleIdentity, TMateOptions, TMateParamMeta, TMatePropMeta, TMergedDecoratorArgs, getConstructor, isConstructor, warnOnDuplicateMate };
package/dist/index.mjs CHANGED
@@ -1,9 +1,42 @@
1
+ //#region src/module-identity.ts
2
+ const MATE_MODULE_IDENTITY_KEY = Symbol.for("prostojs:mate:module-identity");
3
+ function getModulePath() {
4
+ try {
5
+ return import.meta.url;
6
+ } catch {
7
+ return;
8
+ }
9
+ }
10
+ function formatIdentity(identity) {
11
+ const version = identity.version ? `v${identity.version}` : "unknown version";
12
+ return identity.path ? `${version} at ${identity.path}` : version;
13
+ }
14
+ /**
15
+ * Stamps `globalThis` with this copy's identity and warns if another
16
+ * copy of @prostojs/mate was already loaded.
17
+ *
18
+ * Invoked once at module scope from index.ts, so importing the package
19
+ * performs the check.
20
+ */
21
+ function warnOnDuplicateMate() {
22
+ const g = globalThis;
23
+ const identity = {};
24
+ identity.version = "0.5.0";
25
+ const path = getModulePath();
26
+ if (path) identity.path = path;
27
+ const first = g[MATE_MODULE_IDENTITY_KEY];
28
+ if (first) console.warn(`[mate] A second copy of @prostojs/mate was loaded (${formatIdentity(first)}, now ${formatIdentity(identity)}). Decorator metadata is stored per-copy and will NOT be visible across copies. Check your bundler/dedupe settings.`);
29
+ else g[MATE_MODULE_IDENTITY_KEY] = identity;
30
+ }
31
+
32
+ //#endregion
1
33
  //#region src/utils/helpers.ts
34
+ const hasOwn = Object.prototype.hasOwnProperty;
2
35
  function getConstructor(instance) {
3
36
  return isConstructor(instance) ? instance : instance.constructor ? instance.constructor : Object.getPrototypeOf(instance).constructor;
4
37
  }
5
38
  function isConstructor(v) {
6
- return typeof v === "function" && Object.getOwnPropertyDescriptor(v, "prototype") !== void 0 && Object.getOwnPropertyDescriptor(v, "caller") === void 0 && !!v.name;
39
+ return typeof v === "function" && hasOwn.call(v, "prototype") && !hasOwn.call(v, "caller") && !!v.name;
7
40
  }
8
41
 
9
42
  //#endregion
@@ -74,9 +107,11 @@ var Mate = class {
74
107
  _cleanup() {
75
108
  Reflect._cleanup?.call(Reflect);
76
109
  this._readCache = void 0;
110
+ this._readCacheNonCtor = void 0;
77
111
  }
78
112
  set(args, key, value, isArray) {
79
113
  this._readCache = void 0;
114
+ this._readCacheNonCtor = void 0;
80
115
  let level = "CLASS";
81
116
  const newArgs = args.level === "CLASS" ? { target: getConstructor(args.target) } : args.level === "PROP" ? {
82
117
  target: args.target,
@@ -122,11 +157,19 @@ var Mate = class {
122
157
  Reflect.defineMetadata(this.workspace, meta, newArgs.target, newArgs.propKey);
123
158
  }
124
159
  read(target, propKey) {
125
- const constructor = isConstructor(target) ? target : getConstructor(target);
126
- const proto = constructor.prototype;
127
160
  const propKeyStr = propKey;
128
- const ctorCache = this._readCache?.get(constructor);
129
- if (ctorCache?.has(propKeyStr)) return ctorCache.get(propKeyStr);
161
+ const ctorCacheRoot = this._readCache;
162
+ if (ctorCacheRoot !== void 0 && typeof target === "function") {
163
+ const hit = readCached(ctorCacheRoot.get(target), propKeyStr);
164
+ if (hit !== MISS) return hit;
165
+ }
166
+ const isConstr = isConstructor(target);
167
+ const constructor = isConstr ? target : getConstructor(target);
168
+ if (!isConstr) {
169
+ const hit = readCached(ctorCacheRoot?.get(constructor) ?? this._readCacheNonCtor?.get(constructor), propKeyStr);
170
+ if (hit !== MISS) return hit;
171
+ }
172
+ const proto = constructor.prototype;
130
173
  let ownMeta = Reflect.getOwnMetadata(this.workspace, typeof propKey === "string" ? proto : constructor, propKey);
131
174
  if (ownMeta && propKey === void 0 && ownMeta.params === void 0) {
132
175
  const parent = Object.getPrototypeOf(constructor);
@@ -158,7 +201,7 @@ var Mate = class {
158
201
  }
159
202
  }
160
203
  }
161
- const cache = this._readCache ??= /* @__PURE__ */ new WeakMap();
204
+ const cache = isConstr || typeof constructor !== "function" || isConstructor(constructor) ? this._readCache ??= /* @__PURE__ */ new WeakMap() : this._readCacheNonCtor ??= /* @__PURE__ */ new WeakMap();
162
205
  let tc = cache.get(constructor);
163
206
  if (!tc) {
164
207
  tc = /* @__PURE__ */ new Map();
@@ -195,6 +238,16 @@ var Mate = class {
195
238
  }
196
239
  };
197
240
  const fnProto = Object.getPrototypeOf(Function);
241
+ const MISS = Symbol("mate.read.miss");
242
+ function readCached(tc, propKey) {
243
+ if (tc === void 0) return MISS;
244
+ const v = tc.get(propKey);
245
+ return v !== void 0 || tc.has(propKey) ? v : MISS;
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/index.ts
250
+ warnOnDuplicateMate();
198
251
 
199
252
  //#endregion
200
- export { Mate, getConstructor, isConstructor };
253
+ export { MATE_MODULE_IDENTITY_KEY, Mate, getConstructor, isConstructor, warnOnDuplicateMate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prostojs/mate",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "MATE is TS Metadata Organizer",
5
5
  "keywords": [
6
6
  "decorators",