@forgerock/login-framework-cli 0.0.0-beta-20260429204418

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/scripts/generate-registry.d.ts +2 -0
  4. package/dist/scripts/generate-registry.js +20 -0
  5. package/dist/src/commands/generate.d.ts +28 -0
  6. package/dist/src/commands/generate.js +137 -0
  7. package/dist/src/commands/init.d.ts +8 -0
  8. package/dist/src/commands/init.js +95 -0
  9. package/dist/src/commands/releases.d.ts +3 -0
  10. package/dist/src/commands/releases.js +12 -0
  11. package/dist/src/commands/source.d.ts +22 -0
  12. package/dist/src/commands/source.js +24 -0
  13. package/dist/src/commands/update.d.ts +5 -0
  14. package/dist/src/commands/update.js +36 -0
  15. package/dist/src/config/exclusions.d.ts +1 -0
  16. package/dist/src/config/exclusions.js +62 -0
  17. package/dist/src/config/version.d.ts +26 -0
  18. package/dist/src/config/version.js +41 -0
  19. package/dist/src/errors.d.ts +90 -0
  20. package/dist/src/errors.js +25 -0
  21. package/dist/src/main.d.ts +2 -0
  22. package/dist/src/main.js +34 -0
  23. package/dist/src/services/file-system.d.ts +13 -0
  24. package/dist/src/services/file-system.js +64 -0
  25. package/dist/src/services/registry.d.ts +22 -0
  26. package/dist/src/services/registry.js +169 -0
  27. package/dist/src/services/release.d.ts +27 -0
  28. package/dist/src/services/release.js +86 -0
  29. package/dist/src/templates/callback/__COMPONENT_SLUG__.svelte +73 -0
  30. package/dist/src/templates/callback/__COMPONENT_SLUG__.utilities.test.ts +9 -0
  31. package/dist/src/templates/callback/__COMPONENT_SLUG__.utilities.ts +13 -0
  32. package/dist/src/templates/stage/__COMPONENT_SLUG__.svelte +148 -0
  33. package/dist/src/templates/stage/__COMPONENT_SLUG__.utilities.test.ts +9 -0
  34. package/dist/src/templates/stage/__COMPONENT_SLUG__.utilities.ts +13 -0
  35. package/dist/src/utils.d.ts +2 -0
  36. package/dist/src/utils.js +4 -0
  37. package/dist/templates/callback/__COMPONENT_SLUG__.utilities.d.ts +10 -0
  38. package/dist/templates/callback/__COMPONENT_SLUG__.utilities.js +12 -0
  39. package/dist/templates/callback/__COMPONENT_SLUG__.utilities.test.d.ts +1 -0
  40. package/dist/templates/callback/__COMPONENT_SLUG__.utilities.test.js +7 -0
  41. package/dist/templates/stage/__COMPONENT_SLUG__.utilities.d.ts +10 -0
  42. package/dist/templates/stage/__COMPONENT_SLUG__.utilities.js +12 -0
  43. package/dist/templates/stage/__COMPONENT_SLUG__.utilities.test.d.ts +1 -0
  44. package/dist/templates/stage/__COMPONENT_SLUG__.utilities.test.js +7 -0
  45. package/package.json +59 -0
@@ -0,0 +1,86 @@
1
+ import { Context, Effect, Layer, Schema } from 'effect';
2
+ import { FileSystem, HttpClient, HttpClientResponse } from '@effect/platform';
3
+ import { NodeHttpClient } from '@effect/platform-node';
4
+ import { extract } from 'tar';
5
+ import { InvalidVersionError, ReleaseFsError, ReleaseNetworkError, ReleaseNotFoundError, ReleaseParseError, } from '../errors.js';
6
+ const REPO = 'ForgeRock/forgerock-web-login-framework';
7
+ const API_HEADERS = {
8
+ 'User-Agent': 'ping-lf-cli',
9
+ Accept: 'application/vnd.github.v3+json',
10
+ };
11
+ const VERSION_REGEX = /^v?\d+\.\d+\.\d+(-[\w.]+)?$/;
12
+ const toCauseString = (cause) => cause instanceof Error ? cause.message : String(cause);
13
+ export function validateVersion(version) {
14
+ return VERSION_REGEX.test(version)
15
+ ? Effect.succeed(version)
16
+ : Effect.fail(new InvalidVersionError({ version }));
17
+ }
18
+ const GithubReleaseSchema = Schema.Struct({
19
+ tag_name: Schema.String,
20
+ published_at: Schema.String,
21
+ draft: Schema.Boolean,
22
+ prerelease: Schema.Boolean,
23
+ });
24
+ const decodeGithubReleases = Schema.decodeUnknown(Schema.parseJson(Schema.Array(GithubReleaseSchema)));
25
+ /**
26
+ * Parses a GitHub releases API JSON response and returns an array of
27
+ * `{ tag, publishedAt }` objects, excluding drafts and pre-releases.
28
+ */
29
+ export function parseReleaseTags(json) {
30
+ return decodeGithubReleases(json).pipe(Effect.map((releases) => releases
31
+ .filter((r) => !r.draft && !r.prerelease)
32
+ .map((r) => ({ tag: r.tag_name, publishedAt: r.published_at.slice(0, 10) }))), Effect.mapError((cause) => new ReleaseParseError({ cause: toCauseString(cause) })));
33
+ }
34
+ export class Release extends Context.Tag('Release')() {
35
+ }
36
+ const LatestReleaseSchema = Schema.Struct({ tag_name: Schema.String });
37
+ const decodeLatestRelease = Schema.decodeUnknown(Schema.parseJson(LatestReleaseSchema));
38
+ const downloadAndExtract = (http, url, targetDir) => Effect.gen(function* () {
39
+ const fs = yield* FileSystem.FileSystem;
40
+ const tempDir = `${targetDir}/.framework-tmp`;
41
+ const tarPath = `${tempDir}/release.tar.gz`;
42
+ return yield* Effect.acquireRelease(Effect.gen(function* () {
43
+ yield* fs.makeDirectory(tempDir, { recursive: true }).pipe(Effect.mapError((cause) => new ReleaseFsError({
44
+ operation: 'makeDirectory',
45
+ cause: toCauseString(cause),
46
+ })));
47
+ const buffer = yield* HttpClient.followRedirects(http)
48
+ .get(url, { headers: { 'User-Agent': 'ping-lf-cli' } })
49
+ .pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((res) => res.arrayBuffer), Effect.mapError((cause) => new ReleaseNetworkError({ cause: toCauseString(cause) })));
50
+ yield* fs
51
+ .writeFile(tarPath, new Uint8Array(buffer))
52
+ .pipe(Effect.mapError((cause) => new ReleaseFsError({ operation: 'writeFile', cause: toCauseString(cause) })));
53
+ yield* Effect.tryPromise({
54
+ try: () => extract({ file: tarPath, cwd: tempDir, strip: 1 }),
55
+ catch: (cause) => new ReleaseParseError({ cause: toCauseString(cause) }),
56
+ });
57
+ return tempDir;
58
+ }), (dir) => fs.remove(dir, { recursive: true }).pipe(Effect.ignore));
59
+ });
60
+ const buildGithubReleaseLayer = Layer.effect(Release, Effect.gen(function* () {
61
+ const http = yield* HttpClient.HttpClient;
62
+ return {
63
+ archiveUrl: (version) => `https://github.com/${REPO}/archive/refs/tags/${version}.tar.gz`,
64
+ resolveLatest: () => http
65
+ .get(`https://api.github.com/repos/${REPO}/releases/latest`, { headers: API_HEADERS })
66
+ .pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((res) => res.text), Effect.mapError((cause) => new ReleaseNetworkError({ cause: toCauseString(cause) })), Effect.flatMap((text) => decodeLatestRelease(text).pipe(Effect.mapError((cause) => new ReleaseParseError({ cause: toCauseString(cause) })))), Effect.map((data) => data.tag_name)),
67
+ listReleases: () => http
68
+ .get(`https://api.github.com/repos/${REPO}/releases?per_page=20`, {
69
+ headers: API_HEADERS,
70
+ })
71
+ .pipe(Effect.flatMap(HttpClientResponse.filterStatusOk), Effect.flatMap((res) => res.text), Effect.mapError((cause) => new ReleaseNetworkError({ cause: toCauseString(cause) })), Effect.flatMap((json) => parseReleaseTags(json)), Effect.flatMap((releases) => releases.length === 0
72
+ ? Effect.fail(new ReleaseNotFoundError({ cause: 'No releases found on GitHub' }))
73
+ : Effect.succeed(releases))),
74
+ fetch: (version, targetDir) => Effect.gen(function* () {
75
+ yield* validateVersion(version);
76
+ const url = `https://github.com/${REPO}/archive/refs/tags/${version}.tar.gz`;
77
+ return yield* downloadAndExtract(http, url, targetDir);
78
+ }),
79
+ fetchBranch: (branch, targetDir) => {
80
+ const url = `https://github.com/${REPO}/archive/refs/heads/${branch}.tar.gz`;
81
+ return downloadAndExtract(http, url, targetDir);
82
+ },
83
+ };
84
+ }));
85
+ export const makeGithubReleaseLayer = (httpLayer) => buildGithubReleaseLayer.pipe(Layer.provide(httpLayer));
86
+ export const GithubReleaseLayer = makeGithubReleaseLayer(NodeHttpClient.layer);
@@ -0,0 +1,73 @@
1
+ <!--
2
+ @component
3
+ Type: callback
4
+ Name: __COMPONENT_NAME__
5
+
6
+ Custom callback component. Replace this description with your own.
7
+ -->
8
+
9
+ <script lang="ts">
10
+ import type { FRCallback } from '@forgerock/javascript-sdk';
11
+ import type { z } from 'zod';
12
+
13
+ import type {
14
+ CallbackMetadata,
15
+ SelfSubmitFunction,
16
+ StepMetadata,
17
+ } from '$journey/journey.interfaces';
18
+ import type { styleSchema } from '$core/style.store';
19
+ import type { Maybe } from '$core/interfaces';
20
+
21
+ /**
22
+ * The AM callback instance for this component. Use `callback.getInputValue()`
23
+ * and `callback.setInputValue()` to read/write values sent back to the server.
24
+ */
25
+ export let callback: FRCallback;
26
+
27
+ /**
28
+ * Optional function to trigger self-submission (e.g. after an async action).
29
+ * Set to `null` when the callback does not need to submit the form itself.
30
+ */
31
+ export const selfSubmitFunction: Maybe<SelfSubmitFunction> = null;
32
+
33
+ /**
34
+ * Metadata about the current step (page node) — includes stage name,
35
+ * header/description overrides, and derived helpers like `isStepSelfSubmittable()`.
36
+ */
37
+ export const stepMetadata: Maybe<StepMetadata> = null;
38
+
39
+ /**
40
+ * Per-callback metadata from the step — may include policies, failed policies,
41
+ * and other AM-provided hints for rendering.
42
+ */
43
+ export const callbackMetadata: Maybe<CallbackMetadata> = null;
44
+
45
+ /**
46
+ * The widget's resolved style configuration (colors, logos, labels, etc.).
47
+ * Use this to keep your custom callback visually consistent with the theme.
48
+ */
49
+ export const style: z.infer<typeof styleSchema> = {};
50
+
51
+ // Suppress the "unused export" warning — remove `void` once you use `callback`.
52
+ void callback;
53
+ </script>
54
+
55
+ <!-- Replace the markup below with your custom callback UI. -->
56
+ <div class="__COMPONENT_SLUG__ tw_p-4 tw_rounded-lg tw_border tw_border-secondary-light dark:tw_border-secondary-dark">
57
+ <p class="tw_text-base tw_text-secondary-dark dark:tw_text-secondary-light">
58
+ __COMPONENT_NAME__ callback — replace this with your implementation.
59
+ </p>
60
+ </div>
61
+
62
+ <!--
63
+ Scoped styles for this callback.
64
+ These styles only apply to this component — they won't leak to the rest of the widget.
65
+ You can also use Tailwind utility classes (prefixed with `tw_`) in the markup above.
66
+ -->
67
+ <style>
68
+ .__COMPONENT_SLUG__ {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 0.5rem;
72
+ }
73
+ </style>
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { format__COMPONENT_NAME__Label } from './__COMPONENT_SLUG__.utilities.js';
4
+
5
+ describe('__COMPONENT_NAME__ utilities', () => {
6
+ it('trims whitespace from label', () => {
7
+ expect(format__COMPONENT_NAME__Label(' hello ')).toBe('hello');
8
+ });
9
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ callback.
3
+ *
4
+ * Keep callback-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+
8
+ /**
9
+ * Example utility — replace with your own helpers.
10
+ */
11
+ export function format__COMPONENT_NAME__Label(value: string): string {
12
+ return value.trim();
13
+ }
@@ -0,0 +1,148 @@
1
+ <!--
2
+ @component
3
+ Type: stage
4
+ Name: __COMPONENT_NAME__
5
+
6
+ Custom stage component. Replace this description with your own.
7
+
8
+ A stage controls the layout and submission behaviour of an entire authentication
9
+ step (page node). It receives the full FRStep, maps each callback to its
10
+ component via CallbackMapper, and renders the form chrome (header, alerts,
11
+ submit button, links).
12
+ -->
13
+
14
+ <script lang="ts">
15
+ import type { FRStep } from '@forgerock/javascript-sdk';
16
+ import { afterUpdate, onDestroy, onMount } from 'svelte';
17
+ import { get } from 'svelte/store';
18
+ import type { z } from 'zod';
19
+
20
+ import { interpolate } from '$core/_utilities/i18n.utilities';
21
+ import T from '$components/_utilities/locale-strings.svelte';
22
+ import Alert from '$components/primitives/alert/alert.svelte';
23
+ import Button from '$components/primitives/button/button.svelte';
24
+ import Form from '$components/primitives/form/form.svelte';
25
+ import { convertStringToKey } from '$journey/stages/_utilities/step.utilities';
26
+ import { captureLinks } from '$journey/stages/_utilities/stage.utilities';
27
+ import { styleStore } from '$core/style.store';
28
+ import type { styleSchema } from '$core/style.store';
29
+ import CallbackMapper from '$journey/_utilities/callback-mapper.svelte';
30
+
31
+ import type {
32
+ CallbackMetadata,
33
+ StageFormObject,
34
+ StageJourneyObject,
35
+ StepMetadata,
36
+ } from '$journey/journey.interfaces';
37
+ import type { Maybe } from '$core/interfaces';
38
+
39
+ /** Display mode — determines which chrome is visible (header, links, etc.). */
40
+ export let componentStyle: 'app' | 'inline' | 'modal';
41
+
42
+ /** Form helpers — `form.submit()` triggers the journey's next step. */
43
+ export let form: StageFormObject;
44
+
45
+ /** Bound to the underlying <form> element for programmatic access. */
46
+ export let formEl: HTMLFormElement | null = null;
47
+
48
+ /** Journey-level state — includes `loading` flag and navigation helpers. */
49
+ export let journey: StageJourneyObject;
50
+
51
+ /** Step + callback metadata from AM — policies, derived helpers, stage header. */
52
+ export let metadata: Maybe<{ callbacks: CallbackMetadata[]; step: StepMetadata }>;
53
+
54
+ /** The raw FRStep from the JavaScript SDK — contains all callbacks for this step. */
55
+ export let step: FRStep;
56
+
57
+ // Subscribe to style store so the template can pass styles to child callbacks.
58
+ let currentStyle: z.infer<typeof styleSchema> = get(styleStore);
59
+ const unsubStyle = styleStore.subscribe((v) => (currentStyle = v));
60
+ onDestroy(unsubStyle);
61
+
62
+ let alertNeedsFocus = false;
63
+ let formMessageKey = '';
64
+ let linkWrapper: HTMLElement;
65
+
66
+ /**
67
+ * Auto-submits the form when the step declares itself self-submittable
68
+ * (e.g. a DeviceProfile or PollingWait callback that needs no user input).
69
+ */
70
+ function determineSubmission() {
71
+ if (metadata?.step?.derived.isStepSelfSubmittable()) {
72
+ form?.submit();
73
+ }
74
+ }
75
+
76
+ afterUpdate(() => {
77
+ alertNeedsFocus = !!form?.message;
78
+ });
79
+
80
+ onMount(() => {
81
+ // In modal mode, intercept anchor clicks to navigate within the journey
82
+ // instead of performing a full page navigation.
83
+ if (componentStyle === 'modal') {
84
+ captureLinks(linkWrapper, journey);
85
+ }
86
+ });
87
+
88
+ $: {
89
+ formMessageKey = convertStringToKey(form?.message);
90
+ }
91
+ </script>
92
+
93
+ <!--
94
+ Replace or extend the markup below to customise the stage layout.
95
+ The key pieces to keep:
96
+ 1. <Form> wrapper with `onSubmitWhenValid` bound to `form.submit`
97
+ 2. The {#each} block that maps callbacks via <CallbackMapper>
98
+ 3. A submit <Button> (unless the step is fully self-submittable)
99
+ -->
100
+ <Form bind:formEl ariaDescribedBy="__COMPONENT_SLUG__FailureAlert" onSubmitWhenValid={form?.submit}>
101
+ {#if componentStyle !== 'inline'}
102
+ <h1 class="tw_primary-header dark:tw_primary-header_dark">
103
+ <T key="loginHeader" />
104
+ </h1>
105
+ {/if}
106
+
107
+ {#if form?.message}
108
+ <Alert id="__COMPONENT_SLUG__FailureAlert" needsFocus={alertNeedsFocus} type="error">
109
+ {interpolate(formMessageKey, null, form?.message)}
110
+ </Alert>
111
+ {/if}
112
+
113
+ {#each step?.callbacks as callback, idx}
114
+ <CallbackMapper
115
+ props={{
116
+ callback,
117
+ callbackMetadata: metadata?.callbacks[idx],
118
+ selfSubmitFunction: determineSubmission,
119
+ stepMetadata: metadata?.step && { ...metadata.step },
120
+ style: currentStyle,
121
+ }}
122
+ />
123
+ {/each}
124
+
125
+ {#if metadata?.step?.derived.isUserInputOptional || !metadata?.step?.derived.isStepSelfSubmittable()}
126
+ <Button busy={journey?.loading} style="primary" type="submit" width="full">
127
+ <T key="loginButton" />
128
+ </Button>
129
+ {/if}
130
+
131
+ {#if componentStyle !== 'inline'}
132
+ <p
133
+ bind:this={linkWrapper}
134
+ class="tw_text-base tw_text-center tw_py-4 tw_text-secondary-dark dark:tw_text-secondary-light"
135
+ >
136
+ <T key="dontHaveAnAccount" html={true} />
137
+ </p>
138
+ {/if}
139
+ </Form>
140
+
141
+ <!--
142
+ Scoped styles for this stage.
143
+ These styles only apply to this component — they won't leak to the rest of the widget.
144
+ You can also use Tailwind utility classes (prefixed with `tw_`) in the markup above.
145
+ -->
146
+ <style>
147
+ /* Add your custom stage styles here. */
148
+ </style>
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { format__COMPONENT_NAME__Label } from './__COMPONENT_SLUG__.utilities.js';
4
+
5
+ describe('__COMPONENT_NAME__ utilities', () => {
6
+ it('trims whitespace from label', () => {
7
+ expect(format__COMPONENT_NAME__Label(' hello ')).toBe('hello');
8
+ });
9
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ stage.
3
+ *
4
+ * Keep stage-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+
8
+ /**
9
+ * Example utility — replace with your own helpers.
10
+ */
11
+ export function format__COMPONENT_NAME__Label(value: string): string {
12
+ return value.trim();
13
+ }
@@ -0,0 +1,2 @@
1
+ /** Converts all backslashes to forward slashes for cross-platform path comparisons. */
2
+ export declare function normalizeSeparators(p: string): string;
@@ -0,0 +1,4 @@
1
+ /** Converts all backslashes to forward slashes for cross-platform path comparisons. */
2
+ export function normalizeSeparators(p) {
3
+ return p.replace(/\\/g, '/');
4
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ callback.
3
+ *
4
+ * Keep callback-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+ /**
8
+ * Example utility — replace with your own helpers.
9
+ */
10
+ export declare function format__COMPONENT_NAME__Label(value: string): string;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ callback.
3
+ *
4
+ * Keep callback-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+ /**
8
+ * Example utility — replace with your own helpers.
9
+ */
10
+ export function format__COMPONENT_NAME__Label(value) {
11
+ return value.trim();
12
+ }
@@ -0,0 +1,7 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { format__COMPONENT_NAME__Label } from './__COMPONENT_SLUG__.utilities.js';
3
+ describe('__COMPONENT_NAME__ utilities', () => {
4
+ it('trims whitespace from label', () => {
5
+ expect(format__COMPONENT_NAME__Label(' hello ')).toBe('hello');
6
+ });
7
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ stage.
3
+ *
4
+ * Keep stage-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+ /**
8
+ * Example utility — replace with your own helpers.
9
+ */
10
+ export declare function format__COMPONENT_NAME__Label(value: string): string;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utility functions for the __COMPONENT_NAME__ stage.
3
+ *
4
+ * Keep stage-specific helpers here — data formatting, validation, API calls, etc.
5
+ * This file is unit-tested independently of the Svelte component.
6
+ */
7
+ /**
8
+ * Example utility — replace with your own helpers.
9
+ */
10
+ export function format__COMPONENT_NAME__Label(value) {
11
+ return value.trim();
12
+ }
@@ -0,0 +1,7 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { format__COMPONENT_NAME__Label } from './__COMPONENT_SLUG__.utilities.js';
3
+ describe('__COMPONENT_NAME__ utilities', () => {
4
+ it('trims whitespace from label', () => {
5
+ expect(format__COMPONENT_NAME__Label(' hello ')).toBe('hello');
6
+ });
7
+ });
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@forgerock/login-framework-cli",
3
+ "version": "0.0.0-beta-20260429204418",
4
+ "description": "CLI tool for scaffolding and managing Ping Login Widget and Login App custom component projects",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./dist/src/main.js",
8
+ "./generate-registry": "./dist/scripts/generate-registry.js"
9
+ },
10
+ "main": "./dist/src/main.js",
11
+ "module": "./dist/src/main.js",
12
+ "bin": {
13
+ "ping-lf": "./dist/src/main.js"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "ping-identity",
20
+ "forgerock",
21
+ "login",
22
+ "widget",
23
+ "cli",
24
+ "generator",
25
+ "svelte"
26
+ ],
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/ForgeRock/forgerock-web-login-framework.git",
31
+ "directory": "tools/cli"
32
+ },
33
+ "engines": {
34
+ "node": ">=20.0.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "@effect/cli": "^0.75.0",
41
+ "@effect/platform": "^0.96.0",
42
+ "@effect/platform-node": "^0.106.0",
43
+ "@effect/printer": "^0.49.0",
44
+ "@effect/printer-ansi": "^0.49.0",
45
+ "@effect/typeclass": "^0.40.0",
46
+ "effect": "^3.21.0",
47
+ "tar": "^7.5.13"
48
+ },
49
+ "devDependencies": {
50
+ "typescript": "^5.2.2",
51
+ "vitest": "^3.0.0"
52
+ },
53
+ "scripts": {
54
+ "build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/src/main.js && ([ -d templates ] && cp -r templates/ dist/src/templates/ || true)",
55
+ "dev": "tsc --watch",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest"
58
+ }
59
+ }