@mtorre4580/is-deprecated 0.0.2 → 1.0.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/README.md +145 -61
- package/dist/index.cjs +347 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +336 -0
- package/package.json +74 -37
- package/dist/is-deprecated.es.js +0 -28
- package/dist/is-deprecated.umd.js +0 -1
package/README.md
CHANGED
|
@@ -1,89 +1,173 @@
|
|
|
1
1
|
# is-deprecated
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime deprecation warnings for functions, methods, properties and classes, for **Node and the browser**, with **TypeScript types**, **decorators**, **telemetry hooks** and **warn / throw / silent** modes. Zero dependencies.
|
|
4
4
|
|
|
5
|
-
<img
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
5
|
+
<img width="128" height="128" src="https://github.com/mtorre4580/is-deprecated/blob/main/deprecated.png?raw=true" />
|
|
8
6
|
|
|
9
7
|
```
|
|
10
8
|
npm i @mtorre4580/is-deprecated
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
```ts
|
|
12
|
+
import { deprecate } from '@mtorre4580/is-deprecated';
|
|
13
|
+
|
|
14
|
+
export const getUsers = deprecate(getUsersImpl, {
|
|
15
|
+
since: '1.4.0',
|
|
16
|
+
removeIn: '2.0.0',
|
|
17
|
+
replacement: 'getUsersV2()',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
getUsers();
|
|
21
|
+
// (node:4242) DeprecationWarning: getUsers is deprecated since 1.4.0 and will be removed in 2.0.0. Use getUsersV2() instead.
|
|
22
|
+
// Called from file:///app/src/routes.ts:12:3
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Why this one
|
|
26
|
+
|
|
27
|
+
| | is-deprecated | [depd](https://www.npmjs.com/package/depd) | [util-deprecate](https://www.npmjs.com/package/util-deprecate) | Node `util.deprecate` |
|
|
28
|
+
|---|:-:|:-:|:-:|:-:|
|
|
29
|
+
| Works in browsers | ✅ | ❌ (no-op) | ✅ | ❌ |
|
|
30
|
+
| TypeScript types included | ✅ | ❌ | ❌ | ✅ |
|
|
31
|
+
| Functions and classes (`new`) | ✅ | ✅ | ✅ | ✅ |
|
|
32
|
+
| Object methods and properties | ✅ | ✅ | ❌ | ❌ |
|
|
33
|
+
| Decorators (TC39 + legacy) | ✅ | ❌ | ❌ | ❌ |
|
|
34
|
+
| Call site in the warning | ✅ | ✅ | ❌ | with `--trace-deprecation` |
|
|
35
|
+
| Once per call site | ✅ | ✅ | once total | once total |
|
|
36
|
+
| `since` / `removeIn` / `replacement` metadata | ✅ | ❌ | ❌ | `code` only |
|
|
37
|
+
| Overdue removal dates | ✅ | ❌ | ❌ | ❌ |
|
|
38
|
+
| Telemetry hook with structured event | ✅ | `process.on('deprecation')` | ❌ | `process.on('warning')` |
|
|
39
|
+
| Modes warn / throw / silent | ✅ | ✅ (Node flags) | partial | ✅ (Node flags) |
|
|
40
|
+
| ESM + CJS | ✅ | CJS | CJS | n/a |
|
|
41
|
+
|
|
42
|
+
If you only target Node and need to deprecate a single function, `util.deprecate` is perfectly fine. Use this library when you ship code to browsers, want typed metadata, decorators or telemetry.
|
|
14
43
|
|
|
15
|
-
|
|
44
|
+
## API
|
|
16
45
|
|
|
17
|
-
|
|
18
|
-
- method (String): The method name to be deprecated within the object.
|
|
19
|
-
- message (String): Custom deprecation message (default: 'This will be deprecated soon').
|
|
20
|
-
- logger (Object): Logger object for warning messages (default: console).
|
|
21
|
-
- sendMetrics (Function): Function to send metrics upon deprecation (default: empty function).
|
|
46
|
+
### `deprecate(fn, options?)`
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
Wraps a function or class. It warns **when called** (or instantiated with `new`), never when wrapped. It keeps `this`, `new`, `instanceof`, `name`, `length`, `prototype` and static members.
|
|
24
49
|
|
|
25
|
-
|
|
50
|
+
```ts
|
|
51
|
+
const LegacyClient = deprecate(Client, { replacement: 'ApiClient' });
|
|
52
|
+
new LegacyClient(); // Client is deprecated. Use ApiClient instead.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `deprecateMethod(obj, method | methods[], options?)`
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
Returns a proxy of `obj` whose methods warn when **called**. Reading the property, `typeof`, spreading or logging the object does not warn. Deprecated methods run against the original object, so private fields work and their internal `this.x()` calls are not reported again.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
export const api = deprecateMethod(apiImpl, ['getUsers', 'getUser'], {
|
|
61
|
+
removeIn: '2027-06-30',
|
|
62
|
+
url: 'https://example.com/migrate-users',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
28
65
|
|
|
29
|
-
###
|
|
66
|
+
### `deprecateProperty(obj, property, options?)`
|
|
67
|
+
|
|
68
|
+
Redefines the property **in place** so reading or writing it warns. It works with data properties and with getters/setters.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
deprecateProperty(config, 'retries', { replacement: 'config.maxRetries' });
|
|
72
|
+
```
|
|
30
73
|
|
|
31
|
-
|
|
32
|
-
import deprecated from "@mtorre4580/is-deprecated";
|
|
74
|
+
### `@Deprecated(options?)`
|
|
33
75
|
|
|
34
|
-
|
|
35
|
-
const myFunction = (arg1, arg2) => {};
|
|
76
|
+
Decorator for classes, methods, getters, setters and `accessor` fields. It supports standard decorators (TypeScript 5+, Babel) and legacy `experimentalDecorators` (Angular, NestJS).
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
message:
|
|
40
|
-
"myFunction will be removed in the next release, please replace with this https://www.npmjs.com",
|
|
41
|
-
};
|
|
78
|
+
```ts
|
|
79
|
+
import { Deprecated } from '@mtorre4580/is-deprecated';
|
|
42
80
|
|
|
43
|
-
|
|
44
|
-
|
|
81
|
+
class Api {
|
|
82
|
+
/** @deprecated Use {@link Api.getUsersV2} */
|
|
83
|
+
@Deprecated({ since: '1.2.0', replacement: 'Api.getUsersV2()' })
|
|
84
|
+
getUsers() {}
|
|
45
85
|
|
|
46
|
-
|
|
47
|
-
|
|
86
|
+
@Deprecated()
|
|
87
|
+
accessor timeout = 5000;
|
|
88
|
+
}
|
|
89
|
+
// Api.getUsers() is deprecated since 1.2.0. Use Api.getUsersV2() instead.
|
|
48
90
|
```
|
|
49
91
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
92
|
+
Plain class fields cannot be intercepted: use `accessor` or a getter/setter.
|
|
93
|
+
|
|
94
|
+
### Options
|
|
95
|
+
|
|
96
|
+
| Option | Type | Description |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `name` | `string` | Name shown in the message. Defaults to the function, method or property name. |
|
|
99
|
+
| `message` | `string` | Full custom message (replaces the generated one). |
|
|
100
|
+
| `since` | `string` | Version in which the API was deprecated. |
|
|
101
|
+
| `removeIn` | `string \| Date` | Version (`"2.0.0"`) or date (`"2027-01-01"`) of removal. Past dates are marked `[OVERDUE]` and logged with `logger.error` when available. |
|
|
102
|
+
| `replacement` | `string` | What to use instead. |
|
|
103
|
+
| `code` | `string` | Stable id such as `MYLIB_DEP001`. Shown by Node and used for dedupe. |
|
|
104
|
+
| `url` | `string` | Migration guide. |
|
|
105
|
+
| `once` | `boolean` | Warn once per call site (default `true`). |
|
|
106
|
+
| `logger` | `{ warn, error? }` | Logger for this API. |
|
|
107
|
+
| `onDeprecation` | `(event) => void` | Telemetry hook for this API. |
|
|
108
|
+
|
|
109
|
+
### `configure(config)`
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { configure } from '@mtorre4580/is-deprecated';
|
|
113
|
+
|
|
114
|
+
configure({
|
|
115
|
+
mode: process.env.NODE_ENV === 'production' ? 'silent' : 'warn', // or 'throw' in CI
|
|
116
|
+
once: true,
|
|
117
|
+
logger: pinoLogger, // default: process.emitWarning in Node, console.warn in browsers
|
|
118
|
+
onDeprecation(event) {
|
|
119
|
+
metrics.increment('deprecated_api', { name: event.name, code: event.code });
|
|
67
120
|
},
|
|
68
|
-
|
|
69
|
-
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- **`warn`** (default) prints the warning.
|
|
125
|
+
- **`throw`** throws a `DeprecationError` on every call (`error.event` holds the details). It is handy in CI to make sure nothing still uses old APIs.
|
|
126
|
+
- **`silent`** prints nothing but **still calls `onDeprecation`**, so you can track usage in production without noise.
|
|
70
127
|
|
|
71
|
-
|
|
72
|
-
const config = {
|
|
73
|
-
message: "getUsers is deprecated in the next release, please with getUsersV2",
|
|
74
|
-
method: "getUsers",
|
|
75
|
-
};
|
|
128
|
+
When `mode` is not configured it is read from the `IS_DEPRECATED_MODE` env var (`warn|throw|silent`), and then from Node's `--no-deprecation` / `--throw-deprecation` flags.
|
|
76
129
|
|
|
77
|
-
|
|
78
|
-
const APIWithDeprecated = deprecated(API, config);
|
|
130
|
+
The `onDeprecation` event has this shape:
|
|
79
131
|
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
```ts
|
|
133
|
+
{ name, message, code?, since?, removeIn?, replacement?, url?, overdue, callSite?, stack?, mode }
|
|
82
134
|
```
|
|
83
135
|
|
|
84
|
-
|
|
136
|
+
Hooks that throw are ignored: telemetry never breaks your app.
|
|
137
|
+
|
|
138
|
+
### `resetDeprecations()`
|
|
139
|
+
|
|
140
|
+
Clears the "already warned" cache. It is useful in tests.
|
|
141
|
+
|
|
142
|
+
## Best practices
|
|
143
|
+
|
|
144
|
+
- Combine the static and runtime signals: the JSDoc `/** @deprecated */` tag strikes the API through in editors and is caught by [`@typescript-eslint/no-deprecated`](https://typescript-eslint.io/rules/no-deprecated/), while this library warns people who use it at runtime or from plain JavaScript.
|
|
145
|
+
- Always set `replacement` and `removeIn`, so a warning tells people what to do and by when.
|
|
146
|
+
- Use `code` so people can search for it and filter it with Node's `--no-deprecation`/`process.on('warning')`.
|
|
147
|
+
- Send `onDeprecation` to your metrics, so you know when it is safe to remove the API.
|
|
148
|
+
|
|
149
|
+
## Migrating from 0.x
|
|
150
|
+
|
|
151
|
+
The 0.x default export still works but is deprecated:
|
|
152
|
+
|
|
153
|
+
```diff
|
|
154
|
+
- import deprecated from '@mtorre4580/is-deprecated';
|
|
155
|
+
- const fn = deprecated(myFn, { message: '...' });
|
|
156
|
+
- const api = deprecated(API, { method: 'getUsers', sendMetrics });
|
|
157
|
+
+ import { deprecate, deprecateMethod } from '@mtorre4580/is-deprecated';
|
|
158
|
+
+ const fn = deprecate(myFn, { message: '...' });
|
|
159
|
+
+ const api = deprecateMethod(API, 'getUsers', { onDeprecation });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Behavior changes in 1.0:
|
|
163
|
+
|
|
164
|
+
- **Bug fix:** in 0.x a wrapped function warned once when it was *wrapped* and never when it was *called*. It now warns on calls.
|
|
165
|
+
- Object methods warn when called, not on every property read.
|
|
166
|
+
- Warnings are deduplicated per call site (`once: false` restores warning on every call).
|
|
167
|
+
- The default message is generated from the metadata instead of `"This will be deprecated soon"`.
|
|
168
|
+
- `sendMetrics` now receives the event object.
|
|
169
|
+
- In CommonJS, `require()` returns the module namespace: use `const { deprecate } = require('@mtorre4580/is-deprecated')`.
|
|
170
|
+
|
|
171
|
+
## License
|
|
85
172
|
|
|
86
|
-
|
|
87
|
-
- To runtime use the function from this lib `deprecated` to notify in the console
|
|
88
|
-
- The sendMetrics function allows to get more insight what app is using the deprecated method to notify the team
|
|
89
|
-
- You can use any environment variable to apply the wrapper or not, if you only need to run in development or staging
|
|
173
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
// src/env.ts
|
|
6
|
+
function getProcess() {
|
|
7
|
+
const proc = globalThis.process;
|
|
8
|
+
return proc && typeof proc === "object" ? proc : void 0;
|
|
9
|
+
}
|
|
10
|
+
var MODES = ["warn", "throw", "silent"];
|
|
11
|
+
function modeFromEnvironment() {
|
|
12
|
+
const proc = getProcess();
|
|
13
|
+
let fromEnv;
|
|
14
|
+
try {
|
|
15
|
+
fromEnv = proc?.env?.IS_DEPRECATED_MODE;
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
const normalized = fromEnv?.trim().toLowerCase();
|
|
19
|
+
if (normalized && MODES.includes(normalized)) return normalized;
|
|
20
|
+
if (proc?.noDeprecation) return "silent";
|
|
21
|
+
if (proc?.throwDeprecation) return "throw";
|
|
22
|
+
return "warn";
|
|
23
|
+
}
|
|
24
|
+
function nodeEmitWarning() {
|
|
25
|
+
const emit = getProcess()?.emitWarning;
|
|
26
|
+
return typeof emit === "function" ? emit.bind(getProcess()) : void 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/core.ts
|
|
30
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("@mtorre4580/is-deprecated");
|
|
31
|
+
var store = globalThis;
|
|
32
|
+
var state = store[STATE_KEY] ?? (store[STATE_KEY] = { config: {}, seen: /* @__PURE__ */ new Set() });
|
|
33
|
+
function configure(config) {
|
|
34
|
+
Object.assign(state.config, config);
|
|
35
|
+
return { ...state.config };
|
|
36
|
+
}
|
|
37
|
+
function resetDeprecations() {
|
|
38
|
+
state.seen.clear();
|
|
39
|
+
}
|
|
40
|
+
var DeprecationError = class extends Error {
|
|
41
|
+
constructor(event) {
|
|
42
|
+
super(event.message);
|
|
43
|
+
this.name = "DeprecationError";
|
|
44
|
+
this.code = event.code;
|
|
45
|
+
this.event = event;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var ISO_DATE = /^\d{4}-\d{2}-\d{2}/;
|
|
49
|
+
function parseRemoval(removeIn) {
|
|
50
|
+
if (removeIn instanceof Date) {
|
|
51
|
+
return Number.isNaN(removeIn.getTime()) ? {} : { label: removeIn.toISOString().slice(0, 10), date: removeIn };
|
|
52
|
+
}
|
|
53
|
+
if (typeof removeIn !== "string" || removeIn === "") return {};
|
|
54
|
+
if (ISO_DATE.test(removeIn)) {
|
|
55
|
+
const date = new Date(removeIn);
|
|
56
|
+
if (!Number.isNaN(date.getTime())) return { label: removeIn, date };
|
|
57
|
+
}
|
|
58
|
+
return { label: removeIn };
|
|
59
|
+
}
|
|
60
|
+
function buildMessage(options, name, removal, overdue) {
|
|
61
|
+
let message = options.message;
|
|
62
|
+
if (message === void 0) {
|
|
63
|
+
message = `${name} is deprecated`;
|
|
64
|
+
if (options.since) message += ` since ${options.since}`;
|
|
65
|
+
if (removal.label) {
|
|
66
|
+
if (overdue) message += ` and was scheduled for removal on ${removal.label}`;
|
|
67
|
+
else message += ` and will be removed ${removal.date ? "on" : "in"} ${removal.label}`;
|
|
68
|
+
}
|
|
69
|
+
message += ".";
|
|
70
|
+
if (options.replacement) message += ` Use ${options.replacement} instead.`;
|
|
71
|
+
if (options.url) message += ` See ${options.url}`;
|
|
72
|
+
}
|
|
73
|
+
return overdue ? `[OVERDUE] ${message}` : message;
|
|
74
|
+
}
|
|
75
|
+
function captureCallSite(boundary) {
|
|
76
|
+
const E = Error;
|
|
77
|
+
const holder = {};
|
|
78
|
+
const previousLimit = E.stackTraceLimit;
|
|
79
|
+
let skip = 0;
|
|
80
|
+
try {
|
|
81
|
+
if (typeof previousLimit === "number") E.stackTraceLimit = 10;
|
|
82
|
+
if (typeof E.captureStackTrace === "function") {
|
|
83
|
+
E.captureStackTrace(holder, boundary);
|
|
84
|
+
} else {
|
|
85
|
+
holder.stack = new Error().stack;
|
|
86
|
+
skip = 3;
|
|
87
|
+
}
|
|
88
|
+
void holder.stack;
|
|
89
|
+
} catch {
|
|
90
|
+
return {};
|
|
91
|
+
} finally {
|
|
92
|
+
if (typeof previousLimit === "number") E.stackTraceLimit = previousLimit;
|
|
93
|
+
}
|
|
94
|
+
if (typeof holder.stack !== "string") return {};
|
|
95
|
+
const frames = holder.stack.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !/^Error\b/.test(line)).slice(skip);
|
|
96
|
+
if (frames.length === 0) return {};
|
|
97
|
+
return {
|
|
98
|
+
callSite: frames[0].replace(/^at\s+/, ""),
|
|
99
|
+
stack: frames.join("\n")
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function safeCall(hook, event) {
|
|
103
|
+
if (typeof hook !== "function") return;
|
|
104
|
+
try {
|
|
105
|
+
hook(event);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function notify(options, fallbackName, boundary) {
|
|
110
|
+
const { config } = state;
|
|
111
|
+
const mode = config.mode ?? modeFromEnvironment();
|
|
112
|
+
const name = options.name ?? fallbackName;
|
|
113
|
+
const { callSite, stack } = captureCallSite(boundary);
|
|
114
|
+
if (mode !== "throw" && (options.once ?? config.once ?? true)) {
|
|
115
|
+
const key = `${options.code ?? ""}|${name}|${callSite ?? ""}`;
|
|
116
|
+
if (state.seen.has(key)) return;
|
|
117
|
+
state.seen.add(key);
|
|
118
|
+
}
|
|
119
|
+
const removal = parseRemoval(options.removeIn);
|
|
120
|
+
const overdue = removal.date !== void 0 && removal.date.getTime() <= Date.now();
|
|
121
|
+
const message = buildMessage(options, name, removal, overdue);
|
|
122
|
+
const event = {
|
|
123
|
+
name,
|
|
124
|
+
message,
|
|
125
|
+
code: options.code,
|
|
126
|
+
since: options.since,
|
|
127
|
+
removeIn: removal.label,
|
|
128
|
+
replacement: options.replacement,
|
|
129
|
+
url: options.url,
|
|
130
|
+
overdue,
|
|
131
|
+
callSite,
|
|
132
|
+
stack,
|
|
133
|
+
mode
|
|
134
|
+
};
|
|
135
|
+
safeCall(config.onDeprecation, event);
|
|
136
|
+
safeCall(options.onDeprecation, event);
|
|
137
|
+
safeCall(options.sendMetrics, event);
|
|
138
|
+
if (mode === "silent") return;
|
|
139
|
+
if (mode === "throw") throw new DeprecationError(event);
|
|
140
|
+
const logger = options.logger ?? config.logger;
|
|
141
|
+
if (logger) {
|
|
142
|
+
if (overdue && typeof logger.error === "function") logger.error(message);
|
|
143
|
+
else logger.warn(message);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const emitWarning = nodeEmitWarning();
|
|
147
|
+
if (emitWarning) {
|
|
148
|
+
emitWarning(message, {
|
|
149
|
+
type: "DeprecationWarning",
|
|
150
|
+
code: options.code,
|
|
151
|
+
detail: callSite ? `Called from ${callSite}` : void 0
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (typeof console !== "undefined") {
|
|
156
|
+
console.warn(`DeprecationWarning: ${message}${callSite ? `
|
|
157
|
+
at ${callSite}` : ""}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function copyFunctionShape(target, source) {
|
|
161
|
+
for (const key of ["name", "length"]) {
|
|
162
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
163
|
+
if (descriptor) Object.defineProperty(target, key, { ...descriptor, configurable: true });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function deprecate(fn, options = {}) {
|
|
167
|
+
if (typeof fn !== "function") {
|
|
168
|
+
throw new TypeError("deprecate() expects a function or class as first argument");
|
|
169
|
+
}
|
|
170
|
+
const fallbackName = fn.name || "anonymous function";
|
|
171
|
+
const handler = {
|
|
172
|
+
apply(target, thisArg, args) {
|
|
173
|
+
notify(options, fallbackName, handler.apply);
|
|
174
|
+
return Reflect.apply(target, thisArg, args);
|
|
175
|
+
},
|
|
176
|
+
construct(target, args, newTarget) {
|
|
177
|
+
notify(options, fallbackName, handler.construct);
|
|
178
|
+
return Reflect.construct(target, args, newTarget);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return new Proxy(fn, handler);
|
|
182
|
+
}
|
|
183
|
+
function deprecateMethod(obj, methods, options = {}) {
|
|
184
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
185
|
+
throw new TypeError("deprecateMethod() expects an object as first argument");
|
|
186
|
+
}
|
|
187
|
+
const keys = Array.isArray(methods) ? methods : [methods];
|
|
188
|
+
if (keys.length === 0) throw new TypeError("deprecateMethod() expects at least one method name");
|
|
189
|
+
for (const key of keys) {
|
|
190
|
+
if (typeof obj[key] !== "function") {
|
|
191
|
+
throw new TypeError(`"${String(key)}" is not a method of the given object`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const cache = /* @__PURE__ */ new Map();
|
|
195
|
+
const proxy = new Proxy(obj, {
|
|
196
|
+
get(target, property, receiver) {
|
|
197
|
+
const value = Reflect.get(target, property, receiver);
|
|
198
|
+
if (typeof value !== "function" || !keys.includes(property)) return value;
|
|
199
|
+
const original = value;
|
|
200
|
+
const cached = cache.get(property);
|
|
201
|
+
if (cached && cached.original === original) return cached.wrapped;
|
|
202
|
+
const methodOptions = keys.length === 1 ? options : { ...options, name: void 0 };
|
|
203
|
+
const wrapped = function deprecatedMethod(...args) {
|
|
204
|
+
notify(methodOptions, String(property), wrapped);
|
|
205
|
+
return Reflect.apply(original, this === proxy ? target : this, args);
|
|
206
|
+
};
|
|
207
|
+
copyFunctionShape(wrapped, original);
|
|
208
|
+
cache.set(property, { original, wrapped });
|
|
209
|
+
return wrapped;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return proxy;
|
|
213
|
+
}
|
|
214
|
+
function deprecateProperty(obj, property, options = {}) {
|
|
215
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
216
|
+
throw new TypeError("deprecateProperty() expects an object as first argument");
|
|
217
|
+
}
|
|
218
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, property);
|
|
219
|
+
if (!descriptor) throw new TypeError(`"${String(property)}" is not an own property of the given object`);
|
|
220
|
+
if (!descriptor.configurable) throw new TypeError(`"${String(property)}" is not configurable`);
|
|
221
|
+
const name = options.name ?? String(property);
|
|
222
|
+
const next = { configurable: true, enumerable: descriptor.enumerable };
|
|
223
|
+
if ("value" in descriptor || "writable" in descriptor) {
|
|
224
|
+
let current = descriptor.value;
|
|
225
|
+
next.get = function get() {
|
|
226
|
+
notify(options, name, get);
|
|
227
|
+
return current;
|
|
228
|
+
};
|
|
229
|
+
if (descriptor.writable) {
|
|
230
|
+
next.set = function set(value) {
|
|
231
|
+
notify(options, name, set);
|
|
232
|
+
current = value;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
const { get: originalGet, set: originalSet } = descriptor;
|
|
237
|
+
if (originalGet) {
|
|
238
|
+
next.get = function get() {
|
|
239
|
+
notify(options, name, get);
|
|
240
|
+
return originalGet.call(this);
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (originalSet) {
|
|
244
|
+
next.set = function set(value) {
|
|
245
|
+
notify(options, name, set);
|
|
246
|
+
originalSet.call(this, value);
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
Object.defineProperty(obj, property, next);
|
|
251
|
+
return obj;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/decorator.ts
|
|
255
|
+
function isStandardContext(context) {
|
|
256
|
+
return typeof context === "object" && context !== null && "kind" in context;
|
|
257
|
+
}
|
|
258
|
+
function ownerName(instanceOrClass, isStatic) {
|
|
259
|
+
try {
|
|
260
|
+
const owner = isStatic ? instanceOrClass : instanceOrClass?.constructor;
|
|
261
|
+
return typeof owner?.name === "string" && owner.name !== "" ? owner.name : "Object";
|
|
262
|
+
} catch {
|
|
263
|
+
return "Object";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function wrapCallable(original, member, isStatic, suffix, options) {
|
|
267
|
+
const wrapped = function(...args) {
|
|
268
|
+
notify(options, `${ownerName(this, isStatic)}.${member}${suffix}`, wrapped);
|
|
269
|
+
return original.apply(this, args);
|
|
270
|
+
};
|
|
271
|
+
Object.defineProperty(wrapped, "name", { value: original.name, configurable: true });
|
|
272
|
+
return wrapped;
|
|
273
|
+
}
|
|
274
|
+
function fieldError() {
|
|
275
|
+
return new TypeError(
|
|
276
|
+
"@Deprecated() cannot intercept plain class fields: use an `accessor` field or a getter/setter"
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
function decorateStandard(value, context, options) {
|
|
280
|
+
const member = String(context.name);
|
|
281
|
+
const isStatic = Boolean(context.static);
|
|
282
|
+
switch (context.kind) {
|
|
283
|
+
case "class":
|
|
284
|
+
return deprecate(value, { ...options, name: options.name ?? (member || value.name) });
|
|
285
|
+
case "method":
|
|
286
|
+
return wrapCallable(value, member, isStatic, "()", options);
|
|
287
|
+
case "getter":
|
|
288
|
+
case "setter":
|
|
289
|
+
return wrapCallable(value, member, isStatic, "", options);
|
|
290
|
+
case "accessor": {
|
|
291
|
+
const get = function() {
|
|
292
|
+
notify(options, `${ownerName(this, isStatic)}.${member}`, get);
|
|
293
|
+
return value.get.call(this);
|
|
294
|
+
};
|
|
295
|
+
const set = function(next) {
|
|
296
|
+
notify(options, `${ownerName(this, isStatic)}.${member}`, set);
|
|
297
|
+
value.set.call(this, next);
|
|
298
|
+
};
|
|
299
|
+
return { get, set, init: (initial) => initial };
|
|
300
|
+
}
|
|
301
|
+
default:
|
|
302
|
+
throw fieldError();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function decorateLegacy(target, key, descriptor, options) {
|
|
306
|
+
if (key === void 0 && typeof target === "function") {
|
|
307
|
+
return deprecate(target, { ...options, name: options.name ?? target.name });
|
|
308
|
+
}
|
|
309
|
+
if (!descriptor) throw fieldError();
|
|
310
|
+
const member = String(key);
|
|
311
|
+
const isStatic = typeof target === "function";
|
|
312
|
+
const next = { ...descriptor };
|
|
313
|
+
if (typeof descriptor.value === "function") {
|
|
314
|
+
next.value = wrapCallable(descriptor.value, member, isStatic, "()", options);
|
|
315
|
+
}
|
|
316
|
+
if (descriptor.get) next.get = wrapCallable(descriptor.get, member, isStatic, "", options);
|
|
317
|
+
if (descriptor.set) next.set = wrapCallable(descriptor.set, member, isStatic, "", options);
|
|
318
|
+
return next;
|
|
319
|
+
}
|
|
320
|
+
function Deprecated(options = {}) {
|
|
321
|
+
return function deprecatedDecorator(value, context, descriptor) {
|
|
322
|
+
if (isStandardContext(context)) return decorateStandard(value, context, options);
|
|
323
|
+
return decorateLegacy(value, context, descriptor, options);
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/index.ts
|
|
328
|
+
function deprecated(obj, config = {}) {
|
|
329
|
+
if (obj == null) throw new TypeError("The param obj is required");
|
|
330
|
+
const { method, ...options } = config;
|
|
331
|
+
if (typeof obj === "function" && method === void 0) {
|
|
332
|
+
return deprecate(obj, options);
|
|
333
|
+
}
|
|
334
|
+
if (method === void 0 || typeof obj[method] !== "function") {
|
|
335
|
+
throw new TypeError("The object not have the method defined");
|
|
336
|
+
}
|
|
337
|
+
return deprecateMethod(obj, method, options);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
exports.Deprecated = Deprecated;
|
|
341
|
+
exports.DeprecationError = DeprecationError;
|
|
342
|
+
exports.configure = configure;
|
|
343
|
+
exports.default = deprecated;
|
|
344
|
+
exports.deprecate = deprecate;
|
|
345
|
+
exports.deprecateMethod = deprecateMethod;
|
|
346
|
+
exports.deprecateProperty = deprecateProperty;
|
|
347
|
+
exports.resetDeprecations = resetDeprecations;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** How deprecations are surfaced. */
|
|
2
|
+
type DeprecationMode = 'warn' | 'throw' | 'silent';
|
|
3
|
+
/** Anything with a `warn` method (console, pino, winston, a custom logger...). */
|
|
4
|
+
interface Logger {
|
|
5
|
+
warn: (...args: unknown[]) => void;
|
|
6
|
+
error?: (...args: unknown[]) => void;
|
|
7
|
+
}
|
|
8
|
+
/** Metadata describing a single deprecated API. */
|
|
9
|
+
interface DeprecationOptions {
|
|
10
|
+
/** Human readable name of the deprecated API. Defaults to the function/property name. */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Full custom message. When omitted, a message is built from the other fields. */
|
|
13
|
+
message?: string;
|
|
14
|
+
/** Version (or date) in which the API was deprecated, e.g. `"1.4.0"`. */
|
|
15
|
+
since?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Version or date in which the API will be removed, e.g. `"2.0.0"`, `"2027-01-01"` or a `Date`.
|
|
18
|
+
* When it is a date that already passed, the deprecation is flagged as overdue.
|
|
19
|
+
*/
|
|
20
|
+
removeIn?: string | Date;
|
|
21
|
+
/** What to use instead, e.g. `"getUsersV2()"`. */
|
|
22
|
+
replacement?: string;
|
|
23
|
+
/** Stable identifier, e.g. `"MYLIB_DEP001"`. Used for dedupe and shown in Node warnings. */
|
|
24
|
+
code?: string;
|
|
25
|
+
/** Link to the migration guide. */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Warn only once per call site (default: global `once`, which defaults to `true`). */
|
|
28
|
+
once?: boolean;
|
|
29
|
+
/** Per-API logger. When set, it is used instead of `process.emitWarning`/`console.warn`. */
|
|
30
|
+
logger?: Logger;
|
|
31
|
+
/** Per-API telemetry hook, called in addition to the global `onDeprecation`. */
|
|
32
|
+
onDeprecation?: (event: DeprecationEvent) => void;
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated Use `onDeprecation` instead. Kept for compatibility with v0.x.
|
|
35
|
+
*/
|
|
36
|
+
sendMetrics?: (event: DeprecationEvent) => void;
|
|
37
|
+
}
|
|
38
|
+
/** Payload delivered to `onDeprecation` hooks. */
|
|
39
|
+
interface DeprecationEvent {
|
|
40
|
+
name: string;
|
|
41
|
+
message: string;
|
|
42
|
+
code?: string;
|
|
43
|
+
since?: string;
|
|
44
|
+
removeIn?: string;
|
|
45
|
+
replacement?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
/** `true` when `removeIn` is a date that already passed. */
|
|
48
|
+
overdue: boolean;
|
|
49
|
+
/** First stack frame outside this library (best effort, `undefined` when unavailable). */
|
|
50
|
+
callSite?: string;
|
|
51
|
+
/** Stack trace starting at the caller (best effort). */
|
|
52
|
+
stack?: string;
|
|
53
|
+
/** Mode that was applied to this event. */
|
|
54
|
+
mode: DeprecationMode;
|
|
55
|
+
}
|
|
56
|
+
/** Global configuration, set with `configure()`. */
|
|
57
|
+
interface GlobalConfig {
|
|
58
|
+
/**
|
|
59
|
+
* `warn` (default), `throw` or `silent`. When not set, it is read from the
|
|
60
|
+
* `IS_DEPRECATED_MODE` env var, then from Node's `--no-deprecation` / `--throw-deprecation`.
|
|
61
|
+
*/
|
|
62
|
+
mode?: DeprecationMode;
|
|
63
|
+
/** Warn only once per API and call site. Default `true`. */
|
|
64
|
+
once?: boolean;
|
|
65
|
+
/** Global logger. Default: `process.emitWarning` in Node, `console.warn` elsewhere. */
|
|
66
|
+
logger?: Logger;
|
|
67
|
+
/** Global telemetry hook. Called even in `silent` mode. */
|
|
68
|
+
onDeprecation?: (event: DeprecationEvent) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type AnyFunction = (...args: any[]) => any;
|
|
72
|
+
/**
|
|
73
|
+
* Set global behavior. Only the keys you pass are changed; pass `undefined` to reset a key.
|
|
74
|
+
* Returns the resulting configuration.
|
|
75
|
+
*/
|
|
76
|
+
declare function configure(config: GlobalConfig): Readonly<GlobalConfig>;
|
|
77
|
+
/** Forget which deprecations were already reported, so `once` warnings show again. */
|
|
78
|
+
declare function resetDeprecations(): void;
|
|
79
|
+
/** Thrown in `throw` mode. */
|
|
80
|
+
declare class DeprecationError extends Error {
|
|
81
|
+
readonly code?: string;
|
|
82
|
+
readonly event: DeprecationEvent;
|
|
83
|
+
constructor(event: DeprecationEvent);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Wrap a function or class so every call (or `new`) reports a deprecation.
|
|
87
|
+
* The wrapper keeps `this`, `new`, `name`, `length`, `prototype` and static members.
|
|
88
|
+
*/
|
|
89
|
+
declare function deprecate<T extends AnyFunction | (abstract new (...args: any[]) => any)>(fn: T, options?: DeprecationOptions): T;
|
|
90
|
+
/**
|
|
91
|
+
* Return a proxy of `obj` whose given methods report a deprecation when they are **called**
|
|
92
|
+
* (reading the property, `typeof`, spreading or logging the object does not warn).
|
|
93
|
+
* Deprecated methods run against the original object, so calls they make through `this`
|
|
94
|
+
* are not reported again and private class fields keep working.
|
|
95
|
+
*/
|
|
96
|
+
declare function deprecateMethod<T extends object>(obj: T, methods: keyof T | readonly (keyof T)[], options?: DeprecationOptions): T;
|
|
97
|
+
/**
|
|
98
|
+
* Redefine `obj[property]` in place so reading or writing it reports a deprecation.
|
|
99
|
+
* Works with data properties and getters/setters. Returns the same object.
|
|
100
|
+
*/
|
|
101
|
+
declare function deprecateProperty<T extends object>(obj: T, property: keyof T, options?: DeprecationOptions): T;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Decorator that reports a deprecation when a class is instantiated, or when a method,
|
|
105
|
+
* getter, setter or `accessor` field is used.
|
|
106
|
+
*
|
|
107
|
+
* Works with standard (TC39, TypeScript 5+) decorators and with legacy
|
|
108
|
+
* `experimentalDecorators` (Angular, NestJS...).
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* class Api {
|
|
112
|
+
* \@Deprecated({ since: '1.2.0', replacement: 'Api.getUsersV2()' })
|
|
113
|
+
* getUsers() {}
|
|
114
|
+
* }
|
|
115
|
+
*/
|
|
116
|
+
declare function Deprecated(options?: DeprecationOptions): any;
|
|
117
|
+
|
|
118
|
+
/** Options accepted by the v0.x default export. */
|
|
119
|
+
interface LegacyConfig extends DeprecationOptions {
|
|
120
|
+
/** Name of the method to deprecate when the first argument is an object. */
|
|
121
|
+
method?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* v0.x API, kept so existing code keeps working.
|
|
125
|
+
* @deprecated Use `deprecate(fn, options)` or `deprecateMethod(obj, 'method', options)`.
|
|
126
|
+
*/
|
|
127
|
+
declare function deprecated<T extends object>(obj: T, config?: LegacyConfig): T;
|
|
128
|
+
|
|
129
|
+
export { Deprecated, DeprecationError, configure, deprecated as default, deprecate, deprecateMethod, deprecateProperty, resetDeprecations };
|
|
130
|
+
export type { DeprecationEvent, DeprecationMode, DeprecationOptions, GlobalConfig, LegacyConfig, Logger };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** How deprecations are surfaced. */
|
|
2
|
+
type DeprecationMode = 'warn' | 'throw' | 'silent';
|
|
3
|
+
/** Anything with a `warn` method (console, pino, winston, a custom logger...). */
|
|
4
|
+
interface Logger {
|
|
5
|
+
warn: (...args: unknown[]) => void;
|
|
6
|
+
error?: (...args: unknown[]) => void;
|
|
7
|
+
}
|
|
8
|
+
/** Metadata describing a single deprecated API. */
|
|
9
|
+
interface DeprecationOptions {
|
|
10
|
+
/** Human readable name of the deprecated API. Defaults to the function/property name. */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Full custom message. When omitted, a message is built from the other fields. */
|
|
13
|
+
message?: string;
|
|
14
|
+
/** Version (or date) in which the API was deprecated, e.g. `"1.4.0"`. */
|
|
15
|
+
since?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Version or date in which the API will be removed, e.g. `"2.0.0"`, `"2027-01-01"` or a `Date`.
|
|
18
|
+
* When it is a date that already passed, the deprecation is flagged as overdue.
|
|
19
|
+
*/
|
|
20
|
+
removeIn?: string | Date;
|
|
21
|
+
/** What to use instead, e.g. `"getUsersV2()"`. */
|
|
22
|
+
replacement?: string;
|
|
23
|
+
/** Stable identifier, e.g. `"MYLIB_DEP001"`. Used for dedupe and shown in Node warnings. */
|
|
24
|
+
code?: string;
|
|
25
|
+
/** Link to the migration guide. */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Warn only once per call site (default: global `once`, which defaults to `true`). */
|
|
28
|
+
once?: boolean;
|
|
29
|
+
/** Per-API logger. When set, it is used instead of `process.emitWarning`/`console.warn`. */
|
|
30
|
+
logger?: Logger;
|
|
31
|
+
/** Per-API telemetry hook, called in addition to the global `onDeprecation`. */
|
|
32
|
+
onDeprecation?: (event: DeprecationEvent) => void;
|
|
33
|
+
/**
|
|
34
|
+
* @deprecated Use `onDeprecation` instead. Kept for compatibility with v0.x.
|
|
35
|
+
*/
|
|
36
|
+
sendMetrics?: (event: DeprecationEvent) => void;
|
|
37
|
+
}
|
|
38
|
+
/** Payload delivered to `onDeprecation` hooks. */
|
|
39
|
+
interface DeprecationEvent {
|
|
40
|
+
name: string;
|
|
41
|
+
message: string;
|
|
42
|
+
code?: string;
|
|
43
|
+
since?: string;
|
|
44
|
+
removeIn?: string;
|
|
45
|
+
replacement?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
/** `true` when `removeIn` is a date that already passed. */
|
|
48
|
+
overdue: boolean;
|
|
49
|
+
/** First stack frame outside this library (best effort, `undefined` when unavailable). */
|
|
50
|
+
callSite?: string;
|
|
51
|
+
/** Stack trace starting at the caller (best effort). */
|
|
52
|
+
stack?: string;
|
|
53
|
+
/** Mode that was applied to this event. */
|
|
54
|
+
mode: DeprecationMode;
|
|
55
|
+
}
|
|
56
|
+
/** Global configuration, set with `configure()`. */
|
|
57
|
+
interface GlobalConfig {
|
|
58
|
+
/**
|
|
59
|
+
* `warn` (default), `throw` or `silent`. When not set, it is read from the
|
|
60
|
+
* `IS_DEPRECATED_MODE` env var, then from Node's `--no-deprecation` / `--throw-deprecation`.
|
|
61
|
+
*/
|
|
62
|
+
mode?: DeprecationMode;
|
|
63
|
+
/** Warn only once per API and call site. Default `true`. */
|
|
64
|
+
once?: boolean;
|
|
65
|
+
/** Global logger. Default: `process.emitWarning` in Node, `console.warn` elsewhere. */
|
|
66
|
+
logger?: Logger;
|
|
67
|
+
/** Global telemetry hook. Called even in `silent` mode. */
|
|
68
|
+
onDeprecation?: (event: DeprecationEvent) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type AnyFunction = (...args: any[]) => any;
|
|
72
|
+
/**
|
|
73
|
+
* Set global behavior. Only the keys you pass are changed; pass `undefined` to reset a key.
|
|
74
|
+
* Returns the resulting configuration.
|
|
75
|
+
*/
|
|
76
|
+
declare function configure(config: GlobalConfig): Readonly<GlobalConfig>;
|
|
77
|
+
/** Forget which deprecations were already reported, so `once` warnings show again. */
|
|
78
|
+
declare function resetDeprecations(): void;
|
|
79
|
+
/** Thrown in `throw` mode. */
|
|
80
|
+
declare class DeprecationError extends Error {
|
|
81
|
+
readonly code?: string;
|
|
82
|
+
readonly event: DeprecationEvent;
|
|
83
|
+
constructor(event: DeprecationEvent);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Wrap a function or class so every call (or `new`) reports a deprecation.
|
|
87
|
+
* The wrapper keeps `this`, `new`, `name`, `length`, `prototype` and static members.
|
|
88
|
+
*/
|
|
89
|
+
declare function deprecate<T extends AnyFunction | (abstract new (...args: any[]) => any)>(fn: T, options?: DeprecationOptions): T;
|
|
90
|
+
/**
|
|
91
|
+
* Return a proxy of `obj` whose given methods report a deprecation when they are **called**
|
|
92
|
+
* (reading the property, `typeof`, spreading or logging the object does not warn).
|
|
93
|
+
* Deprecated methods run against the original object, so calls they make through `this`
|
|
94
|
+
* are not reported again and private class fields keep working.
|
|
95
|
+
*/
|
|
96
|
+
declare function deprecateMethod<T extends object>(obj: T, methods: keyof T | readonly (keyof T)[], options?: DeprecationOptions): T;
|
|
97
|
+
/**
|
|
98
|
+
* Redefine `obj[property]` in place so reading or writing it reports a deprecation.
|
|
99
|
+
* Works with data properties and getters/setters. Returns the same object.
|
|
100
|
+
*/
|
|
101
|
+
declare function deprecateProperty<T extends object>(obj: T, property: keyof T, options?: DeprecationOptions): T;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Decorator that reports a deprecation when a class is instantiated, or when a method,
|
|
105
|
+
* getter, setter or `accessor` field is used.
|
|
106
|
+
*
|
|
107
|
+
* Works with standard (TC39, TypeScript 5+) decorators and with legacy
|
|
108
|
+
* `experimentalDecorators` (Angular, NestJS...).
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* class Api {
|
|
112
|
+
* \@Deprecated({ since: '1.2.0', replacement: 'Api.getUsersV2()' })
|
|
113
|
+
* getUsers() {}
|
|
114
|
+
* }
|
|
115
|
+
*/
|
|
116
|
+
declare function Deprecated(options?: DeprecationOptions): any;
|
|
117
|
+
|
|
118
|
+
/** Options accepted by the v0.x default export. */
|
|
119
|
+
interface LegacyConfig extends DeprecationOptions {
|
|
120
|
+
/** Name of the method to deprecate when the first argument is an object. */
|
|
121
|
+
method?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* v0.x API, kept so existing code keeps working.
|
|
125
|
+
* @deprecated Use `deprecate(fn, options)` or `deprecateMethod(obj, 'method', options)`.
|
|
126
|
+
*/
|
|
127
|
+
declare function deprecated<T extends object>(obj: T, config?: LegacyConfig): T;
|
|
128
|
+
|
|
129
|
+
export { Deprecated, DeprecationError, configure, deprecated as default, deprecate, deprecateMethod, deprecateProperty, resetDeprecations };
|
|
130
|
+
export type { DeprecationEvent, DeprecationMode, DeprecationOptions, GlobalConfig, LegacyConfig, Logger };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// src/env.ts
|
|
2
|
+
function getProcess() {
|
|
3
|
+
const proc = globalThis.process;
|
|
4
|
+
return proc && typeof proc === "object" ? proc : void 0;
|
|
5
|
+
}
|
|
6
|
+
var MODES = ["warn", "throw", "silent"];
|
|
7
|
+
function modeFromEnvironment() {
|
|
8
|
+
const proc = getProcess();
|
|
9
|
+
let fromEnv;
|
|
10
|
+
try {
|
|
11
|
+
fromEnv = proc?.env?.IS_DEPRECATED_MODE;
|
|
12
|
+
} catch {
|
|
13
|
+
}
|
|
14
|
+
const normalized = fromEnv?.trim().toLowerCase();
|
|
15
|
+
if (normalized && MODES.includes(normalized)) return normalized;
|
|
16
|
+
if (proc?.noDeprecation) return "silent";
|
|
17
|
+
if (proc?.throwDeprecation) return "throw";
|
|
18
|
+
return "warn";
|
|
19
|
+
}
|
|
20
|
+
function nodeEmitWarning() {
|
|
21
|
+
const emit = getProcess()?.emitWarning;
|
|
22
|
+
return typeof emit === "function" ? emit.bind(getProcess()) : void 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/core.ts
|
|
26
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("@mtorre4580/is-deprecated");
|
|
27
|
+
var store = globalThis;
|
|
28
|
+
var state = store[STATE_KEY] ?? (store[STATE_KEY] = { config: {}, seen: /* @__PURE__ */ new Set() });
|
|
29
|
+
function configure(config) {
|
|
30
|
+
Object.assign(state.config, config);
|
|
31
|
+
return { ...state.config };
|
|
32
|
+
}
|
|
33
|
+
function resetDeprecations() {
|
|
34
|
+
state.seen.clear();
|
|
35
|
+
}
|
|
36
|
+
var DeprecationError = class extends Error {
|
|
37
|
+
constructor(event) {
|
|
38
|
+
super(event.message);
|
|
39
|
+
this.name = "DeprecationError";
|
|
40
|
+
this.code = event.code;
|
|
41
|
+
this.event = event;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var ISO_DATE = /^\d{4}-\d{2}-\d{2}/;
|
|
45
|
+
function parseRemoval(removeIn) {
|
|
46
|
+
if (removeIn instanceof Date) {
|
|
47
|
+
return Number.isNaN(removeIn.getTime()) ? {} : { label: removeIn.toISOString().slice(0, 10), date: removeIn };
|
|
48
|
+
}
|
|
49
|
+
if (typeof removeIn !== "string" || removeIn === "") return {};
|
|
50
|
+
if (ISO_DATE.test(removeIn)) {
|
|
51
|
+
const date = new Date(removeIn);
|
|
52
|
+
if (!Number.isNaN(date.getTime())) return { label: removeIn, date };
|
|
53
|
+
}
|
|
54
|
+
return { label: removeIn };
|
|
55
|
+
}
|
|
56
|
+
function buildMessage(options, name, removal, overdue) {
|
|
57
|
+
let message = options.message;
|
|
58
|
+
if (message === void 0) {
|
|
59
|
+
message = `${name} is deprecated`;
|
|
60
|
+
if (options.since) message += ` since ${options.since}`;
|
|
61
|
+
if (removal.label) {
|
|
62
|
+
if (overdue) message += ` and was scheduled for removal on ${removal.label}`;
|
|
63
|
+
else message += ` and will be removed ${removal.date ? "on" : "in"} ${removal.label}`;
|
|
64
|
+
}
|
|
65
|
+
message += ".";
|
|
66
|
+
if (options.replacement) message += ` Use ${options.replacement} instead.`;
|
|
67
|
+
if (options.url) message += ` See ${options.url}`;
|
|
68
|
+
}
|
|
69
|
+
return overdue ? `[OVERDUE] ${message}` : message;
|
|
70
|
+
}
|
|
71
|
+
function captureCallSite(boundary) {
|
|
72
|
+
const E = Error;
|
|
73
|
+
const holder = {};
|
|
74
|
+
const previousLimit = E.stackTraceLimit;
|
|
75
|
+
let skip = 0;
|
|
76
|
+
try {
|
|
77
|
+
if (typeof previousLimit === "number") E.stackTraceLimit = 10;
|
|
78
|
+
if (typeof E.captureStackTrace === "function") {
|
|
79
|
+
E.captureStackTrace(holder, boundary);
|
|
80
|
+
} else {
|
|
81
|
+
holder.stack = new Error().stack;
|
|
82
|
+
skip = 3;
|
|
83
|
+
}
|
|
84
|
+
void holder.stack;
|
|
85
|
+
} catch {
|
|
86
|
+
return {};
|
|
87
|
+
} finally {
|
|
88
|
+
if (typeof previousLimit === "number") E.stackTraceLimit = previousLimit;
|
|
89
|
+
}
|
|
90
|
+
if (typeof holder.stack !== "string") return {};
|
|
91
|
+
const frames = holder.stack.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !/^Error\b/.test(line)).slice(skip);
|
|
92
|
+
if (frames.length === 0) return {};
|
|
93
|
+
return {
|
|
94
|
+
callSite: frames[0].replace(/^at\s+/, ""),
|
|
95
|
+
stack: frames.join("\n")
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function safeCall(hook, event) {
|
|
99
|
+
if (typeof hook !== "function") return;
|
|
100
|
+
try {
|
|
101
|
+
hook(event);
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function notify(options, fallbackName, boundary) {
|
|
106
|
+
const { config } = state;
|
|
107
|
+
const mode = config.mode ?? modeFromEnvironment();
|
|
108
|
+
const name = options.name ?? fallbackName;
|
|
109
|
+
const { callSite, stack } = captureCallSite(boundary);
|
|
110
|
+
if (mode !== "throw" && (options.once ?? config.once ?? true)) {
|
|
111
|
+
const key = `${options.code ?? ""}|${name}|${callSite ?? ""}`;
|
|
112
|
+
if (state.seen.has(key)) return;
|
|
113
|
+
state.seen.add(key);
|
|
114
|
+
}
|
|
115
|
+
const removal = parseRemoval(options.removeIn);
|
|
116
|
+
const overdue = removal.date !== void 0 && removal.date.getTime() <= Date.now();
|
|
117
|
+
const message = buildMessage(options, name, removal, overdue);
|
|
118
|
+
const event = {
|
|
119
|
+
name,
|
|
120
|
+
message,
|
|
121
|
+
code: options.code,
|
|
122
|
+
since: options.since,
|
|
123
|
+
removeIn: removal.label,
|
|
124
|
+
replacement: options.replacement,
|
|
125
|
+
url: options.url,
|
|
126
|
+
overdue,
|
|
127
|
+
callSite,
|
|
128
|
+
stack,
|
|
129
|
+
mode
|
|
130
|
+
};
|
|
131
|
+
safeCall(config.onDeprecation, event);
|
|
132
|
+
safeCall(options.onDeprecation, event);
|
|
133
|
+
safeCall(options.sendMetrics, event);
|
|
134
|
+
if (mode === "silent") return;
|
|
135
|
+
if (mode === "throw") throw new DeprecationError(event);
|
|
136
|
+
const logger = options.logger ?? config.logger;
|
|
137
|
+
if (logger) {
|
|
138
|
+
if (overdue && typeof logger.error === "function") logger.error(message);
|
|
139
|
+
else logger.warn(message);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const emitWarning = nodeEmitWarning();
|
|
143
|
+
if (emitWarning) {
|
|
144
|
+
emitWarning(message, {
|
|
145
|
+
type: "DeprecationWarning",
|
|
146
|
+
code: options.code,
|
|
147
|
+
detail: callSite ? `Called from ${callSite}` : void 0
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (typeof console !== "undefined") {
|
|
152
|
+
console.warn(`DeprecationWarning: ${message}${callSite ? `
|
|
153
|
+
at ${callSite}` : ""}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function copyFunctionShape(target, source) {
|
|
157
|
+
for (const key of ["name", "length"]) {
|
|
158
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
159
|
+
if (descriptor) Object.defineProperty(target, key, { ...descriptor, configurable: true });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function deprecate(fn, options = {}) {
|
|
163
|
+
if (typeof fn !== "function") {
|
|
164
|
+
throw new TypeError("deprecate() expects a function or class as first argument");
|
|
165
|
+
}
|
|
166
|
+
const fallbackName = fn.name || "anonymous function";
|
|
167
|
+
const handler = {
|
|
168
|
+
apply(target, thisArg, args) {
|
|
169
|
+
notify(options, fallbackName, handler.apply);
|
|
170
|
+
return Reflect.apply(target, thisArg, args);
|
|
171
|
+
},
|
|
172
|
+
construct(target, args, newTarget) {
|
|
173
|
+
notify(options, fallbackName, handler.construct);
|
|
174
|
+
return Reflect.construct(target, args, newTarget);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
return new Proxy(fn, handler);
|
|
178
|
+
}
|
|
179
|
+
function deprecateMethod(obj, methods, options = {}) {
|
|
180
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
181
|
+
throw new TypeError("deprecateMethod() expects an object as first argument");
|
|
182
|
+
}
|
|
183
|
+
const keys = Array.isArray(methods) ? methods : [methods];
|
|
184
|
+
if (keys.length === 0) throw new TypeError("deprecateMethod() expects at least one method name");
|
|
185
|
+
for (const key of keys) {
|
|
186
|
+
if (typeof obj[key] !== "function") {
|
|
187
|
+
throw new TypeError(`"${String(key)}" is not a method of the given object`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const cache = /* @__PURE__ */ new Map();
|
|
191
|
+
const proxy = new Proxy(obj, {
|
|
192
|
+
get(target, property, receiver) {
|
|
193
|
+
const value = Reflect.get(target, property, receiver);
|
|
194
|
+
if (typeof value !== "function" || !keys.includes(property)) return value;
|
|
195
|
+
const original = value;
|
|
196
|
+
const cached = cache.get(property);
|
|
197
|
+
if (cached && cached.original === original) return cached.wrapped;
|
|
198
|
+
const methodOptions = keys.length === 1 ? options : { ...options, name: void 0 };
|
|
199
|
+
const wrapped = function deprecatedMethod(...args) {
|
|
200
|
+
notify(methodOptions, String(property), wrapped);
|
|
201
|
+
return Reflect.apply(original, this === proxy ? target : this, args);
|
|
202
|
+
};
|
|
203
|
+
copyFunctionShape(wrapped, original);
|
|
204
|
+
cache.set(property, { original, wrapped });
|
|
205
|
+
return wrapped;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
return proxy;
|
|
209
|
+
}
|
|
210
|
+
function deprecateProperty(obj, property, options = {}) {
|
|
211
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
212
|
+
throw new TypeError("deprecateProperty() expects an object as first argument");
|
|
213
|
+
}
|
|
214
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, property);
|
|
215
|
+
if (!descriptor) throw new TypeError(`"${String(property)}" is not an own property of the given object`);
|
|
216
|
+
if (!descriptor.configurable) throw new TypeError(`"${String(property)}" is not configurable`);
|
|
217
|
+
const name = options.name ?? String(property);
|
|
218
|
+
const next = { configurable: true, enumerable: descriptor.enumerable };
|
|
219
|
+
if ("value" in descriptor || "writable" in descriptor) {
|
|
220
|
+
let current = descriptor.value;
|
|
221
|
+
next.get = function get() {
|
|
222
|
+
notify(options, name, get);
|
|
223
|
+
return current;
|
|
224
|
+
};
|
|
225
|
+
if (descriptor.writable) {
|
|
226
|
+
next.set = function set(value) {
|
|
227
|
+
notify(options, name, set);
|
|
228
|
+
current = value;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
const { get: originalGet, set: originalSet } = descriptor;
|
|
233
|
+
if (originalGet) {
|
|
234
|
+
next.get = function get() {
|
|
235
|
+
notify(options, name, get);
|
|
236
|
+
return originalGet.call(this);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (originalSet) {
|
|
240
|
+
next.set = function set(value) {
|
|
241
|
+
notify(options, name, set);
|
|
242
|
+
originalSet.call(this, value);
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
Object.defineProperty(obj, property, next);
|
|
247
|
+
return obj;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/decorator.ts
|
|
251
|
+
function isStandardContext(context) {
|
|
252
|
+
return typeof context === "object" && context !== null && "kind" in context;
|
|
253
|
+
}
|
|
254
|
+
function ownerName(instanceOrClass, isStatic) {
|
|
255
|
+
try {
|
|
256
|
+
const owner = isStatic ? instanceOrClass : instanceOrClass?.constructor;
|
|
257
|
+
return typeof owner?.name === "string" && owner.name !== "" ? owner.name : "Object";
|
|
258
|
+
} catch {
|
|
259
|
+
return "Object";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function wrapCallable(original, member, isStatic, suffix, options) {
|
|
263
|
+
const wrapped = function(...args) {
|
|
264
|
+
notify(options, `${ownerName(this, isStatic)}.${member}${suffix}`, wrapped);
|
|
265
|
+
return original.apply(this, args);
|
|
266
|
+
};
|
|
267
|
+
Object.defineProperty(wrapped, "name", { value: original.name, configurable: true });
|
|
268
|
+
return wrapped;
|
|
269
|
+
}
|
|
270
|
+
function fieldError() {
|
|
271
|
+
return new TypeError(
|
|
272
|
+
"@Deprecated() cannot intercept plain class fields: use an `accessor` field or a getter/setter"
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
function decorateStandard(value, context, options) {
|
|
276
|
+
const member = String(context.name);
|
|
277
|
+
const isStatic = Boolean(context.static);
|
|
278
|
+
switch (context.kind) {
|
|
279
|
+
case "class":
|
|
280
|
+
return deprecate(value, { ...options, name: options.name ?? (member || value.name) });
|
|
281
|
+
case "method":
|
|
282
|
+
return wrapCallable(value, member, isStatic, "()", options);
|
|
283
|
+
case "getter":
|
|
284
|
+
case "setter":
|
|
285
|
+
return wrapCallable(value, member, isStatic, "", options);
|
|
286
|
+
case "accessor": {
|
|
287
|
+
const get = function() {
|
|
288
|
+
notify(options, `${ownerName(this, isStatic)}.${member}`, get);
|
|
289
|
+
return value.get.call(this);
|
|
290
|
+
};
|
|
291
|
+
const set = function(next) {
|
|
292
|
+
notify(options, `${ownerName(this, isStatic)}.${member}`, set);
|
|
293
|
+
value.set.call(this, next);
|
|
294
|
+
};
|
|
295
|
+
return { get, set, init: (initial) => initial };
|
|
296
|
+
}
|
|
297
|
+
default:
|
|
298
|
+
throw fieldError();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function decorateLegacy(target, key, descriptor, options) {
|
|
302
|
+
if (key === void 0 && typeof target === "function") {
|
|
303
|
+
return deprecate(target, { ...options, name: options.name ?? target.name });
|
|
304
|
+
}
|
|
305
|
+
if (!descriptor) throw fieldError();
|
|
306
|
+
const member = String(key);
|
|
307
|
+
const isStatic = typeof target === "function";
|
|
308
|
+
const next = { ...descriptor };
|
|
309
|
+
if (typeof descriptor.value === "function") {
|
|
310
|
+
next.value = wrapCallable(descriptor.value, member, isStatic, "()", options);
|
|
311
|
+
}
|
|
312
|
+
if (descriptor.get) next.get = wrapCallable(descriptor.get, member, isStatic, "", options);
|
|
313
|
+
if (descriptor.set) next.set = wrapCallable(descriptor.set, member, isStatic, "", options);
|
|
314
|
+
return next;
|
|
315
|
+
}
|
|
316
|
+
function Deprecated(options = {}) {
|
|
317
|
+
return function deprecatedDecorator(value, context, descriptor) {
|
|
318
|
+
if (isStandardContext(context)) return decorateStandard(value, context, options);
|
|
319
|
+
return decorateLegacy(value, context, descriptor, options);
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/index.ts
|
|
324
|
+
function deprecated(obj, config = {}) {
|
|
325
|
+
if (obj == null) throw new TypeError("The param obj is required");
|
|
326
|
+
const { method, ...options } = config;
|
|
327
|
+
if (typeof obj === "function" && method === void 0) {
|
|
328
|
+
return deprecate(obj, options);
|
|
329
|
+
}
|
|
330
|
+
if (method === void 0 || typeof obj[method] !== "function") {
|
|
331
|
+
throw new TypeError("The object not have the method defined");
|
|
332
|
+
}
|
|
333
|
+
return deprecateMethod(obj, method, options);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export { Deprecated, DeprecationError, configure, deprecated as default, deprecate, deprecateMethod, deprecateProperty, resetDeprecations };
|
package/package.json
CHANGED
|
@@ -1,62 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtorre4580/is-deprecated",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Runtime deprecation warnings for functions, methods, properties and classes: Node + browser, TypeScript, decorators, telemetry hooks and warn/throw/silent modes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/mtorre4580/is-deprecated"
|
|
8
|
+
"url": "git+https://github.com/mtorre4580/is-deprecated.git"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://github.com/mtorre4580/is-deprecated
|
|
10
|
+
"homepage": "https://github.com/mtorre4580/is-deprecated#readme",
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/mtorre4580/is-deprecated/issues"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"deprecated",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
16
|
+
"deprecate",
|
|
17
|
+
"deprecation",
|
|
18
|
+
"depd",
|
|
19
|
+
"warning",
|
|
20
|
+
"decorator",
|
|
21
|
+
"typescript",
|
|
22
|
+
"browser",
|
|
23
|
+
"nodejs"
|
|
21
24
|
],
|
|
22
25
|
"author": "mtorre4580",
|
|
26
|
+
"type": "module",
|
|
23
27
|
"files": [
|
|
24
28
|
"dist"
|
|
25
29
|
],
|
|
26
|
-
"
|
|
27
|
-
"
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
28
34
|
"exports": {
|
|
29
35
|
".": {
|
|
30
|
-
"import":
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"default": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/index.d.cts",
|
|
42
|
+
"default": "./dist/index.cjs"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
33
46
|
},
|
|
34
47
|
"scripts": {
|
|
35
|
-
"prepare": "husky
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"lint": "eslint
|
|
40
|
-
"
|
|
41
|
-
"test": "
|
|
42
|
-
"test:coverage": "
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
48
|
+
"prepare": "husky",
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json",
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"lint:fix": "eslint . --fix",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"test:coverage": "vitest run --coverage",
|
|
56
|
+
"check:package": "publint && attw --pack . --profile node16",
|
|
57
|
+
"size": "size-limit",
|
|
58
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build && npm run check:package",
|
|
59
|
+
"update-changelog": "git fetch && auto-changelog"
|
|
46
60
|
},
|
|
47
61
|
"devDependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"@commitlint/
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
62
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
63
|
+
"@commitlint/cli": "^21.2.4",
|
|
64
|
+
"@commitlint/config-conventional": "^21.2.4",
|
|
65
|
+
"@eslint/js": "^10.0.1",
|
|
66
|
+
"@size-limit/preset-small-lib": "^14.3.0",
|
|
67
|
+
"@types/node": "^24.0.0",
|
|
68
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
69
|
+
"auto-changelog": "^2.5.0",
|
|
70
|
+
"eslint": "^10.15.0",
|
|
71
|
+
"happy-dom": "^21.0.4",
|
|
72
|
+
"husky": "^9.1.7",
|
|
73
|
+
"publint": "^0.3.25",
|
|
74
|
+
"size-limit": "^14.3.0",
|
|
75
|
+
"tsup": "^9.0.0",
|
|
76
|
+
"typescript": "~6.0.3",
|
|
77
|
+
"typescript-eslint": "^8.72.2",
|
|
78
|
+
"vitest": "^5.1.0"
|
|
59
79
|
},
|
|
80
|
+
"size-limit": [
|
|
81
|
+
{
|
|
82
|
+
"name": "full bundle (ESM, gzip)",
|
|
83
|
+
"path": "dist/index.js",
|
|
84
|
+
"limit": "2.5 kB"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "deprecate() only",
|
|
88
|
+
"path": "dist/index.js",
|
|
89
|
+
"import": "{ deprecate }",
|
|
90
|
+
"limit": "1.5 kB"
|
|
91
|
+
}
|
|
92
|
+
],
|
|
60
93
|
"auto-changelog": {
|
|
61
94
|
"output": "CHANGELOG.md",
|
|
62
95
|
"template": "update-changelog.hbs",
|
|
@@ -67,6 +100,10 @@
|
|
|
67
100
|
"hideEmptyReleases": true
|
|
68
101
|
},
|
|
69
102
|
"engines": {
|
|
70
|
-
"node": ">=
|
|
103
|
+
"node": ">=18"
|
|
104
|
+
},
|
|
105
|
+
"publishConfig": {
|
|
106
|
+
"access": "public",
|
|
107
|
+
"provenance": true
|
|
71
108
|
}
|
|
72
109
|
}
|
package/dist/is-deprecated.es.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
var f = (e, r) => () => (r || e((r = { exports: {} }).exports, r), r.exports);
|
|
2
|
-
var h = f((w, c) => {
|
|
3
|
-
function u(e) {
|
|
4
|
-
return typeof e == "function";
|
|
5
|
-
}
|
|
6
|
-
function a(e, r) {
|
|
7
|
-
const {
|
|
8
|
-
method: n = null,
|
|
9
|
-
message: t = "This will be deprecated soon",
|
|
10
|
-
logger: o = console,
|
|
11
|
-
sendMetrics: i = () => {
|
|
12
|
-
}
|
|
13
|
-
} = r || {};
|
|
14
|
-
if (e == null)
|
|
15
|
-
throw new Error("The param obj is required");
|
|
16
|
-
if (u(e))
|
|
17
|
-
return o.warn(t), i(), (...s) => e(...s);
|
|
18
|
-
if (!u(e[n]))
|
|
19
|
-
throw new Error("The object not have the method defined");
|
|
20
|
-
return new Proxy(e, {
|
|
21
|
-
get(s, d) {
|
|
22
|
-
return d === n && (o.warn(t), i()), Reflect.get(...arguments);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
c.exports = a;
|
|
27
|
-
});
|
|
28
|
-
export default h();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(n){typeof define=="function"&&define.amd?define(n):n()})(function(){"use strict";function n(e){return typeof e=="function"}function f(e,d){const{method:t=null,message:r="This will be deprecated soon",logger:i=console,sendMetrics:o=()=>{}}=d||{};if(e==null)throw new Error("The param obj is required");if(n(e))return i.warn(r),o(),(...u)=>e(...u);if(!n(e[t]))throw new Error("The object not have the method defined");return new Proxy(e,{get(u,c){return c===t&&(i.warn(r),o()),Reflect.get(...arguments)}})}module.exports=f});
|