@forgerock/login-framework-cli 0.1.0 → 0.1.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.
- package/README.md +6 -6
- package/dist/src/commands/init.d.ts +1 -1
- package/dist/src/commands/init.js +2 -2
- package/dist/src/commands/update.d.ts +1 -1
- package/dist/src/commands/update.js +3 -3
- package/dist/src/main.js +2 -2
- package/dist/src/mcp.js +3 -3
- package/dist/src/services/registry.d.ts +8 -1
- package/dist/src/services/registry.js +18 -11
- package/dist/src/services/release.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,10 +35,10 @@ pnpm dev
|
|
|
35
35
|
|
|
36
36
|
**Options**
|
|
37
37
|
|
|
38
|
-
| Flag | Description
|
|
39
|
-
| ---------------- |
|
|
40
|
-
| `--tag <tag>` | Download a specific release tag (e.g. `v1.2.0`). Defaults to
|
|
41
|
-
| `--local <path>` | Use a local framework directory instead of downloading from GitHub. Useful for development or air-gapped environments.
|
|
38
|
+
| Flag | Description |
|
|
39
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `--tag <tag>` | Download a specific Git release tag (e.g. `@forgerock/login-widget@2.1.0`; legacy `v1.2.0` also works). Defaults to `main`. |
|
|
41
|
+
| `--local <path>` | Use a local framework directory instead of downloading from GitHub. Useful for development or air-gapped environments. |
|
|
42
42
|
|
|
43
43
|
**What it does**
|
|
44
44
|
|
|
@@ -163,7 +163,7 @@ Run from the root of an initialized project.
|
|
|
163
163
|
|
|
164
164
|
```sh
|
|
165
165
|
ping-lf update # update to latest
|
|
166
|
-
ping-lf update --
|
|
166
|
+
ping-lf update --tag @forgerock/login-widget@2.1.0 # pin to a specific release tag
|
|
167
167
|
ping-lf update --local ../framework # use a local directory
|
|
168
168
|
ping-lf update --directory /path/to/project # target a specific project root
|
|
169
169
|
```
|
|
@@ -214,7 +214,7 @@ pnpm --filter @forgerock/login-framework-cli link --global
|
|
|
214
214
|
```sh
|
|
215
215
|
ping-lf init my-project
|
|
216
216
|
ping-lf generate callback MyCallback
|
|
217
|
-
ping-lf update --tag
|
|
217
|
+
ping-lf update --tag @forgerock/login-widget@2.1.0
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
Rebuilds (`pnpm --filter @forgerock/login-framework-cli build`) update the global binary automatically — no relink needed.
|
|
@@ -12,5 +12,5 @@ export declare const initProject: ({ directory, local, version }: InitProjectOpt
|
|
|
12
12
|
export declare const initCommand: Command.Command<"init", FileSystem.FileSystem | Path.Path | import("../services/release.js").Release, import("../errors.js").InvalidVersionError | import("../errors.js").ReleaseNetworkError | import("../errors.js").ReleaseParseError | import("../errors.js").ReleaseFsError | import("../errors.js").FileSystemError | DirectoryConflictError | DirectoryNotEmptyError | import("../errors.js").RegistryScanError | import("@effect/platform/Error").PlatformError, {
|
|
13
13
|
readonly directory: string;
|
|
14
14
|
readonly local: Option.Option<string>;
|
|
15
|
-
readonly
|
|
15
|
+
readonly tag: Option.Option<string>;
|
|
16
16
|
}>;
|
|
@@ -89,5 +89,5 @@ export const initProject = ({ directory, local, version }) => Effect.gen(functio
|
|
|
89
89
|
export const initCommand = Command.make('init', {
|
|
90
90
|
directory: Args.text({ name: 'directory' }).pipe(Args.withDescription('Directory to initialize the project in. Use "./" for the current directory.')),
|
|
91
91
|
local: Options.optional(Options.text('local').pipe(Options.withDescription('Path to a local framework directory. If omitted, the main branch is downloaded from GitHub.'))),
|
|
92
|
-
|
|
93
|
-
}, ({ directory, local,
|
|
92
|
+
tag: Options.optional(Options.text('tag').pipe(Options.withDescription('Framework Git release tag to download (e.g. @forgerock/login-widget@2.1.0 or v1.2.0). If omitted, the main branch is used.'))),
|
|
93
|
+
}, ({ directory, local, tag }) => initProject({ directory, local, version: tag })).pipe(Command.withDescription('Bootstrap a new Ping Login Widget or Login App project from a GitHub release or a local framework path.'));
|
|
@@ -3,5 +3,5 @@ import { Option } from 'effect';
|
|
|
3
3
|
export declare const updateCommand: Command.Command<"update", import("@effect/platform/FileSystem").FileSystem | import("@effect/platform/Path").Path | import("../services/release.js").Release, import("../errors.js").InvalidVersionError | import("../errors.js").ReleaseNetworkError | import("../errors.js").ReleaseParseError | import("../errors.js").ReleaseFsError | import("../errors.js").FileSystemError | import("../errors.js").GeneratorVersionError | import("../errors.js").RegistryScanError | import("@effect/platform/Error").PlatformError, {
|
|
4
4
|
readonly directory: Option.Option<string>;
|
|
5
5
|
readonly local: Option.Option<string>;
|
|
6
|
-
readonly
|
|
6
|
+
readonly tag: Option.Option<string>;
|
|
7
7
|
}>;
|
|
@@ -8,8 +8,8 @@ import { resolveSource } from './source.js';
|
|
|
8
8
|
export const updateCommand = Command.make('update', {
|
|
9
9
|
directory: Options.optional(Options.text('directory').pipe(Options.withDescription('Project root directory. Defaults to the current working directory.'))),
|
|
10
10
|
local: Options.optional(Options.text('local').pipe(Options.withDescription('Path to a local framework directory. If omitted, the main branch is downloaded from GitHub.'))),
|
|
11
|
-
|
|
12
|
-
}, ({ local,
|
|
11
|
+
tag: Options.optional(Options.text('tag').pipe(Options.withDescription('Framework Git release tag to update to (e.g. @forgerock/login-widget@2.1.0 or v1.2.0). If omitted, the main branch is used.'))),
|
|
12
|
+
}, ({ local, tag, directory }) => Effect.gen(function* () {
|
|
13
13
|
const cwd = nodePath.resolve(expandTilde(Option.getOrElse(directory, () => process.cwd())));
|
|
14
14
|
// ── 1. Verify this is an initialized project ──────────────────────────
|
|
15
15
|
const currentVersion = yield* assertValidProject(cwd);
|
|
@@ -18,7 +18,7 @@ export const updateCommand = Command.make('update', {
|
|
|
18
18
|
// Effect.scoped closes the Scope that release.fetch opens, triggering
|
|
19
19
|
// automatic removal of the .framework-tmp directory after copying.
|
|
20
20
|
const resolvedVersion = yield* Effect.scoped(Effect.gen(function* () {
|
|
21
|
-
const { sourceDir, resolvedVersion } = yield* resolveSource(local,
|
|
21
|
+
const { sourceDir, resolvedVersion } = yield* resolveSource(local, tag, cwd);
|
|
22
22
|
yield* Console.log('Copying updated framework files...');
|
|
23
23
|
yield* copyWithExclusions(sourceDir, cwd);
|
|
24
24
|
return resolvedVersion;
|
package/dist/src/main.js
CHANGED
|
@@ -49,8 +49,8 @@ const program = isMcpLaunch
|
|
|
49
49
|
` ${err.cause}\n\n` +
|
|
50
50
|
` • Check your network connection and try again.\n` +
|
|
51
51
|
` • Use a local path: ping-lf init <dir> --local <path>\n` +
|
|
52
|
-
` • Specify a
|
|
53
|
-
` Expected
|
|
52
|
+
` • Specify a tag: ping-lf init <dir> --tag @forgerock/login-widget@2.1.0\n`).pipe(Effect.andThen(Effect.die(err)))), Effect.catchTag('ReleaseParseError', (err) => Console.error(`\nError: Failed to parse the release data.\n ${err.cause}\n`).pipe(Effect.andThen(Effect.die(err)))), Effect.catchTag('ReleaseFsError', (err) => Console.error(`\nError: Filesystem error during release download (${err.operation}).\n ${err.cause}\n`).pipe(Effect.andThen(Effect.die(err)))), Effect.catchTag('InvalidVersionError', (err) => Console.error(`\nError: "${err.version}" is not a valid version tag.\n` +
|
|
53
|
+
` Expected a Git tag like @forgerock/login-widget@2.1.0 or v1.0.0.\n` +
|
|
54
54
|
` Use "ping-lf releases" to list available versions.\n`).pipe(Effect.andThen(Effect.die(err)))), Effect.catchTag('ReleaseNotFoundError', (err) => Console.error(`\nError: No releases found on GitHub.` +
|
|
55
55
|
(err.cause ? `\n ${err.cause}` : '') +
|
|
56
56
|
`\n\n • Check your network connection and try again.\n` +
|
package/dist/src/mcp.js
CHANGED
|
@@ -25,7 +25,7 @@ function formatError(cause) {
|
|
|
25
25
|
const e = err.value;
|
|
26
26
|
switch (e._tag) {
|
|
27
27
|
case 'InvalidVersionError':
|
|
28
|
-
return `Invalid version "${e['version']}". Expected
|
|
28
|
+
return `Invalid version "${e['version']}". Expected a Git tag like @forgerock/login-widget@2.1.0 or v1.0.0.`;
|
|
29
29
|
case 'ReleaseNetworkError':
|
|
30
30
|
return `Network error reaching GitHub: ${e['cause']}`;
|
|
31
31
|
case 'ReleaseParseError':
|
|
@@ -69,7 +69,7 @@ const InitTool = Tool.make('init', {
|
|
|
69
69
|
description: 'Directory to initialize the project in (e.g. "my-project" or "./")',
|
|
70
70
|
}),
|
|
71
71
|
version: Schema.optional(Schema.String).annotations({
|
|
72
|
-
description: 'Framework
|
|
72
|
+
description: 'Framework Git release tag to download (e.g. @forgerock/login-widget@2.1.0 or v1.2.0). Defaults to main.',
|
|
73
73
|
}),
|
|
74
74
|
local: Schema.optional(Schema.String).annotations({
|
|
75
75
|
description: 'Path to a local framework directory instead of downloading from GitHub.',
|
|
@@ -117,7 +117,7 @@ const UpdateTool = Tool.make('update', {
|
|
|
117
117
|
parameters: {
|
|
118
118
|
directory: directoryParam,
|
|
119
119
|
version: Schema.optional(Schema.String).annotations({
|
|
120
|
-
description: 'Framework
|
|
120
|
+
description: 'Framework Git release tag to update to (e.g. @forgerock/login-widget@2.1.0 or v1.2.0). Defaults to main.',
|
|
121
121
|
}),
|
|
122
122
|
local: Schema.optional(Schema.String).annotations({
|
|
123
123
|
description: 'Path to a local framework directory instead of downloading from GitHub.',
|
|
@@ -2,13 +2,20 @@ import { FileSystem, Path } from '@effect/platform';
|
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
3
|
import { RegistryScanError } from '../errors.js';
|
|
4
4
|
type ComponentType = 'stage' | 'callback';
|
|
5
|
-
|
|
5
|
+
interface ComponentEntry {
|
|
6
|
+
filePath: string;
|
|
7
|
+
name: string;
|
|
8
|
+
type: ComponentType;
|
|
9
|
+
acceptedProps: string[];
|
|
10
|
+
}
|
|
11
|
+
/** Extracts names of all `export let` prop declarations from a Svelte component's instance script. */
|
|
6
12
|
export declare function parseAcceptedProps(content: string): string[];
|
|
7
13
|
/** Parses and validates the leading `<!-- @component -->` block from a Svelte file. */
|
|
8
14
|
export declare const parseComponentHeader: (filePath: string, content: string) => Effect.Effect<{
|
|
9
15
|
type: ComponentType;
|
|
10
16
|
name: string;
|
|
11
17
|
}, RegistryScanError>;
|
|
18
|
+
export declare function buildRegistryContent(path: Path.Path, registryDir: string, stageComponents: ComponentEntry[], callbackComponents: ComponentEntry[]): string;
|
|
12
19
|
/**
|
|
13
20
|
* Scans `experimental/custom/stages/` and `experimental/custom/callbacks/` for
|
|
14
21
|
* `@component`-annotated Svelte files and writes
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import { FileSystem, Path } from '@effect/platform';
|
|
2
2
|
import { Console, Effect } from 'effect';
|
|
3
|
+
import { parse } from 'svelte/compiler';
|
|
3
4
|
import { RegistryScanError } from '../errors.js';
|
|
4
5
|
// --------------------------------------------------------------------------
|
|
5
6
|
// Helpers (exported for testing)
|
|
6
7
|
// --------------------------------------------------------------------------
|
|
7
|
-
/** Extracts names of all `export let` prop declarations from a Svelte component's
|
|
8
|
+
/** Extracts names of all `export let` prop declarations from a Svelte component's instance script. */
|
|
8
9
|
export function parseAcceptedProps(content) {
|
|
9
|
-
const
|
|
10
|
-
if (!scriptMatch)
|
|
11
|
-
return [];
|
|
10
|
+
const abstractSyntaxTree = parse(content, { modern: false });
|
|
12
11
|
const props = [];
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
for (const node of abstractSyntaxTree.instance?.content.body ?? []) {
|
|
13
|
+
if (node.type !== 'ExportNamedDeclaration') {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (node.declaration?.type === 'VariableDeclaration' && node.declaration.kind === 'let') {
|
|
17
|
+
for (const declarator of node.declaration.declarations) {
|
|
18
|
+
if (declarator.id.type === 'Identifier') {
|
|
19
|
+
props.push(declarator.id.name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
17
24
|
return props;
|
|
18
25
|
}
|
|
19
26
|
function toPascalCase(str) {
|
|
@@ -72,7 +79,7 @@ const scanDirectory = (fs, path, dir, expectedType) => findSvelteFiles(fs, path,
|
|
|
72
79
|
// --------------------------------------------------------------------------
|
|
73
80
|
// Registry content builder (pure)
|
|
74
81
|
// --------------------------------------------------------------------------
|
|
75
|
-
function buildRegistryContent(path, registryDir, stageComponents, callbackComponents) {
|
|
82
|
+
export function buildRegistryContent(path, registryDir, stageComponents, callbackComponents) {
|
|
76
83
|
const toEntry = (prefix) => ({ filePath, name, acceptedProps }) => {
|
|
77
84
|
const relPath = path.relative(registryDir, filePath).replace(/\\/g, '/');
|
|
78
85
|
const importPath = relPath.startsWith('.') ? relPath : `./${relPath}`;
|
|
@@ -111,12 +118,12 @@ function buildRegistryContent(path, registryDir, stageComponents, callbackCompon
|
|
|
111
118
|
}
|
|
112
119
|
lines.push(`export const customStageRegistry: Record<string, CustomRegistryEntry> = {`);
|
|
113
120
|
for (const { varName, name, acceptedProps } of stageEntries)
|
|
114
|
-
lines.push(` ${JSON.stringify(name)}: { component
|
|
121
|
+
lines.push(` ${JSON.stringify(name)}: { get component() { return ${varName}; }, acceptedProps: ${JSON.stringify(acceptedProps)} },`);
|
|
115
122
|
lines.push(`};`);
|
|
116
123
|
lines.push(``);
|
|
117
124
|
lines.push(`export const customCallbackRegistry: Record<string, CustomRegistryEntry> = {`);
|
|
118
125
|
for (const { varName, name, acceptedProps } of callbackEntries)
|
|
119
|
-
lines.push(` ${JSON.stringify(name)}: { component
|
|
126
|
+
lines.push(` ${JSON.stringify(name)}: { get component() { return ${varName}; }, acceptedProps: ${JSON.stringify(acceptedProps)} },`);
|
|
120
127
|
lines.push(`};`);
|
|
121
128
|
lines.push(``);
|
|
122
129
|
return lines.join('\n');
|
|
@@ -8,7 +8,7 @@ const API_HEADERS = {
|
|
|
8
8
|
'User-Agent': 'ping-lf-cli',
|
|
9
9
|
Accept: 'application/vnd.github.v3+json',
|
|
10
10
|
};
|
|
11
|
-
const VERSION_REGEX = /^v?\d+\.\d+\.\d+(-[\w.]+)
|
|
11
|
+
const VERSION_REGEX = /^(?:v?\d+\.\d+\.\d+(-[\w.]+)?|@forgerock\/login-widget@\d+\.\d+\.\d+(-[\w.]+)?)$/;
|
|
12
12
|
const toCauseString = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
13
13
|
export function validateVersion(version) {
|
|
14
14
|
return VERSION_REGEX.test(version)
|
package/package.json
CHANGED