@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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/dist/src/GGAsyncStorage.d.ts +12 -0
  4. package/dist/src/GGAsyncStorage.d.ts.map +1 -0
  5. package/dist/src/GGAsyncStorage.js +20 -0
  6. package/dist/src/GGAsyncStorage.js.map +1 -0
  7. package/dist/src/GGError.d.ts +32 -0
  8. package/dist/src/GGError.d.ts.map +1 -0
  9. package/dist/src/GGError.js +47 -0
  10. package/dist/src/GGError.js.map +1 -0
  11. package/dist/src/GGExtensionDiscovery.d.ts +54 -0
  12. package/dist/src/GGExtensionDiscovery.d.ts.map +1 -0
  13. package/dist/src/GGExtensionDiscovery.js +281 -0
  14. package/dist/src/GGExtensionDiscovery.js.map +1 -0
  15. package/dist/src/Secret.d.ts +46 -0
  16. package/dist/src/Secret.d.ts.map +1 -0
  17. package/dist/src/Secret.js +68 -0
  18. package/dist/src/Secret.js.map +1 -0
  19. package/dist/src/UnreachableCode.d.ts +5 -0
  20. package/dist/src/UnreachableCode.d.ts.map +1 -0
  21. package/dist/src/UnreachableCode.js +9 -0
  22. package/dist/src/UnreachableCode.js.map +1 -0
  23. package/dist/src/deepClone.d.ts +6 -0
  24. package/dist/src/deepClone.d.ts.map +1 -0
  25. package/dist/src/deepClone.js +38 -0
  26. package/dist/src/deepClone.js.map +1 -0
  27. package/dist/src/deepFreeze.d.ts +6 -0
  28. package/dist/src/deepFreeze.d.ts.map +1 -0
  29. package/dist/src/deepFreeze.js +22 -0
  30. package/dist/src/deepFreeze.js.map +1 -0
  31. package/dist/src/environment.d.ts +14 -0
  32. package/dist/src/environment.d.ts.map +1 -0
  33. package/dist/src/environment.js +18 -0
  34. package/dist/src/environment.js.map +1 -0
  35. package/dist/src/http.d.ts +50 -0
  36. package/dist/src/http.d.ts.map +1 -0
  37. package/dist/src/http.js +50 -0
  38. package/dist/src/http.js.map +1 -0
  39. package/dist/src/index-browser.d.ts +12 -0
  40. package/dist/src/index-browser.d.ts.map +1 -0
  41. package/dist/src/index-browser.js +13 -0
  42. package/dist/src/index-browser.js.map +1 -0
  43. package/dist/src/index-node.d.ts +13 -0
  44. package/dist/src/index-node.d.ts.map +1 -0
  45. package/dist/src/index-node.js +13 -0
  46. package/dist/src/index-node.js.map +1 -0
  47. package/dist/src/sleep.d.ts +2 -0
  48. package/dist/src/sleep.d.ts.map +1 -0
  49. package/dist/src/sleep.js +4 -0
  50. package/dist/src/sleep.js.map +1 -0
  51. package/dist/src/tsconfig.json +17 -0
  52. package/dist/src/types.d.ts +15 -0
  53. package/dist/src/types.d.ts.map +1 -0
  54. package/dist/src/types.js +2 -0
  55. package/dist/src/types.js.map +1 -0
  56. package/dist/src/withTimeout.d.ts +11 -0
  57. package/dist/src/withTimeout.d.ts.map +1 -0
  58. package/dist/src/withTimeout.js +22 -0
  59. package/dist/src/withTimeout.js.map +1 -0
  60. package/dist/tsconfig.publish.tsbuildinfo +1 -0
  61. package/package.json +58 -0
  62. package/src/GGAsyncStorage.ts +27 -0
  63. package/src/GGError.ts +74 -0
  64. package/src/GGExtensionDiscovery.ts +314 -0
  65. package/src/Secret.ts +76 -0
  66. package/src/UnreachableCode.ts +9 -0
  67. package/src/deepClone.ts +43 -0
  68. package/src/deepFreeze.ts +21 -0
  69. package/src/environment.ts +22 -0
  70. package/src/http.ts +52 -0
  71. package/src/index-browser.ts +12 -0
  72. package/src/index-node.ts +12 -0
  73. package/src/sleep.ts +3 -0
  74. package/src/tsconfig.json +17 -0
  75. package/src/types.ts +16 -0
  76. 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"}