@grest-ts/common 0.0.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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/src/GGAsyncStorage.d.ts +12 -0
- package/dist/src/GGAsyncStorage.d.ts.map +1 -0
- package/dist/src/GGAsyncStorage.js +20 -0
- package/dist/src/GGAsyncStorage.js.map +1 -0
- package/dist/src/GGError.d.ts +32 -0
- package/dist/src/GGError.d.ts.map +1 -0
- package/dist/src/GGError.js +47 -0
- package/dist/src/GGError.js.map +1 -0
- package/dist/src/GGExtensionDiscovery.d.ts +54 -0
- package/dist/src/GGExtensionDiscovery.d.ts.map +1 -0
- package/dist/src/GGExtensionDiscovery.js +281 -0
- package/dist/src/GGExtensionDiscovery.js.map +1 -0
- package/dist/src/Secret.d.ts +46 -0
- package/dist/src/Secret.d.ts.map +1 -0
- package/dist/src/Secret.js +68 -0
- package/dist/src/Secret.js.map +1 -0
- package/dist/src/UnreachableCode.d.ts +5 -0
- package/dist/src/UnreachableCode.d.ts.map +1 -0
- package/dist/src/UnreachableCode.js +9 -0
- package/dist/src/UnreachableCode.js.map +1 -0
- package/dist/src/deepClone.d.ts +6 -0
- package/dist/src/deepClone.d.ts.map +1 -0
- package/dist/src/deepClone.js +38 -0
- package/dist/src/deepClone.js.map +1 -0
- package/dist/src/deepFreeze.d.ts +6 -0
- package/dist/src/deepFreeze.d.ts.map +1 -0
- package/dist/src/deepFreeze.js +22 -0
- package/dist/src/deepFreeze.js.map +1 -0
- package/dist/src/environment.d.ts +14 -0
- package/dist/src/environment.d.ts.map +1 -0
- package/dist/src/environment.js +18 -0
- package/dist/src/environment.js.map +1 -0
- package/dist/src/http.d.ts +50 -0
- package/dist/src/http.d.ts.map +1 -0
- package/dist/src/http.js +50 -0
- package/dist/src/http.js.map +1 -0
- package/dist/src/index-browser.d.ts +12 -0
- package/dist/src/index-browser.d.ts.map +1 -0
- package/dist/src/index-browser.js +13 -0
- package/dist/src/index-browser.js.map +1 -0
- package/dist/src/index-node.d.ts +13 -0
- package/dist/src/index-node.d.ts.map +1 -0
- package/dist/src/index-node.js +13 -0
- package/dist/src/index-node.js.map +1 -0
- package/dist/src/sleep.d.ts +2 -0
- package/dist/src/sleep.d.ts.map +1 -0
- package/dist/src/sleep.js +4 -0
- package/dist/src/sleep.js.map +1 -0
- package/dist/src/tsconfig.json +17 -0
- package/dist/src/types.d.ts +15 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/withTimeout.d.ts +11 -0
- package/dist/src/withTimeout.d.ts.map +1 -0
- package/dist/src/withTimeout.js +22 -0
- package/dist/src/withTimeout.js.map +1 -0
- package/dist/tsconfig.publish.tsbuildinfo +1 -0
- package/package.json +58 -0
- package/src/GGAsyncStorage.ts +27 -0
- package/src/GGError.ts +74 -0
- package/src/GGExtensionDiscovery.ts +314 -0
- package/src/Secret.ts +76 -0
- package/src/UnreachableCode.ts +9 -0
- package/src/deepClone.ts +43 -0
- package/src/deepFreeze.ts +21 -0
- package/src/environment.ts +22 -0
- package/src/http.ts +52 -0
- package/src/index-browser.ts +12 -0
- package/src/index-node.ts +12 -0
- package/src/sleep.ts +3 -0
- package/src/tsconfig.json +17 -0
- package/src/types.ts +16 -0
- package/src/withTimeout.ts +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Grest Games OÜ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @grest-ts/common
|
|
2
|
+
|
|
3
|
+
Common utility functions and types shared across all GG packages.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This package contains utility functions and types that are used across multiple packages in the Grest framework. By consolidating them here, we:
|
|
8
|
+
- Eliminate code duplication
|
|
9
|
+
- Ensure consistency across packages
|
|
10
|
+
- Make maintenance easier
|
|
11
|
+
- Reduce package dependencies
|
|
12
|
+
|
|
13
|
+
## Utilities
|
|
14
|
+
|
|
15
|
+
### `deepFreeze<T>(obj: T): T`
|
|
16
|
+
|
|
17
|
+
Recursively freezes an object and all its nested properties, making it immutable.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { deepFreeze } from '@grest-ts/common';
|
|
21
|
+
|
|
22
|
+
const config = deepFreeze({
|
|
23
|
+
name: 'app',
|
|
24
|
+
settings: {
|
|
25
|
+
debug: true
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// This will throw an error in strict mode
|
|
30
|
+
config.settings.debug = false;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### `withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T>`
|
|
34
|
+
|
|
35
|
+
Wraps a promise with a timeout. If the promise doesn't resolve/reject within the specified timeout, the returned promise will reject with an error.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { withTimeout } from '@grest-ts/common';
|
|
39
|
+
|
|
40
|
+
const result = await withTimeout(
|
|
41
|
+
fetch('https://api.example.com'),
|
|
42
|
+
5000,
|
|
43
|
+
'API request timed out'
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Types
|
|
48
|
+
|
|
49
|
+
### `DeepPartial<T>`
|
|
50
|
+
|
|
51
|
+
Recursively makes all properties of an object optional. Useful for partial matching in tests and validation.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { DeepPartial } from '@grest-ts/common';
|
|
55
|
+
|
|
56
|
+
interface User {
|
|
57
|
+
id: string;
|
|
58
|
+
profile: {
|
|
59
|
+
name: string;
|
|
60
|
+
email: string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// All properties are optional
|
|
65
|
+
const partialUser: DeepPartial<User> = {
|
|
66
|
+
profile: {
|
|
67
|
+
name: 'Alice'
|
|
68
|
+
// email is optional
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Adding New Utilities
|
|
74
|
+
|
|
75
|
+
When adding new utilities to this package:
|
|
76
|
+
|
|
77
|
+
1. Create a new file in `src/` for the utility
|
|
78
|
+
2. Export it from `src/index.ts`
|
|
79
|
+
3. Add JSDoc comments explaining usage
|
|
80
|
+
4. Update this README with examples
|
|
81
|
+
5. Ensure the utility is truly reusable across multiple packages
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface IAsyncStorage<T> {
|
|
2
|
+
getStore(): T | undefined;
|
|
3
|
+
run<R>(store: T, fn: () => R): R;
|
|
4
|
+
enterWith(store: T): void;
|
|
5
|
+
}
|
|
6
|
+
export declare class BrowserAsyncStorage<T> implements IAsyncStorage<T> {
|
|
7
|
+
private store;
|
|
8
|
+
getStore(): T | undefined;
|
|
9
|
+
run<R>(store: T, fn: () => R): R;
|
|
10
|
+
enterWith(store: T): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=GGAsyncStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGAsyncStorage.d.ts","sourceRoot":"","sources":["../../src/GGAsyncStorage.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1B,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACjC,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CAC7B;AAED,qBAAa,mBAAmB,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IAC3D,OAAO,CAAC,KAAK,CAAgB;IAE7B,QAAQ,IAAI,CAAC,GAAG,SAAS;IAIzB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAUhC,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;CAG5B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class BrowserAsyncStorage {
|
|
2
|
+
store;
|
|
3
|
+
getStore() {
|
|
4
|
+
return this.store;
|
|
5
|
+
}
|
|
6
|
+
run(store, fn) {
|
|
7
|
+
const prev = this.store;
|
|
8
|
+
this.store = store;
|
|
9
|
+
try {
|
|
10
|
+
return fn();
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
this.store = prev;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
enterWith(store) {
|
|
17
|
+
this.store = store;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=GGAsyncStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGAsyncStorage.js","sourceRoot":"","sources":["../../src/GGAsyncStorage.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,mBAAmB;IACpB,KAAK,CAAgB;IAE7B,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,GAAG,CAAI,KAAQ,EAAE,EAAW;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC;YACD,OAAO,EAAE,EAAE,CAAC;QAChB,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACtB,CAAC;IACL,CAAC;IAED,SAAS,CAAC,KAAQ;QACd,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context for constructing GGError instances
|
|
3
|
+
* This is ONLY an input parameter - not stored on the error object
|
|
4
|
+
* All fields become direct readonly properties on GGError
|
|
5
|
+
*/
|
|
6
|
+
export interface GGErrorContext {
|
|
7
|
+
displayMessage?: string;
|
|
8
|
+
debugMessage?: string;
|
|
9
|
+
debugData?: any;
|
|
10
|
+
originalError?: GGError | Error | string | unknown;
|
|
11
|
+
refId?: string;
|
|
12
|
+
timestamp?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* These values are only returned when running tests or locally.
|
|
16
|
+
*/
|
|
17
|
+
export interface GGErrorDebugMessage {
|
|
18
|
+
debugMessage?: string;
|
|
19
|
+
debugData?: any;
|
|
20
|
+
originalError?: GGErrorDebugMessage | Error | string | unknown;
|
|
21
|
+
}
|
|
22
|
+
export declare class GGError extends Error {
|
|
23
|
+
readonly refId: string;
|
|
24
|
+
readonly timestamp: number;
|
|
25
|
+
readonly displayMessage?: string;
|
|
26
|
+
readonly debugMessage?: string;
|
|
27
|
+
readonly debugData?: any;
|
|
28
|
+
readonly originalError?: GGError | Error | string | unknown;
|
|
29
|
+
constructor(message: string, context?: GGErrorContext | Error);
|
|
30
|
+
static fromUnknown(error: unknown): GGError;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=GGError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGError.d.ts","sourceRoot":"","sources":["../../src/GGError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,GAAG,CAAA;IACf,aAAa,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAGnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,GAAG,CAAA;IACf,aAAa,CAAC,EAAE,mBAAmB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAClE;AAED,qBAAa,OAAQ,SAAQ,KAAK;IAE9B,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxC,SAAgB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtC,SAAgB,SAAS,CAAC,EAAE,GAAG,CAAC;IAChC,SAAgB,aAAa,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;gBAEvD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,KAAK;WA6B/C,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;CASrD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class GGError extends Error {
|
|
2
|
+
refId;
|
|
3
|
+
timestamp;
|
|
4
|
+
displayMessage;
|
|
5
|
+
debugMessage;
|
|
6
|
+
debugData;
|
|
7
|
+
originalError;
|
|
8
|
+
constructor(message, context) {
|
|
9
|
+
if (context instanceof Error) {
|
|
10
|
+
context = {
|
|
11
|
+
originalError: context
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
else if (!context) {
|
|
15
|
+
context = {};
|
|
16
|
+
}
|
|
17
|
+
let refId;
|
|
18
|
+
let timestamp;
|
|
19
|
+
if (context.originalError instanceof GGError) {
|
|
20
|
+
refId = context.originalError.refId;
|
|
21
|
+
timestamp = context.originalError.timestamp;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
refId = context.refId ?? "ERR_REF_" + Math.random().toString(36).substring(2, 10) + Date.now().toString(36);
|
|
25
|
+
timestamp = context.timestamp ?? Date.now();
|
|
26
|
+
}
|
|
27
|
+
super(message + " {" + refId + "} " + (context?.displayMessage ? ": " + context?.displayMessage : ""));
|
|
28
|
+
this.refId = refId;
|
|
29
|
+
this.timestamp = timestamp;
|
|
30
|
+
this.displayMessage = context.displayMessage;
|
|
31
|
+
this.debugMessage = context.debugMessage;
|
|
32
|
+
this.debugData = context.debugData;
|
|
33
|
+
this.originalError = context.originalError;
|
|
34
|
+
}
|
|
35
|
+
static fromUnknown(error) {
|
|
36
|
+
if (error instanceof GGError) {
|
|
37
|
+
return error;
|
|
38
|
+
}
|
|
39
|
+
else if (error instanceof Error) {
|
|
40
|
+
return new GGError(error.message, error);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return new GGError("Unknown", { debugData: error });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=GGError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGError.js","sourceRoot":"","sources":["../../src/GGError.ts"],"names":[],"mappings":"AAyBA,MAAM,OAAO,OAAQ,SAAQ,KAAK;IAEd,KAAK,CAAS;IACd,SAAS,CAAS;IAClB,cAAc,CAAU;IACxB,YAAY,CAAU;IACtB,SAAS,CAAO;IAChB,aAAa,CAAsC;IAEnE,YAAY,OAAe,EAAE,OAAgC;QAEzD,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,GAAG;gBACN,aAAa,EAAE,OAAO;aACzB,CAAA;QACL,CAAC;aAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,GAAG,EAAE,CAAC;QACjB,CAAC;QAED,IAAI,KAAa,CAAC;QAClB,IAAI,SAAiB,CAAC;QACtB,IAAI,OAAO,CAAC,aAAa,YAAY,OAAO,EAAE,CAAC;YAC3C,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC;YACpC,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC;QAChD,CAAC;aAAM,CAAC;YACJ,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC5G,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAChD,CAAC;QAED,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,GAAG,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACvG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC/C,CAAC;IAEM,MAAM,CAAC,WAAW,CAAC,KAAc;QACpC,IAAI,KAAK,YAAY,OAAO,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACjB,CAAC;aAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAChC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACJ,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,EAAC,SAAS,EAAE,KAAK,EAAC,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;CACJ"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovers extensions by scanning node_modules for packages
|
|
3
|
+
* that follow the convention of having a {name}/index-{name}.ts file.
|
|
4
|
+
*
|
|
5
|
+
* For example, with name="testkit":
|
|
6
|
+
* - Scans for: testkit/index-testkit.ts
|
|
7
|
+
* - Types dir: node_modules/@types/grest-ts-testkits
|
|
8
|
+
*
|
|
9
|
+
* Generates:
|
|
10
|
+
* - node_modules/@types/grest-ts-{name}s/index.d.ts - For IDE type completion (triple-slash references)
|
|
11
|
+
*
|
|
12
|
+
* Runtime loading is done via dynamic imports of discovered extensions.
|
|
13
|
+
*/
|
|
14
|
+
export declare class GGExtensionDiscovery {
|
|
15
|
+
private static loadedExtensions;
|
|
16
|
+
private readonly name;
|
|
17
|
+
private readonly typesDir;
|
|
18
|
+
private readonly filePattern;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new extension discovery instance.
|
|
21
|
+
* @param name The extension name (e.g., "testkit", "codegen")
|
|
22
|
+
*/
|
|
23
|
+
constructor(name: string);
|
|
24
|
+
/**
|
|
25
|
+
* Generate types file for IDE support without loading extensions.
|
|
26
|
+
* Use this during build/check steps to ensure IDE has proper type completion.
|
|
27
|
+
*/
|
|
28
|
+
generateTypes(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Discover and load all extensions.
|
|
31
|
+
* - Scans for extension packages
|
|
32
|
+
* - Generates .d.ts file for IDE support
|
|
33
|
+
* - Dynamically imports extensions for runtime
|
|
34
|
+
*/
|
|
35
|
+
load(): Promise<void>;
|
|
36
|
+
private acquireLock;
|
|
37
|
+
private releaseLock;
|
|
38
|
+
private waitForLock;
|
|
39
|
+
private discoverAndLoad;
|
|
40
|
+
private loadFromCache;
|
|
41
|
+
scan(cwd: string): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a package's install directory by walking up node_modules directories from cwd.
|
|
44
|
+
* Mimics Node.js module resolution: checks cwd/node_modules/<pkg>, ../node_modules/<pkg>, etc.
|
|
45
|
+
*/
|
|
46
|
+
private resolvePackageDir;
|
|
47
|
+
private readDependencyNames;
|
|
48
|
+
findMonorepoRoot(startDir: string): string | null;
|
|
49
|
+
private getLockfileMtime;
|
|
50
|
+
private readCache;
|
|
51
|
+
private writeTypesFile;
|
|
52
|
+
private capitalize;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=GGExtensionDiscovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGExtensionDiscovery.d.ts","sourceRoot":"","sources":["../../src/GGExtensionDiscovery.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;GAYG;AACH,qBAAa,oBAAoB;IAE7B,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAqB;IAEpD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC;;;OAGG;gBACS,IAAI,EAAE,MAAM;IAMxB;;;OAGG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3C;;;;;OAKG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBlC,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,WAAW;YAQL,WAAW;YAYX,eAAe;YAqBf,aAAa;IAUd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA4CjD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,mBAAmB;IAYpB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoBxD,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,cAAc;IA6BtB,OAAO,CAAC,UAAU;CAGrB"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { pathToFileURL } from 'url';
|
|
5
|
+
const TYPES_FILE = 'index.d.ts';
|
|
6
|
+
const LOCK_FILE = 'index.d.ts.lock';
|
|
7
|
+
const CACHE_START = '/* @cache-start ';
|
|
8
|
+
const CACHE_END = ' @cache-end */';
|
|
9
|
+
/**
|
|
10
|
+
* Discovers extensions by scanning node_modules for packages
|
|
11
|
+
* that follow the convention of having a {name}/index-{name}.ts file.
|
|
12
|
+
*
|
|
13
|
+
* For example, with name="testkit":
|
|
14
|
+
* - Scans for: testkit/index-testkit.ts
|
|
15
|
+
* - Types dir: node_modules/@types/grest-ts-testkits
|
|
16
|
+
*
|
|
17
|
+
* Generates:
|
|
18
|
+
* - node_modules/@types/grest-ts-{name}s/index.d.ts - For IDE type completion (triple-slash references)
|
|
19
|
+
*
|
|
20
|
+
* Runtime loading is done via dynamic imports of discovered extensions.
|
|
21
|
+
*/
|
|
22
|
+
export class GGExtensionDiscovery {
|
|
23
|
+
static loadedExtensions = new Set();
|
|
24
|
+
name;
|
|
25
|
+
typesDir;
|
|
26
|
+
filePattern;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new extension discovery instance.
|
|
29
|
+
* @param name The extension name (e.g., "testkit", "codegen")
|
|
30
|
+
*/
|
|
31
|
+
constructor(name) {
|
|
32
|
+
this.name = name;
|
|
33
|
+
this.typesDir = `node_modules/@types/grest-ts-${name}s`;
|
|
34
|
+
this.filePattern = `${name}/index-${name}.ts`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generate types file for IDE support without loading extensions.
|
|
38
|
+
* Use this during build/check steps to ensure IDE has proper type completion.
|
|
39
|
+
*/
|
|
40
|
+
async generateTypes() {
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
const typesDir = path.join(cwd, this.typesDir);
|
|
43
|
+
const typesFile = path.join(typesDir, TYPES_FILE);
|
|
44
|
+
const lockfileMtime = this.getLockfileMtime(cwd);
|
|
45
|
+
const extensions = await this.scan(cwd);
|
|
46
|
+
this.writeTypesFile(typesFile, extensions, typesDir, lockfileMtime);
|
|
47
|
+
console.log(`[GG${this.capitalize(this.name)}] Generated types for ${extensions.length} ${this.name}(s)`);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Discover and load all extensions.
|
|
51
|
+
* - Scans for extension packages
|
|
52
|
+
* - Generates .d.ts file for IDE support
|
|
53
|
+
* - Dynamically imports extensions for runtime
|
|
54
|
+
*/
|
|
55
|
+
async load() {
|
|
56
|
+
if (GGExtensionDiscovery.loadedExtensions.has(this.name)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
GGExtensionDiscovery.loadedExtensions.add(this.name);
|
|
60
|
+
const cwd = process.cwd();
|
|
61
|
+
const typesDir = path.join(cwd, this.typesDir);
|
|
62
|
+
const typesFile = path.join(typesDir, TYPES_FILE);
|
|
63
|
+
const lockFile = path.join(typesDir, LOCK_FILE);
|
|
64
|
+
// Try to acquire lock
|
|
65
|
+
if (this.acquireLock(lockFile)) {
|
|
66
|
+
try {
|
|
67
|
+
await this.discoverAndLoad(cwd, typesFile, typesDir);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
this.releaseLock(lockFile);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Wait for lock to be released, then load from cache
|
|
75
|
+
await this.waitForLock(lockFile);
|
|
76
|
+
await this.loadFromCache(typesFile);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
acquireLock(lockFile) {
|
|
80
|
+
try {
|
|
81
|
+
fs.mkdirSync(path.dirname(lockFile), { recursive: true });
|
|
82
|
+
fs.writeFileSync(lockFile, String(process.pid), { flag: 'wx' });
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
releaseLock(lockFile) {
|
|
90
|
+
try {
|
|
91
|
+
fs.unlinkSync(lockFile);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Ignore
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async waitForLock(lockFile, timeout = 30000) {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
while (Date.now() - start < timeout) {
|
|
100
|
+
if (!fs.existsSync(lockFile)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await new Promise(r => setTimeout(r, 50));
|
|
104
|
+
}
|
|
105
|
+
// Timeout - try to clean up stale lock
|
|
106
|
+
this.releaseLock(lockFile);
|
|
107
|
+
}
|
|
108
|
+
async discoverAndLoad(cwd, typesFile, typesDir) {
|
|
109
|
+
const lockfileMtime = this.getLockfileMtime(cwd);
|
|
110
|
+
// Check cache embedded in types file
|
|
111
|
+
const cached = this.readCache(typesFile);
|
|
112
|
+
let extensions;
|
|
113
|
+
if (cached && cached.lockfileMtime === lockfileMtime) {
|
|
114
|
+
extensions = cached.extensions;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
extensions = await this.scan(cwd);
|
|
118
|
+
this.writeTypesFile(typesFile, extensions, typesDir, lockfileMtime);
|
|
119
|
+
console.log(`[GG${this.capitalize(this.name)}] Discovered ${extensions.length} ${this.name}(s)`);
|
|
120
|
+
}
|
|
121
|
+
// Dynamically import all extensions
|
|
122
|
+
for (const extension of extensions) {
|
|
123
|
+
await import(pathToFileURL(extension).href);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async loadFromCache(typesFile) {
|
|
127
|
+
const cached = this.readCache(typesFile);
|
|
128
|
+
if (cached) {
|
|
129
|
+
for (const extension of cached.extensions) {
|
|
130
|
+
await import(pathToFileURL(extension).href);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async scan(cwd) {
|
|
135
|
+
const extensions = [];
|
|
136
|
+
// Resolve extensions by reading package.json dependencies and walking up
|
|
137
|
+
// node_modules directories (like Node.js module resolution).
|
|
138
|
+
// This works regardless of hoisting, workspaces, pnpm, etc.
|
|
139
|
+
const depNames = this.readDependencyNames(cwd);
|
|
140
|
+
for (const dep of depNames) {
|
|
141
|
+
const pkgDir = this.resolvePackageDir(dep, cwd);
|
|
142
|
+
if (pkgDir) {
|
|
143
|
+
// Check source path first (local dev with tsx), then compiled dist path (published packages)
|
|
144
|
+
const sourceFile = path.join(pkgDir, this.filePattern);
|
|
145
|
+
const distFile = path.join(pkgDir, 'dist', this.filePattern.replace(/\.ts$/, '.js'));
|
|
146
|
+
if (fs.existsSync(sourceFile)) {
|
|
147
|
+
extensions.push(sourceFile);
|
|
148
|
+
}
|
|
149
|
+
else if (fs.existsSync(distFile)) {
|
|
150
|
+
extensions.push(distFile);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Also scan monorepo packages/ directories (for framework development)
|
|
155
|
+
const monorepoRoot = this.findMonorepoRoot(cwd);
|
|
156
|
+
if (monorepoRoot) {
|
|
157
|
+
const monorepoExtensions = await fg([
|
|
158
|
+
`packages/*/${this.filePattern}`,
|
|
159
|
+
`packages/*/*/${this.filePattern}`,
|
|
160
|
+
`packages-*/*/${this.filePattern}`,
|
|
161
|
+
`packages-*/*/*/${this.filePattern}`,
|
|
162
|
+
], {
|
|
163
|
+
cwd: monorepoRoot,
|
|
164
|
+
absolute: true,
|
|
165
|
+
onlyFiles: true
|
|
166
|
+
});
|
|
167
|
+
extensions.push(...monorepoExtensions);
|
|
168
|
+
}
|
|
169
|
+
// Resolve symlinks to real paths before deduping to avoid loading same file twice
|
|
170
|
+
// (e.g., node_modules/@grest-ts/foo -> packages/foo would otherwise be seen as different)
|
|
171
|
+
const resolvedExtensions = extensions.map(ext => fs.realpathSync(ext));
|
|
172
|
+
return [...new Set(resolvedExtensions)].sort();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Resolve a package's install directory by walking up node_modules directories from cwd.
|
|
176
|
+
* Mimics Node.js module resolution: checks cwd/node_modules/<pkg>, ../node_modules/<pkg>, etc.
|
|
177
|
+
*/
|
|
178
|
+
resolvePackageDir(dep, cwd) {
|
|
179
|
+
let dir = cwd;
|
|
180
|
+
const root = path.parse(dir).root;
|
|
181
|
+
while (dir !== root) {
|
|
182
|
+
const pkgDir = path.join(dir, 'node_modules', dep);
|
|
183
|
+
if (fs.existsSync(path.join(pkgDir, 'package.json'))) {
|
|
184
|
+
return pkgDir;
|
|
185
|
+
}
|
|
186
|
+
dir = path.dirname(dir);
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
readDependencyNames(cwd) {
|
|
191
|
+
try {
|
|
192
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'));
|
|
193
|
+
return [
|
|
194
|
+
...Object.keys(pkg.dependencies || {}),
|
|
195
|
+
...Object.keys(pkg.devDependencies || {}),
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
findMonorepoRoot(startDir) {
|
|
203
|
+
let currentDir = startDir;
|
|
204
|
+
const root = path.parse(currentDir).root;
|
|
205
|
+
while (currentDir !== root) {
|
|
206
|
+
const packagesPath = path.join(currentDir, 'packages');
|
|
207
|
+
try {
|
|
208
|
+
const stat = fs.statSync(packagesPath);
|
|
209
|
+
if (stat.isDirectory()) {
|
|
210
|
+
return currentDir;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Directory doesn't exist, continue up
|
|
215
|
+
}
|
|
216
|
+
currentDir = path.dirname(currentDir);
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
getLockfileMtime(cwd) {
|
|
221
|
+
const lockfiles = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'];
|
|
222
|
+
// Walk up directories to find lockfile (handles workspaces where lockfile is at root)
|
|
223
|
+
let dir = cwd;
|
|
224
|
+
const root = path.parse(dir).root;
|
|
225
|
+
while (dir !== root) {
|
|
226
|
+
for (const lockfile of lockfiles) {
|
|
227
|
+
try {
|
|
228
|
+
return fs.statSync(path.join(dir, lockfile)).mtimeMs;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// File doesn't exist, try next
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
dir = path.dirname(dir);
|
|
235
|
+
}
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
readCache(typesFile) {
|
|
239
|
+
try {
|
|
240
|
+
const content = fs.readFileSync(typesFile, 'utf-8');
|
|
241
|
+
const startIdx = content.indexOf(CACHE_START);
|
|
242
|
+
const endIdx = content.indexOf(CACHE_END);
|
|
243
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
const jsonStr = content.slice(startIdx + CACHE_START.length, endIdx).trim();
|
|
247
|
+
return JSON.parse(jsonStr);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
writeTypesFile(typesFile, extensions, typesDir, lockfileMtime) {
|
|
254
|
+
const lines = [
|
|
255
|
+
`// Auto-generated by GGExtensionDiscovery (${this.name}) - DO NOT EDIT`,
|
|
256
|
+
'// TypeScript automatically includes @types/* packages, so no tsconfig changes needed.',
|
|
257
|
+
''
|
|
258
|
+
];
|
|
259
|
+
for (const extension of extensions) {
|
|
260
|
+
const relativePath = path.relative(typesDir, extension).replace(/\\/g, '/');
|
|
261
|
+
lines.push(`/// <reference path="${relativePath}" />`);
|
|
262
|
+
}
|
|
263
|
+
// Embed cache as JSON block comment at end of file (single line so TypeScript ignores it)
|
|
264
|
+
lines.push('');
|
|
265
|
+
lines.push(CACHE_START + JSON.stringify({ lockfileMtime, extensions }) + CACHE_END);
|
|
266
|
+
lines.push('');
|
|
267
|
+
fs.mkdirSync(typesDir, { recursive: true });
|
|
268
|
+
fs.writeFileSync(typesFile, lines.join('\n'));
|
|
269
|
+
// Write package.json to make it a proper @types package
|
|
270
|
+
const packageJson = {
|
|
271
|
+
name: `@types/grest-ts-${this.name}s`,
|
|
272
|
+
version: '1.0.0',
|
|
273
|
+
types: 'index.d.ts'
|
|
274
|
+
};
|
|
275
|
+
fs.writeFileSync(path.join(typesDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
276
|
+
}
|
|
277
|
+
capitalize(str) {
|
|
278
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=GGExtensionDiscovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GGExtensionDiscovery.js","sourceRoot":"","sources":["../../src/GGExtensionDiscovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret - A wrapper for sensitive values that prevents accidental logging.
|
|
3
|
+
*
|
|
4
|
+
* Secrets are automatically redacted when:
|
|
5
|
+
* - Converted to string (toString)
|
|
6
|
+
* - Serialized to JSON (toJSON)
|
|
7
|
+
* - Logged with console.log or util.inspect
|
|
8
|
+
*
|
|
9
|
+
* To access the actual value, you must explicitly call unwrap().
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const dbPassword = new Secret('super-secret-password');
|
|
14
|
+
*
|
|
15
|
+
* console.log(dbPassword); // "[REDACTED]"
|
|
16
|
+
* console.log(JSON.stringify(dbPassword)); // "[REDACTED]"
|
|
17
|
+
*
|
|
18
|
+
* // Explicit unwrap required to get value
|
|
19
|
+
* const password = dbPassword.unwrap();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class Secret {
|
|
23
|
+
#private;
|
|
24
|
+
constructor(value: string);
|
|
25
|
+
/**
|
|
26
|
+
* Get the actual secret value.
|
|
27
|
+
* Use with care - avoid logging the result.
|
|
28
|
+
*/
|
|
29
|
+
unwrap(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Check if the secret has a non-empty value.
|
|
32
|
+
*/
|
|
33
|
+
hasValue(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Compare with another secret without exposing either value.
|
|
36
|
+
*/
|
|
37
|
+
equals(other: Secret): boolean;
|
|
38
|
+
toString(): string;
|
|
39
|
+
toJSON(): string;
|
|
40
|
+
valueOf(): string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Type guard to check if a value is a Secret.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isSecret(value: unknown): value is Secret;
|
|
46
|
+
//# sourceMappingURL=Secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Secret.d.ts","sourceRoot":"","sources":["../../src/Secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,MAAM;;gBAEH,KAAK,EAAE,MAAM;IAIzB;;;OAGG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAM9B,QAAQ,IAAI,MAAM;IAIlB,MAAM,IAAI,MAAM;IAUhB,OAAO,IAAI,MAAM;CAGpB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD"}
|