@poppinss/utils 7.0.0-next.3 → 7.0.0-next.5
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 +117 -0
- package/build/chunk-BNZ4_A-W.js +23 -0
- package/build/exception-L7vjh-Gv.js +2 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +142 -167
- package/build/main-CdreHl1_.js +694 -0
- package/build/main-aO4h7ygK.js +514 -0
- package/build/modules/assert.js +8 -21
- package/build/modules/base64.js +24 -41
- package/build/modules/exception.js +2 -12
- package/build/modules/fs/main.js +52 -75
- package/build/modules/json/main.js +3 -8
- package/build/modules/string/main.js +1 -4
- package/build/modules/string/string_builder.js +1 -4
- package/build/modules/types.js +1 -1
- package/build/src/compose.d.ts +4 -4
- package/build/src/imports_bag.d.ts +24 -0
- package/package.json +28 -25
- package/build/chunk-SKNTF5Q5.js +0 -14
- package/build/chunk-XFX47BKO.js +0 -23
- package/build/chunk-YFSCSJNE.js +0 -33
package/README.md
CHANGED
|
@@ -627,6 +627,123 @@ const rawValue = secret.release()
|
|
|
627
627
|
rawValue === opaque_raw_token // true
|
|
628
628
|
```
|
|
629
629
|
|
|
630
|
+
## ImportsBag
|
|
631
|
+
|
|
632
|
+
The `ImportsBag` class helps you manage and deduplicate import statements when generating code. It automatically merges imports from the same source and generates properly formatted import statements.
|
|
633
|
+
|
|
634
|
+
This is particularly useful when building code generators, AST transformers, or any tool that needs to collect and output import statements.
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
import { ImportsBag } from '@poppinss/utils'
|
|
638
|
+
|
|
639
|
+
const bag = new ImportsBag()
|
|
640
|
+
|
|
641
|
+
// Add named imports
|
|
642
|
+
bag.add({
|
|
643
|
+
source: 'lodash',
|
|
644
|
+
namedImports: ['debounce'],
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
// Add more imports from the same source - they will be merged
|
|
648
|
+
bag.add({
|
|
649
|
+
source: 'lodash',
|
|
650
|
+
namedImports: ['throttle', 'debounce'], // duplicate "debounce" will be removed
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
// Add default import with named imports
|
|
654
|
+
bag.add({
|
|
655
|
+
source: 'react',
|
|
656
|
+
defaultImport: 'React',
|
|
657
|
+
namedImports: ['useState', 'useEffect'],
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
// Add type imports
|
|
661
|
+
bag.add({
|
|
662
|
+
source: 'express',
|
|
663
|
+
typeImports: ['Request', 'Response'],
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
// Generate import statements
|
|
667
|
+
console.log(bag.toString())
|
|
668
|
+
// import { debounce, throttle } from 'lodash'
|
|
669
|
+
// import React, { useState, useEffect } from 'react'
|
|
670
|
+
// import type { Request, Response } from 'express'
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Import Types
|
|
674
|
+
|
|
675
|
+
The `ImportsBag` supports three types of imports:
|
|
676
|
+
|
|
677
|
+
- **Default imports**: `defaultImport: 'React'` generates `import React from 'react'`
|
|
678
|
+
- **Named imports**: `namedImports: ['useState']` generates `import { useState } from 'react'`
|
|
679
|
+
- **Type imports**: `typeImports: ['FC']` generates `import type { FC } from 'react'`
|
|
680
|
+
|
|
681
|
+
You can combine default and named imports in a single statement, but type imports are always generated as separate statements.
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
bag.add({
|
|
685
|
+
source: 'react',
|
|
686
|
+
defaultImport: 'React',
|
|
687
|
+
namedImports: ['useState'],
|
|
688
|
+
typeImports: ['FC'],
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
console.log(bag.toString())
|
|
692
|
+
// import React, { useState } from 'react'
|
|
693
|
+
// import type { FC } from 'react'
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Deduplication
|
|
697
|
+
|
|
698
|
+
The `ImportsBag` automatically deduplicates imports from the same source:
|
|
699
|
+
|
|
700
|
+
- Named and type imports are deduplicated when calling `toArray()` or `toString()`
|
|
701
|
+
- Default imports are replaced (the last one wins)
|
|
702
|
+
- Imports are merged by source, so multiple `add()` calls for the same source will combine all imports
|
|
703
|
+
|
|
704
|
+
```ts
|
|
705
|
+
bag.add({ source: 'lodash', namedImports: ['debounce'] })
|
|
706
|
+
bag.add({ source: 'lodash', namedImports: ['throttle'] })
|
|
707
|
+
bag.add({ source: 'lodash', namedImports: ['debounce'] }) // duplicate
|
|
708
|
+
|
|
709
|
+
console.log(bag.toString())
|
|
710
|
+
// import { debounce, throttle } from 'lodash'
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Methods
|
|
714
|
+
|
|
715
|
+
#### add(importInfo)
|
|
716
|
+
|
|
717
|
+
Add an import to the bag. Returns `this` for method chaining.
|
|
718
|
+
|
|
719
|
+
```ts
|
|
720
|
+
bag
|
|
721
|
+
.add({ source: 'lodash', namedImports: ['debounce'] })
|
|
722
|
+
.add({ source: 'express', typeImports: ['Request'] })
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
#### toArray()
|
|
726
|
+
|
|
727
|
+
Returns an array of deduplicated `ImportInfo` objects.
|
|
728
|
+
|
|
729
|
+
```ts
|
|
730
|
+
const imports = bag.toArray()
|
|
731
|
+
// [
|
|
732
|
+
// { source: 'lodash', defaultImport: undefined, namedImports: ['debounce'], typeImports: undefined },
|
|
733
|
+
// { source: 'express', defaultImport: undefined, namedImports: undefined, typeImports: ['Request'] }
|
|
734
|
+
// ]
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
#### toString()
|
|
738
|
+
|
|
739
|
+
Generates formatted import statements as a string.
|
|
740
|
+
|
|
741
|
+
```ts
|
|
742
|
+
const code = bag.toString()
|
|
743
|
+
// import { debounce } from 'lodash'
|
|
744
|
+
// import type { Request } from 'express'
|
|
745
|
+
```
|
|
746
|
+
|
|
630
747
|
[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/poppinss/utils/checks.yml?style=for-the-badge
|
|
631
748
|
[gh-workflow-url]: https://github.com/poppinss/utils/actions/workflows/checks.yml 'Github action'
|
|
632
749
|
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
export { __toESM as n, __commonJSMin as t };
|
package/build/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export { naturalSort } from './src/natural_sort.js';
|
|
|
6
6
|
export { isScriptFile } from './src/is_script_file.js';
|
|
7
7
|
export { importDefault } from './src/import_default.js';
|
|
8
8
|
export { MessageBuilder } from './src/message_builder.js';
|
|
9
|
+
export { ImportsBag, type ImportInfo } from './src/imports_bag.js';
|
|
9
10
|
export { defineStaticProperty } from './src/define_static_property.js';
|
package/build/index.js
CHANGED
|
@@ -1,179 +1,154 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from "./
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Returns the original value
|
|
40
|
-
*/
|
|
41
|
-
release() {
|
|
42
|
-
return this.#value;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Transform the original value and create a new
|
|
46
|
-
* secret from it.
|
|
47
|
-
*/
|
|
48
|
-
map(transformFunc) {
|
|
49
|
-
return new _Secret(transformFunc(this.#value));
|
|
50
|
-
}
|
|
1
|
+
import { n as __toESM } from "./chunk-BNZ4_A-W.js";
|
|
2
|
+
import { n as isScriptFile, r as naturalSort, t as require_main } from "./main-CdreHl1_.js";
|
|
3
|
+
import { r as RuntimeException } from "./exception-L7vjh-Gv.js";
|
|
4
|
+
import { n as safeParse, t as safeStringify } from "./main-aO4h7ygK.js";
|
|
5
|
+
import { flattie } from "flattie";
|
|
6
|
+
import { Buffer } from "node:buffer";
|
|
7
|
+
import { timingSafeEqual } from "node:crypto";
|
|
8
|
+
import string from "@poppinss/string";
|
|
9
|
+
const REDACTED = "[redacted]";
|
|
10
|
+
var Secret = class Secret {
|
|
11
|
+
#value;
|
|
12
|
+
#keyword;
|
|
13
|
+
constructor(value, redactedKeyword) {
|
|
14
|
+
this.#value = value;
|
|
15
|
+
this.#keyword = redactedKeyword || REDACTED;
|
|
16
|
+
}
|
|
17
|
+
toJSON() {
|
|
18
|
+
return this.#keyword;
|
|
19
|
+
}
|
|
20
|
+
valueOf() {
|
|
21
|
+
return this.#keyword;
|
|
22
|
+
}
|
|
23
|
+
[Symbol.for("nodejs.util.inspect.custom")]() {
|
|
24
|
+
return this.#keyword;
|
|
25
|
+
}
|
|
26
|
+
toLocaleString() {
|
|
27
|
+
return this.#keyword;
|
|
28
|
+
}
|
|
29
|
+
toString() {
|
|
30
|
+
return this.#keyword;
|
|
31
|
+
}
|
|
32
|
+
release() {
|
|
33
|
+
return this.#value;
|
|
34
|
+
}
|
|
35
|
+
map(transformFunc) {
|
|
36
|
+
return new Secret(transformFunc(this.#value));
|
|
37
|
+
}
|
|
51
38
|
};
|
|
52
|
-
|
|
53
|
-
// src/compose.ts
|
|
54
39
|
function compose(superclass, ...mixins) {
|
|
55
|
-
|
|
40
|
+
return mixins.reduce((c, mixin) => mixin(c), superclass);
|
|
56
41
|
}
|
|
57
|
-
|
|
58
|
-
// src/flatten.ts
|
|
59
|
-
import { flattie } from "flattie";
|
|
60
42
|
function flatten(input, glue, keepNullish) {
|
|
61
|
-
|
|
43
|
+
return flattie(input, glue, keepNullish);
|
|
62
44
|
}
|
|
63
|
-
|
|
64
|
-
// src/safe_equal.ts
|
|
65
|
-
import { Buffer } from "buffer";
|
|
66
|
-
import { timingSafeEqual } from "crypto";
|
|
67
45
|
function safeEqual(trustedValue, userInput) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Buffer.from(trustedValue),
|
|
78
|
-
Buffer.from(userInput)
|
|
79
|
-
);
|
|
46
|
+
if (typeof trustedValue === "string" && typeof userInput === "string") {
|
|
47
|
+
const trustedLength = Buffer.byteLength(trustedValue);
|
|
48
|
+
const trustedValueBuffer = Buffer.alloc(trustedLength, 0, "utf-8");
|
|
49
|
+
trustedValueBuffer.write(trustedValue);
|
|
50
|
+
const userValueBuffer = Buffer.alloc(trustedLength, 0, "utf-8");
|
|
51
|
+
userValueBuffer.write(userInput);
|
|
52
|
+
return timingSafeEqual(trustedValueBuffer, userValueBuffer) && trustedLength === Buffer.byteLength(userInput);
|
|
53
|
+
}
|
|
54
|
+
return timingSafeEqual(Buffer.from(trustedValue), Buffer.from(userInput));
|
|
80
55
|
}
|
|
81
|
-
|
|
82
|
-
// src/import_default.ts
|
|
83
56
|
async function importDefault(importFn, filePath) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
throw new RuntimeException(errorMessage, {
|
|
88
|
-
cause: {
|
|
89
|
-
source: importFn
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
return moduleExports.default;
|
|
57
|
+
const moduleExports = await importFn();
|
|
58
|
+
if (!("default" in moduleExports)) throw new RuntimeException(filePath ? `Missing "export default" in module "${filePath}"` : `Missing "export default" from lazy import "${importFn}"`, { cause: { source: importFn } });
|
|
59
|
+
return moduleExports.default;
|
|
94
60
|
}
|
|
95
|
-
|
|
96
|
-
// src/message_builder.ts
|
|
97
|
-
import string from "@poppinss/string";
|
|
98
61
|
var MessageBuilder = class {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
* Verifies the message for expiry and purpose.
|
|
125
|
-
*/
|
|
126
|
-
verify(message, purpose) {
|
|
127
|
-
const parsed = safeParse(message);
|
|
128
|
-
if (typeof parsed !== "object" || !parsed) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
if (!parsed.message) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
if (parsed.purpose !== purpose) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
if (this.#isExpired(parsed)) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
return parsed.message;
|
|
141
|
-
}
|
|
62
|
+
#getExpiryDate(expiresIn) {
|
|
63
|
+
if (!expiresIn) return;
|
|
64
|
+
const expiryMs = string.milliseconds.parse(expiresIn);
|
|
65
|
+
return new Date(Date.now() + expiryMs);
|
|
66
|
+
}
|
|
67
|
+
#isExpired(message) {
|
|
68
|
+
if (!message.expiryDate) return false;
|
|
69
|
+
const expiryDate = new Date(message.expiryDate);
|
|
70
|
+
return Number.isNaN(expiryDate.getTime()) || expiryDate < /* @__PURE__ */ new Date();
|
|
71
|
+
}
|
|
72
|
+
build(message, expiresIn, purpose) {
|
|
73
|
+
return safeStringify({
|
|
74
|
+
message,
|
|
75
|
+
purpose,
|
|
76
|
+
expiryDate: this.#getExpiryDate(expiresIn)
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
verify(message, purpose) {
|
|
80
|
+
const parsed = safeParse(message);
|
|
81
|
+
if (typeof parsed !== "object" || !parsed) return null;
|
|
82
|
+
if (!parsed.message) return null;
|
|
83
|
+
if (parsed.purpose !== purpose) return null;
|
|
84
|
+
if (this.#isExpired(parsed)) return null;
|
|
85
|
+
return parsed.message;
|
|
86
|
+
}
|
|
142
87
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
88
|
+
var ImportsBag = class {
|
|
89
|
+
#imports = /* @__PURE__ */ new Map();
|
|
90
|
+
#generateImportStatement(imp) {
|
|
91
|
+
const parts = [];
|
|
92
|
+
if (imp.defaultImport || imp.namedImports && imp.namedImports.length > 0) {
|
|
93
|
+
const importParts = [];
|
|
94
|
+
if (imp.defaultImport) importParts.push(imp.defaultImport);
|
|
95
|
+
if (imp.namedImports && imp.namedImports.length > 0) importParts.push(`{ ${imp.namedImports.join(", ")} }`);
|
|
96
|
+
parts.push(`import ${importParts.join(", ")} from '${imp.source}'`);
|
|
97
|
+
}
|
|
98
|
+
if (imp.typeImports && imp.typeImports.length > 0) parts.push(`import type { ${imp.typeImports.join(", ")} } from '${imp.source}'`);
|
|
99
|
+
return parts.join("\n");
|
|
100
|
+
}
|
|
101
|
+
add(importInfo) {
|
|
102
|
+
const existing = this.#imports.get(importInfo.source);
|
|
103
|
+
if (existing) {
|
|
104
|
+
if (importInfo.defaultImport) existing.defaultImport = importInfo.defaultImport;
|
|
105
|
+
if (importInfo.namedImports) {
|
|
106
|
+
if (!existing.namedImports) existing.namedImports = [];
|
|
107
|
+
existing.namedImports.push(...importInfo.namedImports);
|
|
108
|
+
}
|
|
109
|
+
if (importInfo.typeImports) {
|
|
110
|
+
if (!existing.typeImports) existing.typeImports = [];
|
|
111
|
+
existing.typeImports.push(...importInfo.typeImports);
|
|
112
|
+
}
|
|
113
|
+
} else this.#imports.set(importInfo.source, {
|
|
114
|
+
source: importInfo.source,
|
|
115
|
+
defaultImport: importInfo.defaultImport,
|
|
116
|
+
namedImports: importInfo.namedImports ? [...importInfo.namedImports] : void 0,
|
|
117
|
+
typeImports: importInfo.typeImports ? [...importInfo.typeImports] : void 0
|
|
118
|
+
});
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
toArray() {
|
|
122
|
+
return Array.from(this.#imports.values()).map((imp) => ({
|
|
123
|
+
source: imp.source,
|
|
124
|
+
defaultImport: imp.defaultImport,
|
|
125
|
+
namedImports: imp.namedImports ? [...new Set(imp.namedImports)] : void 0,
|
|
126
|
+
typeImports: imp.typeImports ? [...new Set(imp.typeImports)] : void 0
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
toString() {
|
|
130
|
+
return this.toArray().map((imp) => this.#generateImportStatement(imp)).join("\n");
|
|
131
|
+
}
|
|
179
132
|
};
|
|
133
|
+
var import_main = /* @__PURE__ */ __toESM(require_main());
|
|
134
|
+
function defineStaticProperty(self, propertyName, { initialValue, strategy }) {
|
|
135
|
+
if (!self.hasOwnProperty(propertyName)) {
|
|
136
|
+
const value = self[propertyName];
|
|
137
|
+
if (strategy === "define" || value === void 0) {
|
|
138
|
+
Object.defineProperty(self, propertyName, {
|
|
139
|
+
value: initialValue,
|
|
140
|
+
configurable: true,
|
|
141
|
+
enumerable: true,
|
|
142
|
+
writable: true
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
Object.defineProperty(self, propertyName, {
|
|
147
|
+
value: typeof strategy === "function" ? strategy(value) : import_main.default.cloneDeep(value),
|
|
148
|
+
configurable: true,
|
|
149
|
+
enumerable: true,
|
|
150
|
+
writable: true
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export { ImportsBag, MessageBuilder, Secret, compose, defineStaticProperty, flatten, importDefault, isScriptFile, naturalSort, safeEqual };
|