ts-ag 0.0.1-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +6 -0
  2. package/dist/browser.d.ts +2 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/browser.js +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/lambda/browser.d.ts +4 -0
  9. package/dist/lambda/browser.d.ts.map +1 -0
  10. package/dist/lambda/browser.js +3 -0
  11. package/dist/lambda/client-types.d.ts +74 -0
  12. package/dist/lambda/client-types.d.ts.map +1 -0
  13. package/dist/lambda/client-types.js +1 -0
  14. package/dist/lambda/client.d.ts +26 -0
  15. package/dist/lambda/client.d.ts.map +1 -0
  16. package/dist/lambda/client.js +53 -0
  17. package/dist/lambda/deserializer.d.ts +3 -0
  18. package/dist/lambda/deserializer.d.ts.map +1 -0
  19. package/dist/lambda/deserializer.js +11 -0
  20. package/dist/lambda/handlerUtils.d.ts +31 -0
  21. package/dist/lambda/handlerUtils.d.ts.map +1 -0
  22. package/dist/lambda/handlerUtils.js +24 -0
  23. package/dist/lambda/index.d.ts +2 -0
  24. package/dist/lambda/index.d.ts.map +1 -0
  25. package/dist/lambda/index.js +1 -0
  26. package/dist/lambda/serializer.d.ts +7 -0
  27. package/dist/lambda/serializer.d.ts.map +1 -0
  28. package/dist/lambda/serializer.js +15 -0
  29. package/dist/ts-alias.d.ts +3 -0
  30. package/dist/ts-alias.d.ts.map +1 -0
  31. package/dist/ts-alias.js +167 -0
  32. package/package.json +59 -0
  33. package/src/browser.ts +1 -0
  34. package/src/index.ts +1 -0
  35. package/src/lambda/browser.ts +3 -0
  36. package/src/lambda/client-types.ts +63 -0
  37. package/src/lambda/client.ts +98 -0
  38. package/src/lambda/deserializer.ts +11 -0
  39. package/src/lambda/handlerUtils.ts +47 -0
  40. package/src/lambda/index.ts +1 -0
  41. package/src/lambda/serializer.ts +14 -0
  42. package/src/ts-alias.ts +194 -0
package/README.md ADDED
@@ -0,0 +1,6 @@
1
+ ### Typescript
2
+
3
+ Although shadcn exists here in the repo it is not shipped. This is simply for tooling and nothing else, as such in the
4
+ `svelte.config.js` the aliases are added just for typescript, so that they are not resolved by the packaging process.
5
+
6
+ This allows consumers to bring their own shadcn components just so long as they resolve `$shadcn`
@@ -0,0 +1,2 @@
1
+ export * from './lambda/browser.js';
2
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './lambda/browser.js';
@@ -0,0 +1,2 @@
1
+ export * from './lambda/index.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './lambda/index.js';
@@ -0,0 +1,4 @@
1
+ export * from './client-types.js';
2
+ export * from './client.js';
3
+ export * from './handlerUtils.js';
4
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/lambda/browser.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './client-types.js';
2
+ export * from './client.js';
3
+ export * from './handlerUtils.js';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Converts a RawApiGatewayHandler response type to a fetch like response type.
3
+ */
4
+ type ConvertToFetch<T> = T extends {
5
+ statusCode: number;
6
+ body: object;
7
+ headers: object;
8
+ } ? {
9
+ ok: T['statusCode'] extends 200 | 201 | 204 ? true : false;
10
+ json: () => Promise<T['body']>;
11
+ status: T['statusCode'];
12
+ } : T;
13
+ export type CleanResponse = Omit<Response, 'status' | 'ok' | 'json'>;
14
+ export type FetchResponse<T extends (...args: any) => any> = ConvertToFetch<Awaited<ReturnType<T>>> & CleanResponse;
15
+ /**
16
+ * Extracts the requestInput type from an API endpoint definition
17
+ * @template E - Union type of all API endpoints
18
+ * @template P - Path string literal type (e.g. 'payments/account')
19
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
20
+ */
21
+ export type ApiInput<E extends {
22
+ path: string;
23
+ method: string;
24
+ requestInput: any;
25
+ }, P extends E['path'], M extends E['method']> = Extract<E, {
26
+ path: P;
27
+ method: M;
28
+ }>['requestInput'];
29
+ /**
30
+ * Extracts the requestOutput type from an API endpoint definition
31
+ * @template E - Union type of all API endpoints
32
+ * @template P - Path string literal type (e.g. 'payments/account')
33
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
34
+ */
35
+ export type ApiOutput<E extends {
36
+ path: string;
37
+ method: string;
38
+ requestOutput: any;
39
+ }, P extends E['path'], M extends E['method']> = Extract<E, {
40
+ path: P;
41
+ method: M;
42
+ }>['requestOutput'];
43
+ /**
44
+ * Extracts the response type from an API endpoint definition
45
+ * @template E - Union type of all API endpoints
46
+ * @template P - Path string literal type (e.g. 'payments/account')
47
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
48
+ */
49
+ export type ApiResponse<E extends {
50
+ path: string;
51
+ method: string;
52
+ response: any;
53
+ }, P extends E['path'], M extends E['method']> = Extract<E, {
54
+ path: P;
55
+ method: M;
56
+ }>['response'];
57
+ /**
58
+ * Extracts the sucessful body type from an API endpoint definition
59
+ * @template E - Union type of all API endpoints
60
+ * @template P - Path string literal type (e.g. 'payments/account')
61
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
62
+ */
63
+ export type ApiSuccessBody<E extends {
64
+ path: string;
65
+ method: string;
66
+ response: any;
67
+ }, P extends E['path'], M extends E['method']> = Extract<Extract<E, {
68
+ path: P;
69
+ method: M;
70
+ }>['response'], {
71
+ status: 200;
72
+ }>['json'] extends () => Promise<infer R> ? R : unknown;
73
+ export {};
74
+ //# sourceMappingURL=client-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-types.d.ts","sourceRoot":"","sources":["../../src/lambda/client-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,KAAK,cAAc,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpF;IACE,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC;IAC3D,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;CACzB,GACD,CAAC,CAAC;AAEN,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;AACrE,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC;AAEpH;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,CAClB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,GAAG,CAAA;CAAE,EAC7D,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IACnB,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,cAAc,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,CACnB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,GAAG,CAAA;CAAE,EAC9D,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IACnB,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,eAAe,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,EACzD,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IACnB,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,UAAU,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CACxB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,EACzD,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EACnB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IACnB,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC,UAAU,CAAC,EAAE;IAAE,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAC/G,CAAC,GACD,OAAO,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import * as v from 'valibot';
2
+ import type { ApiInput, ApiResponse } from './client-types.js';
3
+ declare const HTTPMethods: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
4
+ type HTTPMethod = (typeof HTTPMethods)[number];
5
+ export type ApiRequestFunction<API extends {
6
+ path: string;
7
+ method: string;
8
+ requestInput: any;
9
+ requestOutput: any;
10
+ response: any;
11
+ }> = <Path extends API['path'], Method extends Extract<API, {
12
+ path: Path;
13
+ }>['method']>(path: Path, method: Method, input: ApiInput<API, Path, Method>, headers?: HeadersInit) => Promise<ApiResponse<API, Path, Method>>;
14
+ /**
15
+ * @returns A function that can be used to make API requests with type safety
16
+ * @example const clientApiRequest = createApiRequest<ApiEndpoints>();
17
+ */
18
+ export declare function createApiRequest<API extends {
19
+ path: string;
20
+ method: string;
21
+ requestInput: any;
22
+ requestOutput: any;
23
+ response: any;
24
+ }>(schemas: Partial<Record<API['path'], Partial<Record<HTTPMethod, v.GenericSchema | (() => v.GenericSchema)>>>>, apiUrl: string, env: string): ApiRequestFunction<API>;
25
+ export {};
26
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lambda/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAuD/D,QAAA,MAAM,WAAW,uEAAwE,CAAC;AAC1F,KAAK,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,MAAM,MAAM,kBAAkB,CAC5B,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC;IAAC,aAAa,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,IAChG,CAAC,IAAI,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,SAAS,OAAO,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,EAClF,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,WAAW,KAClB,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC;IAAC,aAAa,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,EAElG,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,aAAa,GAAG,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,EAC7G,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,kBAAkB,CAAC,GAAG,CAAC,CAkBzB"}
@@ -0,0 +1,53 @@
1
+ import { deserialize } from './deserializer.js';
2
+ import * as v from 'valibot';
3
+ const bodyMethods = ['POST', 'PUT', 'PATCH'];
4
+ const queryMethods = ['GET', 'DELETE'];
5
+ async function _apiRequest(path, method, input, schema, environment, apiUrl, headers) {
6
+ if (schema) {
7
+ v.parse(schema, input);
8
+ }
9
+ let url = `${apiUrl}${path}`;
10
+ if (queryMethods.includes(method)) {
11
+ const params = input ?? {};
12
+ const queryString = new URLSearchParams(Object.entries(params).reduce((acc, [key, value]) => {
13
+ if (Array.isArray(value)) {
14
+ value.forEach((v) => (acc[key] = String(v)));
15
+ }
16
+ else {
17
+ acc[key] = String(value);
18
+ }
19
+ return acc;
20
+ }, {})).toString();
21
+ if (queryString)
22
+ url += `?${queryString}`;
23
+ }
24
+ headers = {
25
+ 'Content-Type': 'application/json',
26
+ ...(headers || {})
27
+ };
28
+ const response = await fetch(url, {
29
+ method,
30
+ headers,
31
+ body: bodyMethods.includes(method) ? JSON.stringify(input) : undefined,
32
+ credentials: 'include'
33
+ });
34
+ response.json = async () => {
35
+ return await deserialize(await response.text(), environment);
36
+ };
37
+ return response;
38
+ }
39
+ const HTTPMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
40
+ /**
41
+ * @returns A function that can be used to make API requests with type safety
42
+ * @example const clientApiRequest = createApiRequest<ApiEndpoints>();
43
+ */
44
+ export function createApiRequest(schemas, apiUrl, env) {
45
+ return async function apiRequest(path, method, input, headers) {
46
+ // @ts-expect-error method can be used to index schemas
47
+ let schema = schemas[path]?.[method];
48
+ if (typeof schema === 'function') {
49
+ schema = schema();
50
+ }
51
+ return _apiRequest(path, method, input, schema, env, apiUrl, headers);
52
+ };
53
+ }
@@ -0,0 +1,3 @@
1
+ declare function deserialize(data: string, env: string | 'production'): Promise<any>;
2
+ export { deserialize };
3
+ //# sourceMappingURL=deserializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deserializer.d.ts","sourceRoot":"","sources":["../../src/lambda/deserializer.ts"],"names":[],"mappings":"AAAA,iBAAe,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,CAQjF;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ async function deserialize(data, env) {
2
+ if (env === 'production') {
3
+ const { parse: prodParse } = await import('@ungap/structured-clone/json');
4
+ return prodParse(data);
5
+ }
6
+ else {
7
+ const cycle = await import('cycle');
8
+ return JSON.parse(cycle.default.retrocycle(data));
9
+ }
10
+ }
11
+ export { deserialize };
@@ -0,0 +1,31 @@
1
+ import type { APIGatewayProxyResultV2, Context } from 'aws-lambda';
2
+ /**
3
+ * A type for the raw proxy result - just using an object not a stirng for the body
4
+ */
5
+ export type RawProxyResultV2 = {
6
+ statusCode?: number | undefined;
7
+ headers?: {
8
+ [header: string]: boolean | number | string;
9
+ } | undefined;
10
+ body?: object | undefined;
11
+ isBase64Encoded?: boolean | undefined;
12
+ cookies?: string[] | undefined;
13
+ };
14
+ export type APIGatewayHandler<E> = (event: E, context: Context) => Promise<APIGatewayProxyResultV2>;
15
+ export type RawApiGatewayHandler<E> = (event: E, context: Context) => Promise<RawProxyResultV2>;
16
+ /**
17
+ * Wraps a handler that returns the body as an object rather than a string.
18
+ *
19
+ * This means you can achieve proper type inference on your handler and have standardised serialisation
20
+ *
21
+ * @example
22
+ ```ts
23
+ export type AuthorizerContext = {
24
+ details: JWTPayload;
25
+ } | null;
26
+
27
+ export const wrapHandler = baseWrapHandler<APIGatewayProxyEventV2WithLambdaAuthorizer<AuthorizerContext>>
28
+
29
+ */
30
+ export declare function wrapHandler<E>(handler: RawApiGatewayHandler<E>): APIGatewayHandler<E>;
31
+ //# sourceMappingURL=handlerUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlerUtils.d.ts","sourceRoot":"","sources":["../../src/lambda/handlerUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGnE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,OAAO,CAAC,EACJ;QACE,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;KAC7C,GACD,SAAS,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChC,CAAC;AAGF,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAGpG,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;AAEhG;;;;;;;;;;;;;EAaE;AACF,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAQrF"}
@@ -0,0 +1,24 @@
1
+ import { stringify } from './serializer.js';
2
+ /**
3
+ * Wraps a handler that returns the body as an object rather than a string.
4
+ *
5
+ * This means you can achieve proper type inference on your handler and have standardised serialisation
6
+ *
7
+ * @example
8
+ ```ts
9
+ export type AuthorizerContext = {
10
+ details: JWTPayload;
11
+ } | null;
12
+
13
+ export const wrapHandler = baseWrapHandler<APIGatewayProxyEventV2WithLambdaAuthorizer<AuthorizerContext>>
14
+
15
+ */
16
+ export function wrapHandler(handler) {
17
+ return async (event, context) => {
18
+ const result = await handler(event, context);
19
+ return {
20
+ ...result,
21
+ body: result.body ? await stringify(result.body) : undefined
22
+ };
23
+ };
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './browser.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lambda/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './browser.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Turns object into string.
3
+ *
4
+ * Uses different methods in production and otherwise for more readable output
5
+ */
6
+ export declare function stringify(data: any): Promise<any>;
7
+ //# sourceMappingURL=serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../src/lambda/serializer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,GAAG,gBAQxC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Turns object into string.
3
+ *
4
+ * Uses different methods in production and otherwise for more readable output
5
+ */
6
+ export async function stringify(data) {
7
+ if (process.env.PUBLIC_ENVIRONMENT === 'production') {
8
+ const { stringify: prodStrigify } = await import('@ungap/structured-clone/json');
9
+ return prodStrigify(data);
10
+ }
11
+ else {
12
+ const { decycle } = (await import('cycle')).default;
13
+ return JSON.stringify(decycle(data), null);
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=ts-alias.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ts-alias.d.ts","sourceRoot":"","sources":["../src/ts-alias.ts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as chokidar from 'chokidar';
5
+ import { replaceTscAliasPaths } from 'tsc-alias';
6
+ import { glob } from 'glob';
7
+ import console from 'console';
8
+ // Cache for tsconfig.json files
9
+ const tsconfigCache = new Map();
10
+ // Parse command-line arguments
11
+ const args = process.argv.slice(2);
12
+ const watchMode = args.includes('-w') || args.includes('--watch');
13
+ /**
14
+ * Find all dist folders in the project directory, excluding certain patterns.
15
+ */
16
+ async function findDistFolders(baseDir) {
17
+ const ignorePatterns = [
18
+ '**/node_modules/**',
19
+ '**/\\.git/**',
20
+ '**/\\.vscode/**',
21
+ '**/\\.idea/**',
22
+ '**/coverage/**',
23
+ '**/build/**',
24
+ '**/cdk.out/**'
25
+ ];
26
+ const distFolders = await glob('**/dist', {
27
+ cwd: baseDir,
28
+ ignore: ignorePatterns,
29
+ absolute: true
30
+ });
31
+ return distFolders;
32
+ }
33
+ /**
34
+ * Get the tsconfig.json file for a given dist folder.
35
+ * This function caches the tsconfig file to avoid reading it multiple times.
36
+ */
37
+ function getTsconfig(distFolder) {
38
+ const projectRoot = path.dirname(distFolder);
39
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
40
+ try {
41
+ const stats = fs.statSync(tsconfigPath);
42
+ const mtime = stats.mtimeMs;
43
+ // Check cache
44
+ const cached = tsconfigCache.get(tsconfigPath);
45
+ if (cached && cached.mtime === mtime) {
46
+ return cached.config;
47
+ }
48
+ // Read and cache the config
49
+ const config = JSON.parse(fs.readFileSync(tsconfigPath, 'utf8'));
50
+ tsconfigCache.set(tsconfigPath, { config, mtime });
51
+ return config;
52
+ }
53
+ catch (error) {
54
+ console.error(`Error reading tsconfig at ${tsconfigPath}:`, error);
55
+ return null;
56
+ }
57
+ }
58
+ /**
59
+ * Process the dist folder by replacing TypeScript alias paths with relative paths.
60
+ */
61
+ async function processDistFolder(distFolder) {
62
+ const projectRoot = path.dirname(distFolder);
63
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
64
+ if (!fs.existsSync(tsconfigPath)) {
65
+ console.warn(`No tsconfig.json found at ${tsconfigPath}`);
66
+ return;
67
+ }
68
+ const tsconfig = getTsconfig(distFolder);
69
+ if (!tsconfig) {
70
+ console.warn(`Invalid tsconfig.json found for ${distFolder}`);
71
+ return;
72
+ }
73
+ try {
74
+ await replaceTscAliasPaths({
75
+ configFile: tsconfigPath,
76
+ outDir: distFolder
77
+ });
78
+ console.log(`Successfully processed aliases in ${distFolder}`);
79
+ }
80
+ catch (error) {
81
+ console.error(`Error processing aliases in ${distFolder}:`, error);
82
+ }
83
+ }
84
+ /**
85
+ * Watch the dist folder for changes and process it when files are added or changed.
86
+ */
87
+ function watchDistFolder(distFolder) {
88
+ console.log(`Setting up watcher for: ${distFolder}`);
89
+ const watcher = chokidar.watch(distFolder, {
90
+ persistent: true,
91
+ ignoreInitial: true,
92
+ awaitWriteFinish: {
93
+ stabilityThreshold: 300,
94
+ pollInterval: 100
95
+ }
96
+ });
97
+ watcher.on('add', (filePath) => {
98
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
99
+ console.log(`File added: ${filePath}`);
100
+ processDistFolder(distFolder);
101
+ }
102
+ });
103
+ watcher.on('change', (filePath) => {
104
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
105
+ console.log(`File changed: ${filePath}`);
106
+ processDistFolder(distFolder);
107
+ }
108
+ });
109
+ return watcher;
110
+ }
111
+ // Main function
112
+ async function main() {
113
+ const baseDir = process.cwd();
114
+ console.log(`Searching for dist folders in: ${baseDir}`);
115
+ const distFolders = await findDistFolders(baseDir);
116
+ // Process all folders initially if any exist
117
+ if (distFolders.length > 0) {
118
+ console.log(`Found ${distFolders.length} dist folders:`);
119
+ distFolders.forEach((folder) => console.log(` - ${folder}`));
120
+ for (const folder of distFolders) {
121
+ await processDistFolder(folder);
122
+ }
123
+ }
124
+ else {
125
+ console.log('No dist folders found initially');
126
+ }
127
+ // Set up watchers if in watch mode
128
+ if (watchMode) {
129
+ console.log('Watch mode enabled, monitoring for changes...');
130
+ // Set up watchers for existing dist folders
131
+ distFolders.forEach(watchDistFolder);
132
+ // Watch for new dist folders being created
133
+ console.log('Watching for new dist folders...');
134
+ const dirWatcher = chokidar.watch(baseDir, {
135
+ persistent: true,
136
+ ignoreInitial: true,
137
+ depth: 5, // Adjust depth as needed for your project structure
138
+ ignored: [
139
+ '**/node_modules/**',
140
+ '**/\\.git/**',
141
+ '**/\\.vscode/**',
142
+ '**/\\.idea/**',
143
+ '**/coverage/**',
144
+ '**/build/**',
145
+ '**/cdk.out/**',
146
+ // Don't watch the contents of existing dist folders (they'll be watched separately)
147
+ ...distFolders.map((folder) => `${folder}/**`)
148
+ ]
149
+ });
150
+ // Handle directory creation events
151
+ dirWatcher.on('addDir', async (dirPath) => {
152
+ if (path.basename(dirPath) === 'dist') {
153
+ // Make sure it's not already being watched
154
+ if (!distFolders.includes(dirPath)) {
155
+ console.log(`New dist folder detected: ${dirPath}`);
156
+ distFolders.push(dirPath);
157
+ await processDistFolder(dirPath);
158
+ watchDistFolder(dirPath);
159
+ }
160
+ }
161
+ });
162
+ }
163
+ }
164
+ main().catch((error) => {
165
+ console.error('Error in ts-alias script:', error);
166
+ process.exit(1);
167
+ });
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "ts-ag",
3
+ "description": "Useful TS stuff",
4
+ "version": "0.0.1-dev.1",
5
+ "author": "Alexander Hornung",
6
+ "bugs": "https://github.com/ageorgeh/ts-ag/issues",
7
+ "dependencies": {
8
+ "dequal": "^2.0.3",
9
+ "glob": "11.0.2",
10
+ "chokidar": "4.0.3",
11
+ "tsc-alias": "1.8.16",
12
+ "valibot": "1.1.0",
13
+ "cycle": "1.0.3",
14
+ "@ungap/structured-clone": "1.3.0",
15
+ "jose": "6.0.11"
16
+ },
17
+ "peerDependencies": {},
18
+ "devDependencies": {
19
+ "jiti": "2.4.2",
20
+ "@eslint/js": "^9.26.0",
21
+ "eslint": "^9.26.0",
22
+ "eslint-plugin-import": "^2.31.0",
23
+ "globals": "^16.1.0",
24
+ "prettier": "^3.5.3",
25
+ "typescript": "^5.8.3",
26
+ "typescript-eslint": "^8.32.0",
27
+ "typescript-svelte-plugin": "0.3.47",
28
+ "npm-check-updates": "^18.0.1",
29
+ "@types/node": "^22.15.17",
30
+ "@types/aws-lambda": "8.10.149",
31
+ "@types/ungap__structured-clone": "1.2.0"
32
+ },
33
+ "type": "module",
34
+ "files": [
35
+ "./src",
36
+ "./dist"
37
+ ],
38
+ "exports": {
39
+ "./package.json": "./package.json",
40
+ ".": {
41
+ "browser": {
42
+ "types": "./dist/browser.d.ts",
43
+ "default": "./dist/browser.js"
44
+ },
45
+ "default": {
46
+ "types": "./dist/index.d.ts",
47
+ "default": "./dist/index.js"
48
+ }
49
+ }
50
+ },
51
+ "repository": "ageorgeh/ts-ag",
52
+ "scripts": {
53
+ "tsc:build": "tsc",
54
+ "tsc:watch": "tsc --watch",
55
+ "publish:local": "pnpm version prerelease --preid dev --no-git-tag-version && pnpm publish --registry http://localhost:4873 --tag dev --access public --no-git-checks --json > ./publishLocal.json",
56
+ "publish:prerelease": "pnpm publish --tag dev --access public --no-git-checks --registry=https://registry.npmjs.org/ --json > ./publish.json",
57
+ "version:prerelease": "pnpm version prerelease --preid dev --no-git-tag-version"
58
+ }
59
+ }
package/src/browser.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lambda/browser.js';
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lambda/index.js';
@@ -0,0 +1,3 @@
1
+ export * from './client-types.js';
2
+ export * from './client.js';
3
+ export * from './handlerUtils.js';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Converts a RawApiGatewayHandler response type to a fetch like response type.
3
+ */
4
+ type ConvertToFetch<T> = T extends { statusCode: number; body: object; headers: object }
5
+ ? {
6
+ ok: T['statusCode'] extends 200 | 201 | 204 ? true : false;
7
+ json: () => Promise<T['body']>;
8
+ status: T['statusCode'];
9
+ }
10
+ : T;
11
+
12
+ export type CleanResponse = Omit<Response, 'status' | 'ok' | 'json'>;
13
+ export type FetchResponse<T extends (...args: any) => any> = ConvertToFetch<Awaited<ReturnType<T>>> & CleanResponse;
14
+
15
+ /**
16
+ * Extracts the requestInput type from an API endpoint definition
17
+ * @template E - Union type of all API endpoints
18
+ * @template P - Path string literal type (e.g. 'payments/account')
19
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
20
+ */
21
+ export type ApiInput<
22
+ E extends { path: string; method: string; requestInput: any },
23
+ P extends E['path'],
24
+ M extends E['method']
25
+ > = Extract<E, { path: P; method: M }>['requestInput'];
26
+
27
+ /**
28
+ * Extracts the requestOutput type from an API endpoint definition
29
+ * @template E - Union type of all API endpoints
30
+ * @template P - Path string literal type (e.g. 'payments/account')
31
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
32
+ */
33
+ export type ApiOutput<
34
+ E extends { path: string; method: string; requestOutput: any },
35
+ P extends E['path'],
36
+ M extends E['method']
37
+ > = Extract<E, { path: P; method: M }>['requestOutput'];
38
+
39
+ /**
40
+ * Extracts the response type from an API endpoint definition
41
+ * @template E - Union type of all API endpoints
42
+ * @template P - Path string literal type (e.g. 'payments/account')
43
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
44
+ */
45
+ export type ApiResponse<
46
+ E extends { path: string; method: string; response: any },
47
+ P extends E['path'],
48
+ M extends E['method']
49
+ > = Extract<E, { path: P; method: M }>['response'];
50
+
51
+ /**
52
+ * Extracts the sucessful body type from an API endpoint definition
53
+ * @template E - Union type of all API endpoints
54
+ * @template P - Path string literal type (e.g. 'payments/account')
55
+ * @template M - HTTP method string literal type (e.g. 'GET', 'POST')
56
+ */
57
+ export type ApiSuccessBody<
58
+ E extends { path: string; method: string; response: any },
59
+ P extends E['path'],
60
+ M extends E['method']
61
+ > = Extract<Extract<E, { path: P; method: M }>['response'], { status: 200 }>['json'] extends () => Promise<infer R>
62
+ ? R
63
+ : unknown;
@@ -0,0 +1,98 @@
1
+ import { deserialize } from './deserializer.js';
2
+ import * as v from 'valibot';
3
+ import type { ApiInput, ApiResponse } from './client-types.js';
4
+
5
+ const bodyMethods = ['POST', 'PUT', 'PATCH'] as const;
6
+ const queryMethods = ['GET', 'DELETE'] as const;
7
+
8
+ async function _apiRequest<T = Response>(
9
+ path: string,
10
+ method: 'GET' | 'POST' | 'DELETE',
11
+ input: object,
12
+ schema: v.GenericSchema | undefined,
13
+ environment: string | 'production',
14
+ apiUrl: string,
15
+ headers?: HeadersInit
16
+ ): Promise<T> {
17
+ if (schema) {
18
+ v.parse(schema, input);
19
+ }
20
+
21
+ let url = `${apiUrl}${path}`;
22
+
23
+ if (queryMethods.includes(method as any)) {
24
+ const params = input ?? {};
25
+ const queryString = new URLSearchParams(
26
+ Object.entries(params).reduce(
27
+ (acc, [key, value]) => {
28
+ if (Array.isArray(value)) {
29
+ value.forEach((v) => (acc[key] = String(v)));
30
+ } else {
31
+ acc[key] = String(value);
32
+ }
33
+ return acc;
34
+ },
35
+ {} as Record<string, string>
36
+ )
37
+ ).toString();
38
+ if (queryString) url += `?${queryString}`;
39
+ }
40
+
41
+ headers = {
42
+ 'Content-Type': 'application/json',
43
+ ...(headers || {})
44
+ };
45
+ const response = await fetch(url, {
46
+ method,
47
+ headers,
48
+ body: bodyMethods.includes(method as any) ? JSON.stringify(input) : undefined,
49
+ credentials: 'include'
50
+ });
51
+
52
+ response.json = async () => {
53
+ return await deserialize(await response.text(), environment);
54
+ };
55
+ return response as unknown as T;
56
+ }
57
+
58
+ const HTTPMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'] as const;
59
+ type HTTPMethod = (typeof HTTPMethods)[number];
60
+
61
+ export type ApiRequestFunction<
62
+ API extends { path: string; method: string; requestInput: any; requestOutput: any; response: any }
63
+ > = <Path extends API['path'], Method extends Extract<API, { path: Path }>['method']>(
64
+ path: Path,
65
+ method: Method,
66
+ input: ApiInput<API, Path, Method>,
67
+ headers?: HeadersInit
68
+ ) => Promise<ApiResponse<API, Path, Method>>;
69
+
70
+ /**
71
+ * @returns A function that can be used to make API requests with type safety
72
+ * @example const clientApiRequest = createApiRequest<ApiEndpoints>();
73
+ */
74
+ export function createApiRequest<
75
+ API extends { path: string; method: string; requestInput: any; requestOutput: any; response: any }
76
+ >(
77
+ schemas: Partial<Record<API['path'], Partial<Record<HTTPMethod, v.GenericSchema | (() => v.GenericSchema)>>>>,
78
+ apiUrl: string,
79
+ env: string
80
+ ): ApiRequestFunction<API> {
81
+ return async function apiRequest(path, method, input, headers) {
82
+ // @ts-expect-error method can be used to index schemas
83
+ let schema = schemas[path]?.[method];
84
+ if (typeof schema === 'function') {
85
+ schema = schema();
86
+ }
87
+
88
+ return _apiRequest<ApiResponse<API, typeof path, typeof method>>(
89
+ path as string,
90
+ method as 'GET' | 'POST',
91
+ input,
92
+ schema,
93
+ env,
94
+ apiUrl,
95
+ headers
96
+ );
97
+ };
98
+ }
@@ -0,0 +1,11 @@
1
+ async function deserialize(data: string, env: string | 'production'): Promise<any> {
2
+ if (env === 'production') {
3
+ const { parse: prodParse } = await import('@ungap/structured-clone/json');
4
+ return prodParse(data);
5
+ } else {
6
+ const cycle = await import('cycle');
7
+ return JSON.parse(cycle.default.retrocycle(data));
8
+ }
9
+ }
10
+
11
+ export { deserialize };
@@ -0,0 +1,47 @@
1
+ import type { APIGatewayProxyResultV2, Context } from 'aws-lambda';
2
+ import { stringify } from './serializer.js';
3
+
4
+ /**
5
+ * A type for the raw proxy result - just using an object not a stirng for the body
6
+ */
7
+ export type RawProxyResultV2 = {
8
+ statusCode?: number | undefined;
9
+ headers?:
10
+ | {
11
+ [header: string]: boolean | number | string;
12
+ }
13
+ | undefined;
14
+ body?: object | undefined;
15
+ isBase64Encoded?: boolean | undefined;
16
+ cookies?: string[] | undefined;
17
+ };
18
+
19
+ // The type of the handler returned from wrapHandler
20
+ export type APIGatewayHandler<E> = (event: E, context: Context) => Promise<APIGatewayProxyResultV2>;
21
+
22
+ // The type of the handler passed into wrapHandler
23
+ export type RawApiGatewayHandler<E> = (event: E, context: Context) => Promise<RawProxyResultV2>;
24
+
25
+ /**
26
+ * Wraps a handler that returns the body as an object rather than a string.
27
+ *
28
+ * This means you can achieve proper type inference on your handler and have standardised serialisation
29
+ *
30
+ * @example
31
+ ```ts
32
+ export type AuthorizerContext = {
33
+ details: JWTPayload;
34
+ } | null;
35
+
36
+ export const wrapHandler = baseWrapHandler<APIGatewayProxyEventV2WithLambdaAuthorizer<AuthorizerContext>>
37
+
38
+ */
39
+ export function wrapHandler<E>(handler: RawApiGatewayHandler<E>): APIGatewayHandler<E> {
40
+ return async (event: E, context: Context): Promise<APIGatewayProxyResultV2> => {
41
+ const result = await handler(event, context);
42
+ return {
43
+ ...result,
44
+ body: result.body ? await stringify(result.body) : undefined
45
+ };
46
+ };
47
+ }
@@ -0,0 +1 @@
1
+ export * from './browser.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Turns object into string.
3
+ *
4
+ * Uses different methods in production and otherwise for more readable output
5
+ */
6
+ export async function stringify(data: any) {
7
+ if (process.env.PUBLIC_ENVIRONMENT === 'production') {
8
+ const { stringify: prodStrigify } = await import('@ungap/structured-clone/json');
9
+ return prodStrigify(data);
10
+ } else {
11
+ const { decycle } = (await import('cycle')).default;
12
+ return JSON.stringify(decycle(data), null);
13
+ }
14
+ }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import * as chokidar from 'chokidar';
6
+ import { replaceTscAliasPaths } from 'tsc-alias';
7
+ import { glob } from 'glob';
8
+ import console from 'console';
9
+
10
+ // Cache for tsconfig.json files
11
+ const tsconfigCache: Map<string, { config: any; mtime: number }> = new Map();
12
+
13
+ // Parse command-line arguments
14
+ const args = process.argv.slice(2);
15
+ const watchMode = args.includes('-w') || args.includes('--watch');
16
+
17
+ /**
18
+ * Find all dist folders in the project directory, excluding certain patterns.
19
+ */
20
+ async function findDistFolders(baseDir: string): Promise<string[]> {
21
+ const ignorePatterns = [
22
+ '**/node_modules/**',
23
+ '**/\\.git/**',
24
+ '**/\\.vscode/**',
25
+ '**/\\.idea/**',
26
+ '**/coverage/**',
27
+ '**/build/**',
28
+ '**/cdk.out/**'
29
+ ];
30
+
31
+ const distFolders = await glob('**/dist', {
32
+ cwd: baseDir,
33
+ ignore: ignorePatterns,
34
+ absolute: true
35
+ });
36
+
37
+ return distFolders;
38
+ }
39
+
40
+ /**
41
+ * Get the tsconfig.json file for a given dist folder.
42
+ * This function caches the tsconfig file to avoid reading it multiple times.
43
+ */
44
+ function getTsconfig(distFolder: string): any {
45
+ const projectRoot = path.dirname(distFolder);
46
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
47
+
48
+ try {
49
+ const stats = fs.statSync(tsconfigPath);
50
+ const mtime = stats.mtimeMs;
51
+
52
+ // Check cache
53
+ const cached = tsconfigCache.get(tsconfigPath);
54
+ if (cached && cached.mtime === mtime) {
55
+ return cached.config;
56
+ }
57
+
58
+ // Read and cache the config
59
+ const config = JSON.parse(fs.readFileSync(tsconfigPath, 'utf8'));
60
+ tsconfigCache.set(tsconfigPath, { config, mtime });
61
+
62
+ return config;
63
+ } catch (error) {
64
+ console.error(`Error reading tsconfig at ${tsconfigPath}:`, error);
65
+ return null;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Process the dist folder by replacing TypeScript alias paths with relative paths.
71
+ */
72
+ async function processDistFolder(distFolder: string): Promise<void> {
73
+ const projectRoot = path.dirname(distFolder);
74
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
75
+
76
+ if (!fs.existsSync(tsconfigPath)) {
77
+ console.warn(`No tsconfig.json found at ${tsconfigPath}`);
78
+ return;
79
+ }
80
+
81
+ const tsconfig = getTsconfig(distFolder);
82
+
83
+ if (!tsconfig) {
84
+ console.warn(`Invalid tsconfig.json found for ${distFolder}`);
85
+ return;
86
+ }
87
+
88
+ try {
89
+ await replaceTscAliasPaths({
90
+ configFile: tsconfigPath,
91
+ outDir: distFolder
92
+ });
93
+ console.log(`Successfully processed aliases in ${distFolder}`);
94
+ } catch (error) {
95
+ console.error(`Error processing aliases in ${distFolder}:`, error);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Watch the dist folder for changes and process it when files are added or changed.
101
+ */
102
+ function watchDistFolder(distFolder: string): chokidar.FSWatcher {
103
+ console.log(`Setting up watcher for: ${distFolder}`);
104
+
105
+ const watcher = chokidar.watch(distFolder, {
106
+ persistent: true,
107
+ ignoreInitial: true,
108
+ awaitWriteFinish: {
109
+ stabilityThreshold: 300,
110
+ pollInterval: 100
111
+ }
112
+ });
113
+
114
+ watcher.on('add', (filePath) => {
115
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
116
+ console.log(`File added: ${filePath}`);
117
+ processDistFolder(distFolder);
118
+ }
119
+ });
120
+
121
+ watcher.on('change', (filePath) => {
122
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
123
+ console.log(`File changed: ${filePath}`);
124
+ processDistFolder(distFolder);
125
+ }
126
+ });
127
+
128
+ return watcher;
129
+ }
130
+
131
+ // Main function
132
+ async function main(): Promise<void> {
133
+ const baseDir = process.cwd();
134
+ console.log(`Searching for dist folders in: ${baseDir}`);
135
+
136
+ const distFolders = await findDistFolders(baseDir);
137
+
138
+ // Process all folders initially if any exist
139
+ if (distFolders.length > 0) {
140
+ console.log(`Found ${distFolders.length} dist folders:`);
141
+ distFolders.forEach((folder) => console.log(` - ${folder}`));
142
+
143
+ for (const folder of distFolders) {
144
+ await processDistFolder(folder);
145
+ }
146
+ } else {
147
+ console.log('No dist folders found initially');
148
+ }
149
+
150
+ // Set up watchers if in watch mode
151
+ if (watchMode) {
152
+ console.log('Watch mode enabled, monitoring for changes...');
153
+
154
+ // Set up watchers for existing dist folders
155
+ distFolders.forEach(watchDistFolder);
156
+
157
+ // Watch for new dist folders being created
158
+ console.log('Watching for new dist folders...');
159
+ const dirWatcher = chokidar.watch(baseDir, {
160
+ persistent: true,
161
+ ignoreInitial: true,
162
+ depth: 5, // Adjust depth as needed for your project structure
163
+ ignored: [
164
+ '**/node_modules/**',
165
+ '**/\\.git/**',
166
+ '**/\\.vscode/**',
167
+ '**/\\.idea/**',
168
+ '**/coverage/**',
169
+ '**/build/**',
170
+ '**/cdk.out/**',
171
+ // Don't watch the contents of existing dist folders (they'll be watched separately)
172
+ ...distFolders.map((folder) => `${folder}/**`)
173
+ ]
174
+ });
175
+
176
+ // Handle directory creation events
177
+ dirWatcher.on('addDir', async (dirPath) => {
178
+ if (path.basename(dirPath) === 'dist') {
179
+ // Make sure it's not already being watched
180
+ if (!distFolders.includes(dirPath)) {
181
+ console.log(`New dist folder detected: ${dirPath}`);
182
+ distFolders.push(dirPath);
183
+ await processDistFolder(dirPath);
184
+ watchDistFolder(dirPath);
185
+ }
186
+ }
187
+ });
188
+ }
189
+ }
190
+
191
+ main().catch((error) => {
192
+ console.error('Error in ts-alias script:', error);
193
+ process.exit(1);
194
+ });