@passlock/cli 2.0.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.
- package/README.md +58 -0
- package/README.template.md +58 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +31 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +102 -0
- package/dist/init.js.map +1 -0
- package/package.json +86 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The pnpm script build:readme replaces tokens
|
|
3
|
+
in README.template.md and outputs to README.md
|
|
4
|
+
-->
|
|
5
|
+
<div align="center">
|
|
6
|
+
<a href="https://github.com/passlock-dev/passlock">
|
|
7
|
+
<img src="https://passlock-assets.b-cdn.net/images/passlock-logo.svg" alt="Passlock logo" width="80" height="80">
|
|
8
|
+
</a>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div align="center">
|
|
12
|
+
<picture align="center">
|
|
13
|
+
<source srcset="https://passlock-assets.b-cdn.net/images/client-repo-banner.dark.svg" media="(prefers-color-scheme: dark)" />
|
|
14
|
+
<img align="center" width=550 height=50 src="https://passlock-assets.b-cdn.net/images/client-repo-banner.svg" />
|
|
15
|
+
</picture>
|
|
16
|
+
<p align="center">
|
|
17
|
+
CLI wrapper around the Passlock API
|
|
18
|
+
<br />
|
|
19
|
+
<a href="https://passlock.dev"><strong>Project website »</strong></a>
|
|
20
|
+
<br />
|
|
21
|
+
<a href="https://github.com/passlock-dev/passlock">GitHub</a>
|
|
22
|
+
·
|
|
23
|
+
<a href="https://passlock.dev">Documentation</a>
|
|
24
|
+
·
|
|
25
|
+
<a href="https://passlock.dev/getting-started/">Quick start</a>
|
|
26
|
+
·
|
|
27
|
+
<a href="https://passlock.dev/#demo">Demo</a>
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<br />
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
Node 22+
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Install the CLI globally:
|
|
40
|
+
|
|
41
|
+
`npm install -g @passlock/cli`
|
|
42
|
+
|
|
43
|
+
or
|
|
44
|
+
|
|
45
|
+
`pnpm add -g @passlock/api`
|
|
46
|
+
|
|
47
|
+
### Create a cloud instance
|
|
48
|
+
|
|
49
|
+
`passlock init`
|
|
50
|
+
|
|
51
|
+
Follow the prompts
|
|
52
|
+
|
|
53
|
+
### Help
|
|
54
|
+
|
|
55
|
+
`passlock --help`
|
|
56
|
+
|
|
57
|
+
[contact]: https://passlock.dev/contact
|
|
58
|
+
[client]: https://www.npmjs.com/package/@passlock/client
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The pnpm script build:readme replaces tokens
|
|
3
|
+
in README.template.md and outputs to README.md
|
|
4
|
+
-->
|
|
5
|
+
<div align="center">
|
|
6
|
+
<a href="#{GITHUB_REPO}#">
|
|
7
|
+
<img src="#{PASSLOCK_LOGO}#" alt="Passlock logo" width="80" height="80">
|
|
8
|
+
</a>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div align="center">
|
|
12
|
+
<picture align="center">
|
|
13
|
+
<source srcset="#{ASSETS}#/images/client-repo-banner.dark.svg" media="(prefers-color-scheme: dark)" />
|
|
14
|
+
<img align="center" width=550 height=50 src="#{ASSETS}#/images/client-repo-banner.svg" />
|
|
15
|
+
</picture>
|
|
16
|
+
<p align="center">
|
|
17
|
+
CLI wrapper around the Passlock API
|
|
18
|
+
<br />
|
|
19
|
+
<a href="#{PASSLOCK_SITE}#"><strong>Project website »</strong></a>
|
|
20
|
+
<br />
|
|
21
|
+
<a href="#{GITHUB_REPO}#">GitHub</a>
|
|
22
|
+
·
|
|
23
|
+
<a href="#{DOCS}#">Documentation</a>
|
|
24
|
+
·
|
|
25
|
+
<a href="#{TUTORIAL}#">Quick start</a>
|
|
26
|
+
·
|
|
27
|
+
<a href="#{DEMO}#">Demo</a>
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<br />
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
Node 22+
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Install the CLI globally:
|
|
40
|
+
|
|
41
|
+
`npm install -g @passlock/cli`
|
|
42
|
+
|
|
43
|
+
or
|
|
44
|
+
|
|
45
|
+
`pnpm add -g @passlock/api`
|
|
46
|
+
|
|
47
|
+
### Create a cloud instance
|
|
48
|
+
|
|
49
|
+
`passlock init`
|
|
50
|
+
|
|
51
|
+
Follow the prompts
|
|
52
|
+
|
|
53
|
+
### Help
|
|
54
|
+
|
|
55
|
+
`passlock --help`
|
|
56
|
+
|
|
57
|
+
[contact]: https://passlock.dev/contact
|
|
58
|
+
[client]: https://www.npmjs.com/package/@passlock/client
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Options } from "@effect/cli";
|
|
3
|
+
import { HttpClient, HttpClientRequest } from "@effect/platform";
|
|
4
|
+
import { NodeContext, NodeHttpClient, NodeRuntime, } from "@effect/platform-node";
|
|
5
|
+
import { Console, Effect, Layer, pipe } from "effect";
|
|
6
|
+
import { init } from "./init.js";
|
|
7
|
+
// prepend the correct endpoint
|
|
8
|
+
const mapClient = (endpoint) => pipe(HttpClient.HttpClient, Effect.map(HttpClient.mapRequest(HttpClientRequest.prependUrl(endpoint))), Layer.effect(HttpClient.HttpClient));
|
|
9
|
+
const endpoint = Options.text("endpoint")
|
|
10
|
+
.pipe(Options.withAlias("e"))
|
|
11
|
+
.pipe(Options.withDescription("Passlock API endpoint"))
|
|
12
|
+
.pipe(Options.withDefault("https://api.passlock.dev"));
|
|
13
|
+
const initCmd = pipe(Command.make("init", { endpoint }, ({ endpoint }) => pipe(init, Effect.provide(mapClient(endpoint)))), Command.withDescription("Setup a new Passlock cloud instance"));
|
|
14
|
+
const mainCmd = pipe(Command.make("passlock", {}, () => Console.log("Passlock CLI tools\nRun with --help for commands and options")), Command.withDescription("Passlock CLI tools"));
|
|
15
|
+
const command = pipe(mainCmd, Command.withSubcommands([initCmd]));
|
|
16
|
+
// Set up the CLI application
|
|
17
|
+
const cli = Command.run(command, {
|
|
18
|
+
name: "Passlock CLI tools",
|
|
19
|
+
version: "v2.0.0.alpha.1",
|
|
20
|
+
});
|
|
21
|
+
// Prepare and run the CLI application
|
|
22
|
+
pipe(cli(process.argv), Effect.provide(NodeHttpClient.layer), Effect.provide(NodeContext.layer), NodeRuntime.runMain);
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EACL,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,+BAA+B;AAC/B,MAAM,SAAS,GAAG,CAAC,QAAgB,EAAE,EAAE,CACrC,IAAI,CACF,UAAU,CAAC,UAAU,EACrB,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,iBAAiB,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EACzE,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CACpC,CAAC;AAEJ,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;KACtC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;KAC5B,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;KACtD,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,CAClB,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAClD,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAChD,EACD,OAAO,CAAC,eAAe,CAAC,qCAAqC,CAAC,CAC/D,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,CAClB,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC,EAC/G,OAAO,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAC9C,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAElE,6BAA6B;AAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE;IAC/B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,gBAAgB;CAC1B,CAAC,CAAC;AAEH,sCAAsC;AACtC,IAAI,CACF,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EACjB,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,EACpC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EACjC,WAAW,CAAC,OAAO,CACpB,CAAC","sourcesContent":["#!/usr/bin/env node\n\nimport { Command, Options } from \"@effect/cli\";\nimport { HttpClient, HttpClientRequest } from \"@effect/platform\";\nimport {\n NodeContext,\n NodeHttpClient,\n NodeRuntime,\n} from \"@effect/platform-node\";\nimport { Console, Effect, Layer, pipe } from \"effect\";\nimport { init } from \"./init.js\";\n\n// prepend the correct endpoint\nconst mapClient = (endpoint: string) =>\n pipe(\n HttpClient.HttpClient,\n Effect.map(HttpClient.mapRequest(HttpClientRequest.prependUrl(endpoint))),\n Layer.effect(HttpClient.HttpClient),\n );\n\nconst endpoint = Options.text(\"endpoint\")\n .pipe(Options.withAlias(\"e\"))\n .pipe(Options.withDescription(\"Passlock API endpoint\"))\n .pipe(Options.withDefault(\"https://api.passlock.dev\"));\n\nconst initCmd = pipe(\n Command.make(\"init\", { endpoint }, ({ endpoint }) =>\n pipe(init, Effect.provide(mapClient(endpoint))),\n ),\n Command.withDescription(\"Setup a new Passlock cloud instance\"),\n);\n\nconst mainCmd = pipe(\n Command.make(\"passlock\", {}, () => Console.log(\"Passlock CLI tools\\nRun with --help for commands and options\")),\n Command.withDescription(\"Passlock CLI tools\"),\n);\n\nconst command = pipe(mainCmd, Command.withSubcommands([initCmd]));\n\n// Set up the CLI application\nconst cli = Command.run(command, {\n name: \"Passlock CLI tools\",\n version: \"v2.0.0.alpha.1\",\n});\n\n// Prepare and run the CLI application\npipe(\n cli(process.argv),\n Effect.provide(NodeHttpClient.layer),\n Effect.provide(NodeContext.layer),\n NodeRuntime.runMain,\n);\n"]}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpClient } from "@effect/platform";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
|
+
declare const SignupPayload: Schema.Struct<{
|
|
4
|
+
email: typeof Schema.String;
|
|
5
|
+
firstName: typeof Schema.String;
|
|
6
|
+
lastName: typeof Schema.String;
|
|
7
|
+
}>;
|
|
8
|
+
type SignupPayload = typeof SignupPayload.Type;
|
|
9
|
+
declare const InvalidEmail_base: Schema.TaggedErrorClass<InvalidEmail, "@error/InvalidEmail", {
|
|
10
|
+
readonly _tag: Schema.tag<"@error/InvalidEmail">;
|
|
11
|
+
} & {
|
|
12
|
+
message: typeof Schema.String;
|
|
13
|
+
}>;
|
|
14
|
+
export declare class InvalidEmail extends InvalidEmail_base {
|
|
15
|
+
}
|
|
16
|
+
declare const DuplicateEmail_base: Schema.TaggedErrorClass<DuplicateEmail, "@error/DuplicateEmail", {
|
|
17
|
+
readonly _tag: Schema.tag<"@error/DuplicateEmail">;
|
|
18
|
+
} & {
|
|
19
|
+
message: typeof Schema.String;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class DuplicateEmail extends DuplicateEmail_base {
|
|
22
|
+
}
|
|
23
|
+
export declare const TenancyData: Schema.TaggedStruct<"TenancyData", {
|
|
24
|
+
tenancyId: typeof Schema.String;
|
|
25
|
+
apiKey: typeof Schema.String;
|
|
26
|
+
}>;
|
|
27
|
+
export type TenancyData = typeof TenancyData.Type;
|
|
28
|
+
export declare const signup: (payload: SignupPayload) => Effect.Effect<TenancyData, InvalidEmail | DuplicateEmail, HttpClient.HttpClient>;
|
|
29
|
+
export declare const init: Effect.Effect<void, never, HttpClient.HttpClient>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAYA,OAAO,EAAY,UAAU,EAAsB,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAQ,MAAM,EAAe,MAAM,EAAE,MAAM,QAAQ,CAAC;AAM3D,QAAA,MAAM,aAAa;;;;EAIjB,CAAC;AAEH,KAAK,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;;;;;;AAE/C,qBAAa,YAAa,SAAQ,iBAEkB;CAAG;;;;;;AAEvD,qBAAa,cAAe,SAAQ,mBAEkB;CAAG;AAEzD,eAAO,MAAM,WAAW;;;EAGtB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAC;AA6DlD,eAAO,MAAM,MAAM,GACjB,SAAS,aAAa,KACrB,MAAM,CAAC,MAAM,CACd,WAAW,EACX,YAAY,GAAG,cAAc,EAC7B,UAAU,CAAC,UAAU,CAkCpB,CAAC;AAEJ,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAmDlE,CAAC"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { confirm, intro, isCancel, log, outro, spinner, text, } from "@clack/prompts";
|
|
2
|
+
import kleur from "kleur";
|
|
3
|
+
import { HttpBody, HttpClient, HttpClientResponse } from "@effect/platform";
|
|
4
|
+
import { Data, Effect, Match, pipe, Schema } from "effect";
|
|
5
|
+
class CancelError extends Data.TaggedError("@error/Abort") {
|
|
6
|
+
}
|
|
7
|
+
const emailRegex = /^[^@]+@[^@]+.[^@]+$/;
|
|
8
|
+
const SignupPayload = Schema.Struct({
|
|
9
|
+
email: Schema.String,
|
|
10
|
+
firstName: Schema.String,
|
|
11
|
+
lastName: Schema.String,
|
|
12
|
+
});
|
|
13
|
+
export class InvalidEmail extends Schema.TaggedError("@error/InvalidEmail")("@error/InvalidEmail", { message: Schema.String }) {
|
|
14
|
+
}
|
|
15
|
+
export class DuplicateEmail extends Schema.TaggedError("@error/DuplicateEmail")("@error/DuplicateEmail", { message: Schema.String }) {
|
|
16
|
+
}
|
|
17
|
+
export const TenancyData = Schema.TaggedStruct("TenancyData", {
|
|
18
|
+
tenancyId: Schema.String,
|
|
19
|
+
apiKey: Schema.String,
|
|
20
|
+
});
|
|
21
|
+
const captureData = Effect.gen(function* () {
|
|
22
|
+
const email = yield* Effect.promise(() => text({
|
|
23
|
+
message: "Root account email? (we'll send a single use code to this address)",
|
|
24
|
+
placeholder: "jdoe@gmail.com",
|
|
25
|
+
validate(value) {
|
|
26
|
+
if (value.length === 0)
|
|
27
|
+
return `Value is required!`;
|
|
28
|
+
if (!emailRegex.test(value))
|
|
29
|
+
return `Please provide a valid email address!`;
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
if (isCancel(email))
|
|
33
|
+
return yield* new CancelError({});
|
|
34
|
+
const firstName = yield* Effect.promise(() => text({
|
|
35
|
+
message: "Your first/given name",
|
|
36
|
+
placeholder: "John",
|
|
37
|
+
validate(value) {
|
|
38
|
+
if (value.length === 0)
|
|
39
|
+
return `Value is required!`;
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
if (isCancel(firstName))
|
|
43
|
+
return yield* new CancelError({});
|
|
44
|
+
const lastName = yield* Effect.promise(() => text({
|
|
45
|
+
message: "Your last/family name",
|
|
46
|
+
placeholder: "Doe",
|
|
47
|
+
validate(value) {
|
|
48
|
+
if (value.length === 0)
|
|
49
|
+
return `Value is required!`;
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
52
|
+
if (isCancel(lastName))
|
|
53
|
+
return yield* new CancelError({});
|
|
54
|
+
const isConfirmed = yield* Effect.promise(() => confirm({
|
|
55
|
+
message: `Using ${firstName} ${lastName} <${email}>, continue?`,
|
|
56
|
+
}));
|
|
57
|
+
if (isCancel(isConfirmed))
|
|
58
|
+
return yield* new CancelError({});
|
|
59
|
+
return isConfirmed ? { email, firstName, lastName } : yield* captureData;
|
|
60
|
+
});
|
|
61
|
+
export const signup = (payload) => pipe(Effect.gen(function* () {
|
|
62
|
+
const client = yield* HttpClient.HttpClient;
|
|
63
|
+
const encodedPayload = yield* HttpBody.json(payload);
|
|
64
|
+
const response = yield* pipe(
|
|
65
|
+
// endpoint will be prepended by the HttpClient
|
|
66
|
+
client.post("/signup", {
|
|
67
|
+
body: encodedPayload,
|
|
68
|
+
}));
|
|
69
|
+
const encoded = yield* HttpClientResponse.matchStatus(response, {
|
|
70
|
+
"2xx": () => HttpClientResponse.schemaBodyJson(TenancyData)(response),
|
|
71
|
+
orElse: () => HttpClientResponse.schemaBodyJson(Schema.Union(InvalidEmail, DuplicateEmail))(response),
|
|
72
|
+
});
|
|
73
|
+
return yield* pipe(Match.value(encoded), Match.tag("TenancyData", (principal) => Effect.succeed(principal)), Match.tag("@error/InvalidEmail", (err) => Effect.fail(err)), Match.tag("@error/DuplicateEmail", (err) => Effect.fail(err)), Match.exhaustive);
|
|
74
|
+
}), Effect.catchTag("HttpBodyError", Effect.die), Effect.catchTag("ParseError", Effect.die), Effect.catchTag("RequestError", Effect.die), Effect.catchTag("ResponseError", Effect.die));
|
|
75
|
+
export const init = pipe(Effect.gen(function* () {
|
|
76
|
+
intro(`Setting up new Passlock cloud instance...`);
|
|
77
|
+
const signupData = yield* captureData;
|
|
78
|
+
const s = spinner();
|
|
79
|
+
s.start("Setting up instance");
|
|
80
|
+
const { tenancyId, apiKey } = yield* pipe(signup(signupData), Effect.tapError(() => Effect.sync(() => {
|
|
81
|
+
s.stop("Something went wrong", 1);
|
|
82
|
+
})));
|
|
83
|
+
s.stop("Instance ready");
|
|
84
|
+
log.success("Here are your instance credentials\nPlease keep them secure");
|
|
85
|
+
log.message(`Tenancy ID: ${kleur.blue(tenancyId)}\n` +
|
|
86
|
+
`API Key: ${kleur.blue(apiKey)}`);
|
|
87
|
+
log.message("Please refer to the quick start at" +
|
|
88
|
+
"https://passlock.dev/getting-started/");
|
|
89
|
+
outro("You're all set!");
|
|
90
|
+
}), Effect.catchTags({
|
|
91
|
+
"@error/Abort": () => Effect.sync(() => {
|
|
92
|
+
log.error("Operation cancelled");
|
|
93
|
+
}),
|
|
94
|
+
"@error/DuplicateEmail": () => Effect.sync(() => {
|
|
95
|
+
log.error("Email already registered\n" +
|
|
96
|
+
"Sign in at https://console.passlock.dev");
|
|
97
|
+
}),
|
|
98
|
+
"@error/InvalidEmail": () => Effect.sync(() => {
|
|
99
|
+
log.error("Invalid email address");
|
|
100
|
+
}),
|
|
101
|
+
}));
|
|
102
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n confirm,\n intro,\n isCancel,\n log,\n outro,\n spinner,\n text,\n} from \"@clack/prompts\";\n\nimport kleur from \"kleur\";\n\nimport { HttpBody, HttpClient, HttpClientResponse } from \"@effect/platform\";\nimport { Data, Effect, Match, pipe, Schema } from \"effect\";\n\nclass CancelError extends Data.TaggedError(\"@error/Abort\")<object> {}\n\nconst emailRegex = /^[^@]+@[^@]+.[^@]+$/;\n\nconst SignupPayload = Schema.Struct({\n email: Schema.String,\n firstName: Schema.String,\n lastName: Schema.String,\n});\n\ntype SignupPayload = typeof SignupPayload.Type;\n\nexport class InvalidEmail extends Schema.TaggedError<InvalidEmail>(\n \"@error/InvalidEmail\",\n)(\"@error/InvalidEmail\", { message: Schema.String }) {}\n\nexport class DuplicateEmail extends Schema.TaggedError<DuplicateEmail>(\n \"@error/DuplicateEmail\",\n)(\"@error/DuplicateEmail\", { message: Schema.String }) {}\n\nexport const TenancyData = Schema.TaggedStruct(\"TenancyData\", {\n tenancyId: Schema.String,\n apiKey: Schema.String,\n});\n\nexport type TenancyData = typeof TenancyData.Type;\n\nconst captureData: Effect.Effect<SignupPayload, CancelError> = Effect.gen(\n function* () {\n const email = yield* Effect.promise(() =>\n text({\n message:\n \"Root account email? (we'll send a single use code to this address)\",\n\n placeholder: \"jdoe@gmail.com\",\n\n validate(value) {\n if (value.length === 0) return `Value is required!`;\n if (!emailRegex.test(value))\n return `Please provide a valid email address!`;\n },\n }),\n );\n\n if (isCancel(email)) return yield* new CancelError({});\n\n const firstName = yield* Effect.promise(() =>\n text({\n message: \"Your first/given name\",\n\n placeholder: \"John\",\n\n validate(value) {\n if (value.length === 0) return `Value is required!`;\n },\n }),\n );\n\n if (isCancel(firstName)) return yield* new CancelError({});\n\n const lastName = yield* Effect.promise(() =>\n text({\n message: \"Your last/family name\",\n\n placeholder: \"Doe\",\n\n validate(value) {\n if (value.length === 0) return `Value is required!`;\n },\n }),\n );\n\n if (isCancel(lastName)) return yield* new CancelError({});\n\n const isConfirmed = yield* Effect.promise(() =>\n confirm({\n message: `Using ${firstName} ${lastName} <${email}>, continue?`,\n }),\n );\n\n if (isCancel(isConfirmed)) return yield* new CancelError({});\n\n return isConfirmed ? { email, firstName, lastName } : yield* captureData;\n },\n);\n\nexport const signup = (\n payload: SignupPayload,\n): Effect.Effect<\n TenancyData,\n InvalidEmail | DuplicateEmail,\n HttpClient.HttpClient\n> =>\n pipe(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const encodedPayload = yield* HttpBody.json(payload);\n\n const response = yield* pipe(\n // endpoint will be prepended by the HttpClient\n client.post(\"/signup\", {\n body: encodedPayload,\n }),\n );\n\n const encoded = yield* HttpClientResponse.matchStatus(response, {\n \"2xx\": () => HttpClientResponse.schemaBodyJson(TenancyData)(response),\n orElse: () =>\n HttpClientResponse.schemaBodyJson(\n Schema.Union(InvalidEmail, DuplicateEmail),\n )(response),\n });\n\n return yield* pipe(\n Match.value(encoded),\n Match.tag(\"TenancyData\", (principal) => Effect.succeed(principal)),\n Match.tag(\"@error/InvalidEmail\", (err) => Effect.fail(err)),\n Match.tag(\"@error/DuplicateEmail\", (err) => Effect.fail(err)),\n Match.exhaustive,\n );\n }),\n Effect.catchTag(\"HttpBodyError\", Effect.die),\n Effect.catchTag(\"ParseError\", Effect.die),\n Effect.catchTag(\"RequestError\", Effect.die),\n Effect.catchTag(\"ResponseError\", Effect.die),\n );\n\nexport const init: Effect.Effect<void, never, HttpClient.HttpClient> = pipe(\n Effect.gen(function* () {\n intro(`Setting up new Passlock cloud instance...`);\n\n const signupData = yield* captureData;\n const s = spinner();\n s.start(\"Setting up instance\");\n\n const { tenancyId, apiKey } = yield* pipe(\n signup(signupData),\n Effect.tapError(() =>\n Effect.sync(() => {\n s.stop(\"Something went wrong\", 1);\n }),\n ),\n );\n s.stop(\"Instance ready\");\n\n log.success(\n \"Here are your instance credentials\\nPlease keep them secure\",\n );\n\n log.message(\n `Tenancy ID: ${kleur.blue(tenancyId)}\\n` +\n `API Key: ${kleur.blue(apiKey)}`,\n );\n\n log.message(\n \"Please refer to the quick start at\" +\n \"https://passlock.dev/getting-started/\",\n );\n\n outro(\"You're all set!\");\n }),\n Effect.catchTags({\n \"@error/Abort\": () =>\n Effect.sync(() => {\n log.error(\"Operation cancelled\");\n }),\n \"@error/DuplicateEmail\": () =>\n Effect.sync(() => {\n log.error(\n \"Email already registered\\n\" +\n \"Sign in at https://console.passlock.dev\",\n );\n }),\n \"@error/InvalidEmail\": () =>\n Effect.sync(() => {\n log.error(\"Invalid email address\");\n }),\n }),\n);\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@passlock/cli",
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"description": "CLI tooling support for the Passlock authentication framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"passkey",
|
|
7
|
+
"webauthn",
|
|
8
|
+
"passlock"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://passlock.dev",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/passlock-dev/passlock/issues",
|
|
13
|
+
"email": "team@passlock.dev"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/passlock-dev/passlock.git",
|
|
18
|
+
"directory": "packages/client"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "Toby Hobson",
|
|
23
|
+
"email": "toby@passlock.dev"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"module": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"bin": {
|
|
37
|
+
"passlock": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"!dist/**/*.test.*",
|
|
42
|
+
"!dist/**/*.spec.*"
|
|
43
|
+
],
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@clack/prompts": "^0.11.0",
|
|
46
|
+
"@effect/cli": "^0.72.1",
|
|
47
|
+
"@effect/platform": "^0.93.6",
|
|
48
|
+
"@effect/platform-node": "^0.103.0",
|
|
49
|
+
"effect": "3.19.8",
|
|
50
|
+
"kleur": "^4.1.5"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/compat": "^2.0.0",
|
|
54
|
+
"@eslint/js": "^9.39.1",
|
|
55
|
+
"@types/node": "24.10.1",
|
|
56
|
+
"eslint": "^9.39.1",
|
|
57
|
+
"globals": "^16.5.0",
|
|
58
|
+
"npm-check-updates": "19.1.2",
|
|
59
|
+
"prettier": "^3.7.4",
|
|
60
|
+
"publint": "0.3.15",
|
|
61
|
+
"rimraf": "^6.1.2",
|
|
62
|
+
"tsx": "4.21.0",
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"typescript-eslint": "^8.48.1"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=22"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsc --build",
|
|
71
|
+
"build:clean": "$npm_execpath run clean:full && $npm_execpath run build",
|
|
72
|
+
"build:production": "$npm_execpath run build:clean && $npm_execpath run build:readme && $npm_execpath run replaceTokens && echo '' && publint",
|
|
73
|
+
"build:readme": "LATEST=${npm_package_version} tsx ../../scripts/replace-readme-tokens.ts ./packages/cli",
|
|
74
|
+
"clean": "tsc --build --clean",
|
|
75
|
+
"clean:full": "rimraf dist tsconfig.tsbuildinfo",
|
|
76
|
+
"format": "prettier --write \"src/**/*.+(js|ts|json)\"",
|
|
77
|
+
"lint": "prettier --check \"src/**/*.+(js|ts|json)\" && eslint ./src",
|
|
78
|
+
"lint:fix": "prettier --check \"src/**/*.+(js|ts|json)\" && eslint ./src --fix",
|
|
79
|
+
"replaceTokens": "LATEST=${npm_package_version} tsx ../../scripts/replace-tokens.ts ./packages/cli",
|
|
80
|
+
"test": "vitest run",
|
|
81
|
+
"test:coverage": "vitest run --coverage",
|
|
82
|
+
"test:ui": "vitest --coverage.enabled=true --ui",
|
|
83
|
+
"test:watch": "vitest dev",
|
|
84
|
+
"typecheck": "tsc --noEmit"
|
|
85
|
+
}
|
|
86
|
+
}
|