@withstudiocms/sdk 0.0.0-beta.0 → 0.1.0-beta.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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/dist/cache.d.ts +109 -0
  4. package/dist/cache.js +94 -0
  5. package/dist/consts.d.ts +28 -0
  6. package/dist/consts.js +26 -0
  7. package/dist/context.d.ts +188 -0
  8. package/dist/context.js +33 -0
  9. package/dist/index.d.ts +1136 -0
  10. package/dist/index.js +24 -0
  11. package/dist/lib/diff.d.ts +39 -0
  12. package/dist/lib/diff.js +29 -0
  13. package/dist/lib/logger.d.ts +31 -0
  14. package/dist/lib/logger.js +131 -0
  15. package/dist/lib/pluginUtils.d.ts +221 -0
  16. package/dist/lib/pluginUtils.js +80 -0
  17. package/dist/modules/auth/index.d.ts +463 -0
  18. package/dist/modules/auth/index.js +412 -0
  19. package/dist/modules/clear/index.d.ts +72 -0
  20. package/dist/modules/clear/index.js +52 -0
  21. package/dist/modules/config/consts.d.ts +32 -0
  22. package/dist/modules/config/consts.js +18 -0
  23. package/dist/modules/config/index.d.ts +100 -0
  24. package/dist/modules/config/index.js +205 -0
  25. package/dist/modules/config/templates/mailer.d.ts +36 -0
  26. package/dist/modules/config/templates/mailer.js +218 -0
  27. package/dist/modules/config/type-utils.d.ts +13 -0
  28. package/dist/modules/config/type-utils.js +11 -0
  29. package/dist/modules/delete/index.d.ts +140 -0
  30. package/dist/modules/delete/index.js +274 -0
  31. package/dist/modules/diffTracking/index.d.ts +188 -0
  32. package/dist/modules/diffTracking/index.js +276 -0
  33. package/dist/modules/get/index.d.ts +272 -0
  34. package/dist/modules/get/index.js +466 -0
  35. package/dist/modules/index.d.ts +1003 -0
  36. package/dist/modules/index.js +37 -0
  37. package/dist/modules/init/index.d.ts +60 -0
  38. package/dist/modules/init/index.js +38 -0
  39. package/dist/modules/middleware/index.d.ts +56 -0
  40. package/dist/modules/middleware/index.js +50 -0
  41. package/dist/modules/notificationSettings/index.d.ts +57 -0
  42. package/dist/modules/notificationSettings/index.js +39 -0
  43. package/dist/modules/plugins/index.d.ts +166 -0
  44. package/dist/modules/plugins/index.js +261 -0
  45. package/dist/modules/post/index.d.ts +305 -0
  46. package/dist/modules/post/index.js +305 -0
  47. package/dist/modules/resetTokenBucket/index.d.ts +91 -0
  48. package/dist/modules/resetTokenBucket/index.js +93 -0
  49. package/dist/modules/rest_api/index.d.ts +92 -0
  50. package/dist/modules/rest_api/index.js +113 -0
  51. package/dist/modules/update/index.d.ts +184 -0
  52. package/dist/modules/update/index.js +174 -0
  53. package/dist/modules/util/collectors.d.ts +261 -0
  54. package/dist/modules/util/collectors.js +141 -0
  55. package/dist/modules/util/folderTree.d.ts +100 -0
  56. package/dist/modules/util/folderTree.js +176 -0
  57. package/dist/modules/util/generators.d.ts +83 -0
  58. package/dist/modules/util/generators.js +106 -0
  59. package/dist/modules/util/getFromNPM.d.ts +191 -0
  60. package/dist/modules/util/getFromNPM.js +100 -0
  61. package/dist/modules/util/index.d.ts +236 -0
  62. package/dist/modules/util/index.js +20 -0
  63. package/dist/modules/util/parsers.d.ts +60 -0
  64. package/dist/modules/util/parsers.js +43 -0
  65. package/dist/modules/util/slugify.d.ts +22 -0
  66. package/dist/modules/util/slugify.js +19 -0
  67. package/dist/modules/util/users.d.ts +99 -0
  68. package/dist/modules/util/users.js +78 -0
  69. package/dist/types.d.ts +360 -0
  70. package/dist/types.js +10 -0
  71. package/package.json +55 -7
@@ -0,0 +1,83 @@
1
+ import { Effect } from '@withstudiocms/effect';
2
+ import type { JwtVerificationResult } from '../../types.js';
3
+ declare const GeneratorError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
4
+ readonly _tag: "GeneratorError";
5
+ } & Readonly<A>;
6
+ /**
7
+ * Represents errors that occur during generator operations.
8
+ */
9
+ export declare class GeneratorError extends GeneratorError_base<{
10
+ cause: unknown;
11
+ }> {
12
+ }
13
+ /**
14
+ * A helper function to wrap generator operations in an Effect with error handling.
15
+ *
16
+ * @param _try - A function that performs the generator operation.
17
+ * @returns An Effect that either yields the result of the operation or fails with a GeneratorError.
18
+ */
19
+ export declare const useGeneratorError: <A>(_try: () => A) => Effect.Effect<A, GeneratorError, never>;
20
+ /**
21
+ * SDKGenerators
22
+ *
23
+ * Effect generator that builds a collection of utility functions used across the SDK.
24
+ *
25
+ * This generator:
26
+ * - Reads the CMS encryption key from configuration (CMS_ENCRYPTION_KEY). The key's value is kept redacted for
27
+ * safety in logs and debug output; the generator uses the redacted value for cryptographic operations where needed.
28
+ * - Exposes utilities for creating random IDs, secure passwords, issuing JWTs for users, and verifying JWTs.
29
+ *
30
+ * Exposed utilities:
31
+ * - generateRandomIDNumber(length: number): number
32
+ * - Generates a numeric identifier of the specified length.
33
+ * - Uses Math.random() and therefore is suitable for non-cryptographic identifiers.
34
+ *
35
+ * - generateRandomPassword(length: number): string
36
+ * - Produces a password of the requested length using the character set
37
+ * "A-Z a-z 0-9".
38
+ * - Uses crypto.getRandomValues and rejection sampling to avoid modulo bias, providing cryptographically
39
+ * strong random selection from the character set.
40
+ *
41
+ * - generateToken(userId: string, noExpire?: boolean): string
42
+ * - Creates a signed JSON Web Token for the supplied userId.
43
+ * - By default the token is time-limited (expires in a short timeframe — implementation uses a 3 hour default).
44
+ * Passing `noExpire = true` will produce a token without the automatic expiry.
45
+ * - Token signing uses the CMS encryption key (redacted) via the SDK's internal JWT generation routine.
46
+ *
47
+ * - testToken(token: string): Effect.Effect<JwtVerificationResult, GeneratorError, never>
48
+ * - Validates the provided JWT using the CMS encryption key.
49
+ * - Verification performs:
50
+ * - header algorithm check (expects HS256),
51
+ * - expiration check (tokens with exp in the past are rejected),
52
+ * - signature verification (HMAC SHA-256 using the configured secret).
53
+ * - Returns a JwtVerificationResult indicating whether the token is valid and, if so, the userId extracted from the token.
54
+ *
55
+ * Error handling and effects:
56
+ * - Many operations are wrapped with the generator's error handling helpers and may fail with GeneratorError when
57
+ * JSON parsing, cryptographic operations, or other wrapped computations error.
58
+ * - Sensitive values (CMS encryption key) are handled in redacted form to avoid accidental exposure.
59
+ *
60
+ * Security notes:
61
+ * - generateRandomPassword uses cryptographically secure randomness; generateRandomIDNumber does not and should not
62
+ * be used where cryptographic unpredictability is required.
63
+ * - Tokens are signed and validated using HMAC-SHA256 (HS256). Ensure the configured CMS_ENCRYPTION_KEY is strong and
64
+ * kept secret.
65
+ *
66
+ * Returns:
67
+ * - An object containing the four utilities: { generateRandomIDNumber, generateRandomPassword, generateToken, testToken }.
68
+ *
69
+ * Example:
70
+ * @example
71
+ * const sdk = yield* SDKGenerators;
72
+ * const token = yield* sdk.generateToken('user-123');
73
+ * const result = yield* sdk.testToken(token);
74
+ *
75
+ * @throws {GeneratorError} if any underlying generator-wrapped operation (parsing, crypto, config retrieval) fails.
76
+ */
77
+ export declare const SDKGenerators: Effect.Effect<{
78
+ generateRandomIDNumber: (length: number) => Effect.Effect<number, GeneratorError, never>;
79
+ generateRandomPassword: (length: number) => Effect.Effect<string, GeneratorError, never>;
80
+ generateToken: (userId: string, noExpire?: boolean | undefined) => Effect.Effect<string, GeneratorError, never>;
81
+ testToken: (token: string) => Effect.Effect<JwtVerificationResult, GeneratorError, never>;
82
+ }, import("effect/ConfigError").ConfigError, never>;
83
+ export {};
@@ -0,0 +1,106 @@
1
+ import * as crypto from "node:crypto";
2
+ import { Config, Data, Effect, Redacted } from "@withstudiocms/effect";
3
+ class GeneratorError extends Data.TaggedError("GeneratorError") {
4
+ }
5
+ const useGeneratorError = (_try) => Effect.try({
6
+ try: _try,
7
+ catch: (error) => new GeneratorError({ cause: error })
8
+ });
9
+ const SDKGenerators = Effect.gen(function* () {
10
+ const redactedCMSEncryptionKey = yield* Config.redacted("CMS_ENCRYPTION_KEY");
11
+ const cmsEncryptionKey = Redacted.value(redactedCMSEncryptionKey);
12
+ const base64UrlEncode = (input) => Buffer.from(input).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
13
+ const base64UrlDecode = (input) => {
14
+ let newInput = input.replace(/-/g, "+").replace(/_/g, "/");
15
+ while (newInput.length % 4 !== 0) {
16
+ newInput += "=";
17
+ }
18
+ return Buffer.from(newInput, "base64").toString();
19
+ };
20
+ const generateJwt = Effect.fn(
21
+ (secret, payload, noExpire) => useGeneratorError(() => {
22
+ const header = { alg: "HS256", typ: "JWT" };
23
+ const currentDate = /* @__PURE__ */ new Date();
24
+ const thirtyYearsFromToday = Math.floor(
25
+ currentDate.setFullYear(currentDate.getFullYear() + 30) / 1e3
26
+ );
27
+ const exp = noExpire ? thirtyYearsFromToday : Math.floor(Date.now() / 1e3) + 86400;
28
+ const payloadObj = {
29
+ ...payload,
30
+ iat: Math.floor(Date.now() / 1e3),
31
+ // Corrected iat
32
+ exp
33
+ };
34
+ const encodedHeader = base64UrlEncode(JSON.stringify(header));
35
+ const encodedPayload = base64UrlEncode(JSON.stringify(payloadObj));
36
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
37
+ const signature = Buffer.from(
38
+ crypto.createHmac("sha256", secret + secret).update(signatureInput).digest()
39
+ ).toString("base64url");
40
+ return `${encodedHeader}.${encodedPayload}.${signature}`;
41
+ })
42
+ );
43
+ const verifyJwt = (token, secret) => Effect.gen(function* () {
44
+ const [encodedHeader, encodedPayload, encodedSignature] = token.split(".");
45
+ if (!encodedHeader || !encodedPayload || !encodedSignature) {
46
+ yield* Effect.logDebug("Invalid token format");
47
+ return { isValid: false };
48
+ }
49
+ const [header, payload] = yield* Effect.all([
50
+ useGeneratorError(() => JSON.parse(base64UrlDecode(encodedHeader))),
51
+ useGeneratorError(() => JSON.parse(base64UrlDecode(encodedPayload)))
52
+ ]);
53
+ if (header.alg !== "HS256") {
54
+ yield* Effect.logDebug("Invalid algorithm");
55
+ return { isValid: false };
56
+ }
57
+ const currentTime = Math.floor(Date.now() / 1e3);
58
+ if (payload.exp && currentTime > payload.exp) {
59
+ yield* Effect.logDebug("Token has expired");
60
+ return { isValid: false };
61
+ }
62
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
63
+ const generatedSignature = yield* useGeneratorError(() => {
64
+ return Buffer.from(
65
+ crypto.createHmac("sha256", secret + secret).update(signatureInput).digest()
66
+ ).toString("base64url");
67
+ });
68
+ if (generatedSignature !== encodedSignature) {
69
+ yield* Effect.logDebug("Invalid signature");
70
+ return { isValid: false };
71
+ }
72
+ return { isValid: true, userId: payload.userId };
73
+ });
74
+ const generateRandomIDNumber = Effect.fn(
75
+ (length) => useGeneratorError(() => Math.floor(Math.random() * 10 ** length))
76
+ );
77
+ const generateRandomPassword = Effect.fn(
78
+ (length) => useGeneratorError(() => {
79
+ const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
80
+ let password = "";
81
+ const maxValidValue = Math.floor((2 ** 32 - 1) / characters.length) * characters.length;
82
+ while (password.length < length) {
83
+ const n = crypto.getRandomValues(new Uint32Array(1))[0];
84
+ if (n < maxValidValue) {
85
+ password += characters[n % characters.length];
86
+ }
87
+ }
88
+ return password;
89
+ })
90
+ );
91
+ const generateToken = Effect.fn(
92
+ (userId, noExpire) => generateJwt(cmsEncryptionKey, { userId }, noExpire)
93
+ );
94
+ const testToken = Effect.fn((token) => verifyJwt(token, cmsEncryptionKey));
95
+ return {
96
+ generateRandomIDNumber,
97
+ generateRandomPassword,
98
+ generateToken,
99
+ testToken
100
+ };
101
+ });
102
+ export {
103
+ GeneratorError,
104
+ SDKGenerators,
105
+ useGeneratorError
106
+ };
@@ -0,0 +1,191 @@
1
+ import { Effect, Schema } from '@withstudiocms/effect';
2
+ import { CacheService } from '../../cache.js';
3
+ declare const NpmRegistryResponseSchema_base: Schema.Class<NpmRegistryResponseSchema, {
4
+ _id: typeof Schema.String;
5
+ _integrity: typeof Schema.String;
6
+ _npmUser: Schema.Struct<{
7
+ name: typeof Schema.String;
8
+ email: typeof Schema.String;
9
+ trustedPublisher: Schema.Struct<{
10
+ id: typeof Schema.String;
11
+ oidcConfigId: typeof Schema.String;
12
+ }>;
13
+ }>;
14
+ maintainers: Schema.Array$<Schema.Struct<{
15
+ name: typeof Schema.String;
16
+ email: typeof Schema.String;
17
+ }>>;
18
+ name: typeof Schema.String;
19
+ version: typeof Schema.String;
20
+ description: typeof Schema.String;
21
+ license: typeof Schema.String;
22
+ author: Schema.optional<Schema.Struct<{
23
+ name: typeof Schema.String;
24
+ url: typeof Schema.String;
25
+ }>>;
26
+ repository: Schema.optional<Schema.Struct<{
27
+ type: typeof Schema.String;
28
+ url: typeof Schema.String;
29
+ directory: Schema.optional<typeof Schema.String>;
30
+ }>>;
31
+ contributors: Schema.optional<Schema.Array$<Schema.Struct<{
32
+ name: typeof Schema.String;
33
+ url: Schema.optional<typeof Schema.String>;
34
+ }>>>;
35
+ keywords: Schema.Array$<typeof Schema.String>;
36
+ homepage: Schema.optional<typeof Schema.String>;
37
+ publishConfig: Schema.optional<Schema.Struct<{
38
+ access: typeof Schema.String;
39
+ provenance: typeof Schema.Boolean;
40
+ }>>;
41
+ sideEffects: Schema.optional<typeof Schema.Boolean>;
42
+ }, Schema.Struct.Encoded<{
43
+ _id: typeof Schema.String;
44
+ _integrity: typeof Schema.String;
45
+ _npmUser: Schema.Struct<{
46
+ name: typeof Schema.String;
47
+ email: typeof Schema.String;
48
+ trustedPublisher: Schema.Struct<{
49
+ id: typeof Schema.String;
50
+ oidcConfigId: typeof Schema.String;
51
+ }>;
52
+ }>;
53
+ maintainers: Schema.Array$<Schema.Struct<{
54
+ name: typeof Schema.String;
55
+ email: typeof Schema.String;
56
+ }>>;
57
+ name: typeof Schema.String;
58
+ version: typeof Schema.String;
59
+ description: typeof Schema.String;
60
+ license: typeof Schema.String;
61
+ author: Schema.optional<Schema.Struct<{
62
+ name: typeof Schema.String;
63
+ url: typeof Schema.String;
64
+ }>>;
65
+ repository: Schema.optional<Schema.Struct<{
66
+ type: typeof Schema.String;
67
+ url: typeof Schema.String;
68
+ directory: Schema.optional<typeof Schema.String>;
69
+ }>>;
70
+ contributors: Schema.optional<Schema.Array$<Schema.Struct<{
71
+ name: typeof Schema.String;
72
+ url: Schema.optional<typeof Schema.String>;
73
+ }>>>;
74
+ keywords: Schema.Array$<typeof Schema.String>;
75
+ homepage: Schema.optional<typeof Schema.String>;
76
+ publishConfig: Schema.optional<Schema.Struct<{
77
+ access: typeof Schema.String;
78
+ provenance: typeof Schema.Boolean;
79
+ }>>;
80
+ sideEffects: Schema.optional<typeof Schema.Boolean>;
81
+ }>, never, {
82
+ readonly name: string;
83
+ } & {
84
+ readonly description: string;
85
+ } & {
86
+ readonly _id: string;
87
+ } & {
88
+ readonly _integrity: string;
89
+ } & {
90
+ readonly version: string;
91
+ } & {
92
+ readonly license: string;
93
+ } & {
94
+ readonly _npmUser: {
95
+ readonly name: string;
96
+ readonly email: string;
97
+ readonly trustedPublisher: {
98
+ readonly id: string;
99
+ readonly oidcConfigId: string;
100
+ };
101
+ };
102
+ } & {
103
+ readonly maintainers: readonly {
104
+ readonly name: string;
105
+ readonly email: string;
106
+ }[];
107
+ } & {
108
+ readonly author?: {
109
+ readonly name: string;
110
+ readonly url: string;
111
+ } | undefined;
112
+ } & {
113
+ readonly repository?: {
114
+ readonly type: string;
115
+ readonly url: string;
116
+ readonly directory?: string | undefined;
117
+ } | undefined;
118
+ } & {
119
+ readonly contributors?: readonly {
120
+ readonly name: string;
121
+ readonly url?: string | undefined;
122
+ }[] | undefined;
123
+ } & {
124
+ readonly keywords: readonly string[];
125
+ } & {
126
+ readonly homepage?: string | undefined;
127
+ } & {
128
+ readonly publishConfig?: {
129
+ readonly access: string;
130
+ readonly provenance: boolean;
131
+ } | undefined;
132
+ } & {
133
+ readonly sideEffects?: boolean | undefined;
134
+ }, {}, {}>;
135
+ /**
136
+ * Represents the shape of an npm registry document for a specific package version.
137
+ *
138
+ * This class models the JSON structure returned by the npm registry for a published
139
+ * package version. It includes metadata about the package (name, version, description,
140
+ * license), publisher/maintainer information, repository and publish configuration,
141
+ * and other auxiliary fields commonly present on the npm registry.
142
+ *
143
+ * @remarks
144
+ * The shape mirrors common fields returned by the npm registry and is suitable for
145
+ * validating or typing responses when fetching package metadata (for example via
146
+ * the registry REST API). Optional fields reflect that not all packages supply every
147
+ * piece of metadata.
148
+ *
149
+ * @public
150
+ */
151
+ export declare class NpmRegistryResponseSchema extends NpmRegistryResponseSchema_base {
152
+ }
153
+ /**
154
+ * Helper to process and validate the HTTP response from the NPM registry.
155
+ */
156
+ export declare const parseNpmRegistryResponse: (response: Response) => Effect.Effect<NpmRegistryResponseSchema, import("effect/Cause").UnknownException | import("effect/ParseResult").ParseError, never>;
157
+ /**
158
+ * Cache key for NPM package data.
159
+ */
160
+ export declare const cacheKey: (name: string, version: string) => string;
161
+ /**
162
+ * Cache options for NPM package data.
163
+ */
164
+ export declare const cacheOpts: {
165
+ tags: string[];
166
+ };
167
+ /**
168
+ * An Effect service for retrieving the version of an NPM package from the NPM registry.
169
+ *
170
+ * @remarks
171
+ * This service uses an HTTP client with retry logic to fetch the version information
172
+ * for a given package and version (defaulting to 'latest') from the NPM registry.
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const version = await GetVersionFromNPM.get('react');
177
+ * ```
178
+ *
179
+ * @method get
180
+ * @param pkg - The name of the NPM package.
181
+ * @param ver - The version tag or number (defaults to 'latest').
182
+ * @returns An Effect that resolves to the version string of the specified package.
183
+ */
184
+ export declare const GetFromNPM: Effect.Effect<{
185
+ getVersion: (pkg: string, ver?: string | undefined) => Effect.Effect<{
186
+ version: string;
187
+ lastCacheUpdate: Date;
188
+ }, import("effect/Cause").UnknownException | Error | import("effect/ParseResult").ParseError, never>;
189
+ getDataFromNPM: (pkg: string, ver?: string | undefined) => Effect.Effect<NpmRegistryResponseSchema, import("effect/Cause").UnknownException | Error | import("effect/ParseResult").ParseError, never>;
190
+ }, never, CacheService>;
191
+ export {};
@@ -0,0 +1,100 @@
1
+ import { Effect, HTTPClient, Schema } from "@withstudiocms/effect";
2
+ import { CacheService } from "../../cache.js";
3
+ import { cacheKeyGetters, cacheTags } from "../../consts.js";
4
+ class NpmRegistryResponseSchema extends Schema.Class(
5
+ "NpmRegistryResponseSchema"
6
+ )({
7
+ _id: Schema.String,
8
+ _integrity: Schema.String,
9
+ _npmUser: Schema.Struct({
10
+ name: Schema.String,
11
+ email: Schema.String,
12
+ trustedPublisher: Schema.Struct({
13
+ id: Schema.String,
14
+ oidcConfigId: Schema.String
15
+ })
16
+ }),
17
+ maintainers: Schema.Array(
18
+ Schema.Struct({
19
+ name: Schema.String,
20
+ email: Schema.String
21
+ })
22
+ ),
23
+ name: Schema.String,
24
+ version: Schema.String,
25
+ description: Schema.String,
26
+ license: Schema.String,
27
+ author: Schema.optional(
28
+ Schema.Struct({
29
+ name: Schema.String,
30
+ url: Schema.String
31
+ })
32
+ ),
33
+ repository: Schema.optional(
34
+ Schema.Struct({
35
+ type: Schema.String,
36
+ url: Schema.String,
37
+ directory: Schema.optional(Schema.String)
38
+ })
39
+ ),
40
+ contributors: Schema.optional(
41
+ Schema.Array(
42
+ Schema.Struct({
43
+ name: Schema.String,
44
+ url: Schema.optional(Schema.String)
45
+ })
46
+ )
47
+ ),
48
+ keywords: Schema.Array(Schema.String),
49
+ homepage: Schema.optional(Schema.String),
50
+ publishConfig: Schema.optional(
51
+ Schema.Struct({
52
+ access: Schema.String,
53
+ provenance: Schema.Boolean
54
+ })
55
+ ),
56
+ sideEffects: Schema.optional(Schema.Boolean)
57
+ }) {
58
+ }
59
+ const parseNpmRegistryResponse = Effect.fn(function* (response) {
60
+ const data = yield* Effect.tryPromise(() => response.json());
61
+ return yield* Schema.decodeUnknown(NpmRegistryResponseSchema)(data);
62
+ });
63
+ const cacheKey = cacheKeyGetters.npmPackage;
64
+ const cacheOpts = { tags: cacheTags.npmPackage };
65
+ const GetFromNPM = Effect.gen(function* () {
66
+ const [{ memoize, getCacheStatus }] = yield* Effect.all([CacheService]);
67
+ const effectFetch = Effect.fn(
68
+ (url) => Effect.tryPromise({
69
+ try: () => fetch(url),
70
+ catch: (error) => new Error(`Failed to fetch NPM package data: ${String(error)}`)
71
+ })
72
+ );
73
+ const _remapCacheStatusData = (status) => status ? status.lastUpdatedAt : /* @__PURE__ */ new Date();
74
+ const _remapData = (pkg, srcVer) => ({ version }) => Effect.all({
75
+ version: Effect.succeed(version),
76
+ lastCacheUpdate: getCacheStatus(cacheKey(pkg, srcVer || "latest")).pipe(
77
+ Effect.map(_remapCacheStatusData)
78
+ )
79
+ });
80
+ const getDataFromNPM = Effect.fn(
81
+ (pkg, ver) => memoize(
82
+ cacheKey(pkg, ver || "latest"),
83
+ effectFetch(`https://registry.npmjs.org/${pkg}/${ver || "latest"}`).pipe(
84
+ Effect.flatMap(parseNpmRegistryResponse)
85
+ ),
86
+ cacheOpts
87
+ )
88
+ );
89
+ const getVersion = Effect.fn(
90
+ (pkg, ver) => getDataFromNPM(pkg, ver).pipe(Effect.flatMap(_remapData(pkg, ver)))
91
+ );
92
+ return { getVersion, getDataFromNPM };
93
+ }).pipe(Effect.provide(HTTPClient.Default));
94
+ export {
95
+ GetFromNPM,
96
+ NpmRegistryResponseSchema,
97
+ cacheKey,
98
+ cacheOpts,
99
+ parseNpmRegistryResponse
100
+ };