create-feathersdev 0.11.0
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/CHANGELOG.md +107 -0
- package/README.md +3 -0
- package/bin/feathersdev +9 -0
- package/esm/actions/app.js +51 -0
- package/esm/actions/integrate.js +80 -0
- package/esm/actions/login.js +8 -0
- package/esm/actions/logout.js +12 -0
- package/esm/actions/server.js +52 -0
- package/esm/base.js +40 -0
- package/esm/flows/check-login.js +70 -0
- package/esm/flows/with-application.js +81 -0
- package/esm/flows/with-organization.js +74 -0
- package/esm/index.js +55 -0
- package/esm/package.json +1 -0
- package/examples/frontend/auth.ts +39 -0
- package/examples/frontend/react/.eslintrc.cjs +18 -0
- package/examples/frontend/react/README.md +30 -0
- package/examples/frontend/react/index.html +13 -0
- package/examples/frontend/react/package.json +29 -0
- package/examples/frontend/react/public/vite.svg +1 -0
- package/examples/frontend/react/src/App.css +42 -0
- package/examples/frontend/react/src/App.tsx +37 -0
- package/examples/frontend/react/src/assets/react.svg +1 -0
- package/examples/frontend/react/src/auth.ts +39 -0
- package/examples/frontend/react/src/automerge.ts +16 -0
- package/examples/frontend/react/src/index.css +68 -0
- package/examples/frontend/react/src/main.tsx +10 -0
- package/examples/frontend/react/src/vite-env.d.ts +1 -0
- package/examples/frontend/react/tsconfig.app.json +27 -0
- package/examples/frontend/react/tsconfig.json +11 -0
- package/examples/frontend/react/tsconfig.node.json +13 -0
- package/examples/frontend/react/vite.config.ts +7 -0
- package/examples/frontend/svelte/.vscode/extensions.json +3 -0
- package/examples/frontend/svelte/README.md +47 -0
- package/examples/frontend/svelte/index.html +13 -0
- package/examples/frontend/svelte/package.json +24 -0
- package/examples/frontend/svelte/public/vite.svg +1 -0
- package/examples/frontend/svelte/src/App.svelte +30 -0
- package/examples/frontend/svelte/src/app.css +79 -0
- package/examples/frontend/svelte/src/assets/svelte.svg +1 -0
- package/examples/frontend/svelte/src/auth.ts +33 -0
- package/examples/frontend/svelte/src/main.ts +9 -0
- package/examples/frontend/svelte/src/vite-env.d.ts +2 -0
- package/examples/frontend/svelte/svelte.config.js +7 -0
- package/examples/frontend/svelte/tsconfig.json +21 -0
- package/examples/frontend/svelte/tsconfig.node.json +12 -0
- package/examples/frontend/svelte/vite.config.ts +8 -0
- package/examples/frontend/vanilla/index.html +13 -0
- package/examples/frontend/vanilla/package.json +16 -0
- package/examples/frontend/vanilla/public/vite.svg +1 -0
- package/examples/frontend/vanilla/src/auth.ts +39 -0
- package/examples/frontend/vanilla/src/automerge.ts +16 -0
- package/examples/frontend/vanilla/src/main.ts +23 -0
- package/examples/frontend/vanilla/src/style.css +96 -0
- package/examples/frontend/vanilla/src/vite-env.d.ts +1 -0
- package/examples/frontend/vanilla/tsconfig.json +23 -0
- package/examples/frontend/vanilla/vite.config.ts +8 -0
- package/examples/frontend/vue/.vscode/extensions.json +3 -0
- package/examples/frontend/vue/README.md +33 -0
- package/examples/frontend/vue/env.d.ts +1 -0
- package/examples/frontend/vue/index.html +13 -0
- package/examples/frontend/vue/package.json +29 -0
- package/examples/frontend/vue/public/favicon.ico +0 -0
- package/examples/frontend/vue/src/App.vue +30 -0
- package/examples/frontend/vue/src/assets/base.css +86 -0
- package/examples/frontend/vue/src/assets/logo.svg +1 -0
- package/examples/frontend/vue/src/assets/main.css +35 -0
- package/examples/frontend/vue/src/auth.ts +39 -0
- package/examples/frontend/vue/src/automerge.ts +16 -0
- package/examples/frontend/vue/src/main.ts +6 -0
- package/examples/frontend/vue/tsconfig.app.json +14 -0
- package/examples/frontend/vue/tsconfig.json +11 -0
- package/examples/frontend/vue/tsconfig.node.json +19 -0
- package/examples/frontend/vue/vite.config.ts +18 -0
- package/examples/server/bun/README.md +15 -0
- package/examples/server/bun/bun.lockb +0 -0
- package/examples/server/bun/package.json +17 -0
- package/examples/server/bun/src/authenticate.ts +17 -0
- package/examples/server/bun/src/index.ts +54 -0
- package/examples/server/bun/tsconfig.json +27 -0
- package/examples/server/cloudflare/.editorconfig +12 -0
- package/examples/server/cloudflare/.prettierrc +6 -0
- package/examples/server/cloudflare/package.json +22 -0
- package/examples/server/cloudflare/src/authenticate.ts +17 -0
- package/examples/server/cloudflare/src/index.ts +50 -0
- package/examples/server/cloudflare/test/index.spec.ts +25 -0
- package/examples/server/cloudflare/test/tsconfig.json +8 -0
- package/examples/server/cloudflare/tsconfig.json +105 -0
- package/examples/server/cloudflare/vitest.config.mts +11 -0
- package/examples/server/cloudflare/worker-configuration.d.ts +4 -0
- package/examples/server/cloudflare/wrangler.toml +108 -0
- package/examples/server/deno/deno.lock +79 -0
- package/examples/server/deno/package.json +13 -0
- package/examples/server/deno/src/authenticate.ts +17 -0
- package/examples/server/deno/src/server.ts +51 -0
- package/examples/server/express/package.json +24 -0
- package/examples/server/express/src/app.ts +26 -0
- package/examples/server/express/src/authenticate.ts +51 -0
- package/examples/server/express/tsconfig.json +108 -0
- package/examples/server/nodejs/package.json +20 -0
- package/examples/server/nodejs/src/authenticate.ts +28 -0
- package/examples/server/nodejs/src/server.ts +38 -0
- package/examples/server/nodejs/tsconfig.json +108 -0
- package/package.json +67 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [0.11.0](https://github.com/feathersdev/cloud/compare/v0.10.1...v0.11.0) (2025-05-25)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Move CLI to create-feathersdev and update flows ([#279](https://github.com/feathersdev/cloud/issues/279)) ([3b7cb26](https://github.com/feathersdev/cloud/commit/3b7cb26a7170ceec5138243ca6016eed2cc10643))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.10.1](https://github.com/feathersdev/cloud/compare/v0.10.0...v0.10.1) (2025-05-16)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* **cli:** Fix feathersdev/auth package reference ([#274](https://github.com/feathersdev/cloud/issues/274)) ([2a0d199](https://github.com/feathersdev/cloud/commit/2a0d199fbb9d4ab9408535f6844bcce0c9ae9563))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# [0.10.0](https://github.com/feathersdev/cloud/compare/v0.9.1...v0.10.0) (2025-05-16)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
* **auth:** Make type and class naming more consistent ([#273](https://github.com/feathersdev/cloud/issues/273)) ([8e5ce70](https://github.com/feathersdev/cloud/commit/8e5ce708e2ca995b7209cd4888e4a8d1bed29f38))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## [0.9.1](https://github.com/feathersdev/cloud/compare/v0.9.0...v0.9.1) (2025-05-15)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Bug Fixes
|
|
43
|
+
|
|
44
|
+
* Move to feathersdev/cli ([#272](https://github.com/feathersdev/cloud/issues/272)) ([7062262](https://github.com/feathersdev/cloud/commit/706226263db7b7f80f07200793559cf3e1988796))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# [0.9.0](https://github.com/feathersdev/cloud/compare/v0.8.0...v0.9.0) (2025-05-15)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
* Update to feathers.dev branding ([#270](https://github.com/feathersdev/cloud/issues/270)) ([475f925](https://github.com/feathersdev/cloud/commit/475f925fafe0072d8cdbe33009b5edee96f5a2de))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# [0.8.0](https://github.com/feathersdev/cloud/compare/v0.7.3...v0.8.0) (2025-04-26)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Features
|
|
65
|
+
|
|
66
|
+
* Refactor verification methods into services ([#268](https://github.com/feathersdev/cloud/issues/268)) ([28d05d0](https://github.com/feathersdev/cloud/commit/28d05d02e6e596652c5b32b1273768a43f343223))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## [0.7.3](https://github.com/feathersdev/cloud/compare/v0.7.2...v0.7.3) (2024-12-20)
|
|
73
|
+
|
|
74
|
+
**Note:** Version bump only for package @featherscloud/cli
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## [0.7.2](https://github.com/feathersdev/cloud/compare/v0.7.1...v0.7.2) (2024-12-16)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Bug Fixes
|
|
84
|
+
|
|
85
|
+
* **cli:** Fix CLI NodeJS example name ([#244](https://github.com/feathersdev/cloud/issues/244)) ([726b113](https://github.com/feathersdev/cloud/commit/726b113ef925d64b3af8900405dc496c9e8a7565))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## [0.7.1](https://github.com/feathersdev/cloud/compare/v0.7.0...v0.7.1) (2024-12-12)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
### Bug Fixes
|
|
95
|
+
|
|
96
|
+
* Update CLI package publish config ([7984758](https://github.com/feathersdev/cloud/commit/7984758b661f6680762b34f6bb217111abc2bf71))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# [0.7.0](https://github.com/feathersdev/cloud/compare/v0.6.5...v0.7.0) (2024-12-12)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
### Features
|
|
106
|
+
|
|
107
|
+
* Feathers Cloud CLI ([#215](https://github.com/feathersdev/cloud/issues/215)) ([18b7010](https://github.com/feathersdev/cloud/commit/18b7010099c0be1e0ab7f2fa2262f87654c6c74b))
|
package/README.md
ADDED
package/bin/feathersdev
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { copyFiles, fromFile, prompt, toFile } from '@featherscloud/pinion';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { withApplication } from '../flows/with-application.js';
|
|
4
|
+
import { generateIntegration } from './integrate.js';
|
|
5
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
|
6
|
+
export function generateApp() {
|
|
7
|
+
return async (init) => Promise.resolve(init)
|
|
8
|
+
.then(prompt(ctx => ({
|
|
9
|
+
framework: {
|
|
10
|
+
type: 'list',
|
|
11
|
+
message: 'Which framework do you want to use?',
|
|
12
|
+
choices: [
|
|
13
|
+
{ name: 'React', value: 'react' },
|
|
14
|
+
{ name: 'Vue', value: 'vue' },
|
|
15
|
+
{ name: 'Svelte', value: 'svelte' },
|
|
16
|
+
{ name: `None (${chalk.gray('or any other framework.')})`, value: 'vanilla' },
|
|
17
|
+
],
|
|
18
|
+
when: !ctx.framework,
|
|
19
|
+
},
|
|
20
|
+
folder: {
|
|
21
|
+
type: 'input',
|
|
22
|
+
message: 'What folder should the app be created in?',
|
|
23
|
+
default: ctx.selectedApplication?.name?.toLowerCase(),
|
|
24
|
+
},
|
|
25
|
+
})))
|
|
26
|
+
.then(copyFiles(fromFile(__dirname, '..', '..', 'examples', 'frontend', ctx => ctx.framework), toFile(ctx => ctx.folder)))
|
|
27
|
+
.then(ctx => ({
|
|
28
|
+
...ctx,
|
|
29
|
+
integrationTypes: ['frontend'],
|
|
30
|
+
frontendFile: `${ctx.folder}/src/auth.ts`,
|
|
31
|
+
}))
|
|
32
|
+
.then(generateIntegration(true));
|
|
33
|
+
}
|
|
34
|
+
export function successMessage(ctx) {
|
|
35
|
+
return `
|
|
36
|
+
Your feathers.dev app is now set up. 🚀
|
|
37
|
+
|
|
38
|
+
Go to the ${chalk.gray(ctx.folder)} folder and install the dependencies with your package manager (e.g. ${chalk.gray('npm install')}).
|
|
39
|
+
Then run the ${chalk.gray('dev')} script (e.g. ${chalk.gray('npm run dev')}) to start the development server.
|
|
40
|
+
|
|
41
|
+
Visit ${chalk.blue.underline('https://app.feathers.dev')} to customize your login page and manage your users.
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
export function app(init) {
|
|
45
|
+
return Promise.resolve(init)
|
|
46
|
+
.then(withApplication)
|
|
47
|
+
.then(generateApp())
|
|
48
|
+
.then((ctx) => {
|
|
49
|
+
ctx.pinion.logger.log(successMessage(ctx));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { fromFile, getCallable, prompt, renderTemplate, toFile, when } from '@featherscloud/pinion';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { withApplication } from '../flows/with-application.js';
|
|
5
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
|
6
|
+
export function replaceFromFile(file, replacements) {
|
|
7
|
+
return async (ctx) => {
|
|
8
|
+
const fileName = await getCallable(file, ctx);
|
|
9
|
+
const content = (await readFile(fileName)).toString();
|
|
10
|
+
const replace = await getCallable(replacements, ctx);
|
|
11
|
+
return Object.keys(replace).reduce((content, key) => content.replace(new RegExp(key, 'g'), replace[key]), content);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function generateIntegration(force = false) {
|
|
15
|
+
return async (ctx) => Promise.resolve(ctx)
|
|
16
|
+
.then(prompt(ctx => ({
|
|
17
|
+
integrationTypes: {
|
|
18
|
+
type: 'checkbox',
|
|
19
|
+
message: 'Which integrations would you like to generate?',
|
|
20
|
+
choices: [
|
|
21
|
+
{
|
|
22
|
+
name: `Frontend - ${chalk.gray('Make authenticated requests from the browser with any framework like React, VueJS, Svelte or Angular etc.')}`,
|
|
23
|
+
checked: true,
|
|
24
|
+
value: 'frontend',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: `Backend - ${chalk.gray('Verify authenticated requests in a NodeJS, Deno, Bun or Cloudflare Worker server')}`,
|
|
28
|
+
checked: true,
|
|
29
|
+
value: 'backend',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
when: !ctx.integrationTypes,
|
|
33
|
+
},
|
|
34
|
+
})))
|
|
35
|
+
.then(prompt((ctx) => ({
|
|
36
|
+
platform: {
|
|
37
|
+
type: 'list',
|
|
38
|
+
message: 'Which backend platform are you using?',
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: 'NodeJS', value: 'nodejs' },
|
|
41
|
+
{ name: 'Deno', value: 'deno' },
|
|
42
|
+
{ name: 'Bun', value: 'bun' },
|
|
43
|
+
{ name: 'Cloudflare Workers', value: 'cloudflare' },
|
|
44
|
+
{ name: 'Express', value: 'express' },
|
|
45
|
+
],
|
|
46
|
+
when: !ctx.platform && ctx.integrationTypes.includes('backend'),
|
|
47
|
+
},
|
|
48
|
+
})))
|
|
49
|
+
.then(prompt(ctx => ({
|
|
50
|
+
frontendFile: {
|
|
51
|
+
type: 'input',
|
|
52
|
+
message: 'Where should the frontend integration file be saved?',
|
|
53
|
+
default: 'frontend/src/auth.ts',
|
|
54
|
+
when: !ctx.frontendFile && ctx.integrationTypes.includes('frontend'),
|
|
55
|
+
},
|
|
56
|
+
backendFile: {
|
|
57
|
+
type: 'input',
|
|
58
|
+
message: 'Where should the backend server integration file be saved?',
|
|
59
|
+
default: 'server/src/authenticate.ts',
|
|
60
|
+
when: !ctx.backendFile && ctx.integrationTypes.includes('backend'),
|
|
61
|
+
},
|
|
62
|
+
})))
|
|
63
|
+
.then(when(ctx => !!ctx.frontendFile, renderTemplate(replaceFromFile(fromFile(__dirname, '..', '..', 'examples', 'frontend', 'auth.ts'), { '<your-app-id>': ctx.selectedApplication.id }), toFile(ctx => ctx.frontendFile), { force })))
|
|
64
|
+
.then(when(ctx => !!ctx.backendFile, renderTemplate(replaceFromFile(fromFile(__dirname, '..', '..', 'examples', 'server', (ctx) => ctx.platform, 'src', 'authenticate.ts'), { '<your-app-id>': ctx.selectedApplication.id }), toFile(ctx => ctx.backendFile), { force })));
|
|
65
|
+
}
|
|
66
|
+
export const successMessage = `
|
|
67
|
+
Your feathers.dev auth app is now integrated. 🚀
|
|
68
|
+
Make sure to install the ${chalk.gray('@feathersdev/auth')} package using your your package manager.
|
|
69
|
+
Have a look at the generated files for more information on how to use them.
|
|
70
|
+
|
|
71
|
+
Go to ${chalk.blue.underline('https://app.feathers.dev')} to customize your login page and manage your users.
|
|
72
|
+
`;
|
|
73
|
+
export function integrate(init) {
|
|
74
|
+
return Promise.resolve(init)
|
|
75
|
+
.then(withApplication)
|
|
76
|
+
.then(generateIntegration())
|
|
77
|
+
.then((ctx) => {
|
|
78
|
+
ctx.pinion.logger.log(successMessage);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { checkLogin } from '../flows/check-login.js';
|
|
2
|
+
export function logout(init) {
|
|
3
|
+
return Promise.resolve(init)
|
|
4
|
+
.then(checkLogin)
|
|
5
|
+
.then(async (ctx) => {
|
|
6
|
+
await ctx.auth.logoutAndForget();
|
|
7
|
+
return ctx;
|
|
8
|
+
})
|
|
9
|
+
.then((ctx) => {
|
|
10
|
+
ctx.pinion.logger.notice('You are now logged out.');
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { copyFiles, fromFile, prompt, toFile } from '@featherscloud/pinion';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { withApplication } from '../flows/with-application.js';
|
|
4
|
+
import { generateIntegration } from './integrate.js';
|
|
5
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
|
6
|
+
export function generateServer() {
|
|
7
|
+
return async (init) => Promise.resolve(init)
|
|
8
|
+
.then(prompt(ctx => ({
|
|
9
|
+
platform: {
|
|
10
|
+
type: 'list',
|
|
11
|
+
message: 'Which server platform do you want to use?',
|
|
12
|
+
when: !ctx.platform,
|
|
13
|
+
choices: [
|
|
14
|
+
{ name: 'Node', value: 'nodejs' },
|
|
15
|
+
{ name: 'Deno', value: 'deno' },
|
|
16
|
+
{ name: 'Bun', value: 'bun' },
|
|
17
|
+
{ name: 'Cloudflare Workers', value: 'cloudflare' },
|
|
18
|
+
{ name: 'Express', value: 'express' },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
folder: {
|
|
22
|
+
type: 'input',
|
|
23
|
+
message: 'What folder should the server be created in?',
|
|
24
|
+
default: ctx.selectedApplication?.name?.toLowerCase(),
|
|
25
|
+
},
|
|
26
|
+
})))
|
|
27
|
+
.then(copyFiles(fromFile(__dirname, '..', '..', 'examples', 'server', ctx => ctx.platform), toFile(ctx => ctx.folder)))
|
|
28
|
+
.then(ctx => ({
|
|
29
|
+
...ctx,
|
|
30
|
+
integrationTypes: ['backend'],
|
|
31
|
+
backendFile: `${ctx.folder}/src/authenticate.ts`,
|
|
32
|
+
}))
|
|
33
|
+
.then(generateIntegration(true));
|
|
34
|
+
}
|
|
35
|
+
export function successMessage(ctx) {
|
|
36
|
+
return `
|
|
37
|
+
Your example server with feathers.dev auth is now set up. 🚀
|
|
38
|
+
|
|
39
|
+
Go to the ${chalk.gray(ctx.folder)} folder and install the dependencies with your package manager (e.g. ${chalk.gray('npm install')}).
|
|
40
|
+
Then run the ${chalk.gray('start')} script (e.g. ${chalk.gray('npm start')}) to start the server.
|
|
41
|
+
|
|
42
|
+
Visit ${chalk.blue.underline('https://app.feathers.dev')} to customize your login page and manage your users.
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
export function server(init) {
|
|
46
|
+
return Promise.resolve(init)
|
|
47
|
+
.then(withApplication)
|
|
48
|
+
.then(generateServer())
|
|
49
|
+
.then((ctx) => {
|
|
50
|
+
ctx.pinion.logger.log(successMessage(ctx));
|
|
51
|
+
});
|
|
52
|
+
}
|
package/esm/base.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getContext } from '@featherscloud/pinion';
|
|
5
|
+
import { createApiClient, createClient } from '@feathersdev/auth';
|
|
6
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
|
7
|
+
// Read the package.json file
|
|
8
|
+
export const packageJson = JSON.parse(readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
9
|
+
// Map OS platform to browser-style OS string
|
|
10
|
+
const osMapping = {
|
|
11
|
+
darwin: () => `Macintosh; Intel Mac OS X ${os.release()}`,
|
|
12
|
+
win32: () => `Windows NT ${os.release()}`,
|
|
13
|
+
linux: () => `X11; Linux ${os.arch()}`,
|
|
14
|
+
};
|
|
15
|
+
export function getUserAgent() {
|
|
16
|
+
const osInfo = osMapping[os.platform()]?.() || 'Unknown';
|
|
17
|
+
return `cli/${packageJson.version} (${osInfo})`;
|
|
18
|
+
}
|
|
19
|
+
export async function getBaseContext(options, init = {}) {
|
|
20
|
+
const ctx = getContext(init);
|
|
21
|
+
const { appId, store, apiUrl, referer } = options;
|
|
22
|
+
const api = createApiClient(apiUrl, {
|
|
23
|
+
'User-Agent': getUserAgent(),
|
|
24
|
+
'Referer': referer,
|
|
25
|
+
});
|
|
26
|
+
const auth = createClient({
|
|
27
|
+
appId,
|
|
28
|
+
store,
|
|
29
|
+
api,
|
|
30
|
+
});
|
|
31
|
+
const device = await auth.getDevice();
|
|
32
|
+
const user = await auth.getUser();
|
|
33
|
+
return {
|
|
34
|
+
...ctx,
|
|
35
|
+
device,
|
|
36
|
+
auth,
|
|
37
|
+
user,
|
|
38
|
+
api,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { prompt } from '@featherscloud/pinion';
|
|
2
|
+
import { LoginRequiredError } from '@feathersdev/auth';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
export async function performLogin(init) {
|
|
5
|
+
return Promise.resolve(init)
|
|
6
|
+
.then(prompt(ctx => ({
|
|
7
|
+
email: {
|
|
8
|
+
type: 'input',
|
|
9
|
+
message: 'What is your email address?',
|
|
10
|
+
default: ctx.user?.email,
|
|
11
|
+
when: !ctx.email,
|
|
12
|
+
},
|
|
13
|
+
})))
|
|
14
|
+
.then(async (ctx) => {
|
|
15
|
+
const credentials = await ctx.device.getCredentials();
|
|
16
|
+
const { id } = await ctx.api.service('verifications/email').create({
|
|
17
|
+
email: ctx.email,
|
|
18
|
+
}, {
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${credentials}`,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
...ctx,
|
|
25
|
+
credentials,
|
|
26
|
+
verificationId: id,
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.then(prompt(ctx => ({
|
|
30
|
+
code: {
|
|
31
|
+
type: 'input',
|
|
32
|
+
message: `Please enter the verification code that was sent to ${chalk.bold(ctx.email)}`,
|
|
33
|
+
validate: async (code) => {
|
|
34
|
+
try {
|
|
35
|
+
await ctx.api.service('verifications/email').update(ctx.verificationId, { code }, {
|
|
36
|
+
headers: {
|
|
37
|
+
Authorization: `Bearer ${ctx.credentials}`,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return error.message;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
})));
|
|
48
|
+
}
|
|
49
|
+
export async function checkLogin(ctx) {
|
|
50
|
+
try {
|
|
51
|
+
const authorization = await ctx.auth.getHeader();
|
|
52
|
+
const user = await ctx.auth.getUser();
|
|
53
|
+
if (ctx.email && ctx.email !== user?.email) {
|
|
54
|
+
throw new Error(`You are already logged in as ${user?.email}. Please log out first.`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
...ctx,
|
|
58
|
+
user,
|
|
59
|
+
authorization,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof LoginRequiredError) {
|
|
64
|
+
return performLogin(ctx).then(checkLogin);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { fromFile, loadJSON, prompt } from '@featherscloud/pinion';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { withOrganization } from './with-organization.js';
|
|
4
|
+
export async function createApplication(init) {
|
|
5
|
+
return Promise.resolve(init)
|
|
6
|
+
.then(loadJSON(fromFile('package.json'), pkg => ({ pkg }), {}))
|
|
7
|
+
.then(prompt(ctx => ({
|
|
8
|
+
appName: {
|
|
9
|
+
type: 'input',
|
|
10
|
+
message: 'What is the name of your application?',
|
|
11
|
+
default: ctx.pkg.name,
|
|
12
|
+
},
|
|
13
|
+
referers: {
|
|
14
|
+
type: 'input',
|
|
15
|
+
message: 'What are the allowed referers?',
|
|
16
|
+
suffix: chalk.white(' A comma separated list of URLs where your frontend is running'),
|
|
17
|
+
default: 'http://localhost:3030',
|
|
18
|
+
},
|
|
19
|
+
})))
|
|
20
|
+
.then(async (ctx) => {
|
|
21
|
+
const application = await ctx.api.service('applications').create({
|
|
22
|
+
name: ctx.appName,
|
|
23
|
+
organizationId: ctx.selectedOrganization.id,
|
|
24
|
+
referrers: ctx.referers.split(',').map(r => r.trim()),
|
|
25
|
+
}, {
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: ctx.authorization,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
...ctx,
|
|
32
|
+
applications: [application],
|
|
33
|
+
selectedApplication: application,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export async function withApplication(init) {
|
|
38
|
+
return Promise.resolve(init)
|
|
39
|
+
.then(withOrganization)
|
|
40
|
+
.then(async (ctx) => {
|
|
41
|
+
const { data: applications } = await ctx.api.service('applications').find({
|
|
42
|
+
query: {
|
|
43
|
+
organizationId: ctx.selectedOrganization.id,
|
|
44
|
+
},
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: ctx.authorization,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
...ctx,
|
|
51
|
+
applications,
|
|
52
|
+
};
|
|
53
|
+
})
|
|
54
|
+
.then(prompt(ctx => ({
|
|
55
|
+
selectedApplication: {
|
|
56
|
+
type: 'list',
|
|
57
|
+
message: 'Which application do you want to use?',
|
|
58
|
+
when: ctx.applications.length > 0,
|
|
59
|
+
choices: [
|
|
60
|
+
{
|
|
61
|
+
name: 'Create a new application',
|
|
62
|
+
value: '',
|
|
63
|
+
},
|
|
64
|
+
...ctx.applications.map(app => ({
|
|
65
|
+
name: app.name,
|
|
66
|
+
value: app.id,
|
|
67
|
+
})),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
})))
|
|
71
|
+
.then(async (ctx) => {
|
|
72
|
+
if (!ctx.selectedApplication) {
|
|
73
|
+
return createApplication(ctx);
|
|
74
|
+
}
|
|
75
|
+
const app = ctx.applications.find(a => a.id === ctx.selectedApplication);
|
|
76
|
+
return {
|
|
77
|
+
...ctx,
|
|
78
|
+
selectedApplication: app,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { prompt } from '@featherscloud/pinion';
|
|
2
|
+
import { checkLogin } from './check-login.js';
|
|
3
|
+
export async function createOrganization(init) {
|
|
4
|
+
return Promise.resolve(init)
|
|
5
|
+
.then(prompt(ctx => ({
|
|
6
|
+
orgName: {
|
|
7
|
+
type: 'input',
|
|
8
|
+
message: 'What is the name of your organization?',
|
|
9
|
+
default: ctx.user?.email && `${ctx.user.email.split('@')[0]}'s organization`,
|
|
10
|
+
},
|
|
11
|
+
replyToEmail: {
|
|
12
|
+
type: 'input',
|
|
13
|
+
message: 'What is the email address for your organization?',
|
|
14
|
+
default: ctx.user?.email,
|
|
15
|
+
},
|
|
16
|
+
})))
|
|
17
|
+
.then(async (ctx) => {
|
|
18
|
+
const { orgName: name, replyToEmail } = ctx;
|
|
19
|
+
const organization = await ctx.api.service('organizations').create({ name, replyToEmail }, {
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: ctx.authorization,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
...ctx,
|
|
26
|
+
organizations: [organization],
|
|
27
|
+
selectedOrganization: organization,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export async function withOrganization(init) {
|
|
32
|
+
return Promise.resolve(init)
|
|
33
|
+
.then(checkLogin)
|
|
34
|
+
.then(async (ctx) => {
|
|
35
|
+
const { data: organizations } = await ctx.api.service('organizations').find({
|
|
36
|
+
headers: {
|
|
37
|
+
Authorization: ctx.authorization,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
...ctx,
|
|
42
|
+
organizations,
|
|
43
|
+
};
|
|
44
|
+
})
|
|
45
|
+
.then(async (ctx) => {
|
|
46
|
+
if (ctx.organizations.length === 0) {
|
|
47
|
+
return createOrganization(ctx);
|
|
48
|
+
}
|
|
49
|
+
if (ctx.organizations.length > 1) {
|
|
50
|
+
return Promise.resolve(ctx)
|
|
51
|
+
.then(prompt(ctx => ({
|
|
52
|
+
selectedOrganization: {
|
|
53
|
+
type: 'list',
|
|
54
|
+
message: 'Which organizations do you want to use?',
|
|
55
|
+
choices: ctx.organizations.map(organization => ({
|
|
56
|
+
name: organization.name,
|
|
57
|
+
value: organization.id,
|
|
58
|
+
})),
|
|
59
|
+
},
|
|
60
|
+
})))
|
|
61
|
+
.then((ctx) => {
|
|
62
|
+
const org = ctx.organizations.find(organization => organization.id === ctx.selectedOrganization);
|
|
63
|
+
return {
|
|
64
|
+
...ctx,
|
|
65
|
+
selectedOrganization: org,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
...ctx,
|
|
71
|
+
selectedOrganization: ctx.organizations[0],
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Command, commander } from '@featherscloud/pinion';
|
|
4
|
+
import { createFileStore } from '@feathersdev/auth';
|
|
5
|
+
import { app } from './actions/app.js';
|
|
6
|
+
import { integrate } from './actions/integrate.js';
|
|
7
|
+
import { login } from './actions/login.js';
|
|
8
|
+
import { logout } from './actions/logout.js';
|
|
9
|
+
import { server } from './actions/server.js';
|
|
10
|
+
import { getBaseContext, packageJson } from './base.js';
|
|
11
|
+
const __dirname = new URL('.', import.meta.url).pathname;
|
|
12
|
+
const apiUrl = 'https://api.feathers.dev';
|
|
13
|
+
const referer = 'https://app.feathers.dev';
|
|
14
|
+
const appId = 'did:key:z6MkheUYC6euEMuZjL84CixXgRmPfciuYXhz51D85WT3fC5T';
|
|
15
|
+
// const apiUrl = 'http://localhost:8787'
|
|
16
|
+
// const referer = 'http://localhost:3000'
|
|
17
|
+
// const appId = ''
|
|
18
|
+
const store = createFileStore(fs, path.join(__dirname, '..', '.auth.json'));
|
|
19
|
+
export const options = {
|
|
20
|
+
apiUrl,
|
|
21
|
+
referer,
|
|
22
|
+
appId,
|
|
23
|
+
store,
|
|
24
|
+
};
|
|
25
|
+
export function generate(initialContext) {
|
|
26
|
+
const program = new Command()
|
|
27
|
+
.description('The feathers.dev CLI')
|
|
28
|
+
.option('-e, --email <email>', 'Email address to log in with')
|
|
29
|
+
.option('-p, --platform <platform>', 'The server platform to use (node, deno, bun, cloudflare, express)')
|
|
30
|
+
.option('-f, --framework <framework>', 'The frontend framework to use (react, vue, svelte, vanilla)')
|
|
31
|
+
.version(packageJson.version)
|
|
32
|
+
.showHelpAfterError();
|
|
33
|
+
const init = getBaseContext(options, initialContext).then(commander(program));
|
|
34
|
+
program
|
|
35
|
+
.command('app')
|
|
36
|
+
.description('Create a new feathers.dev application')
|
|
37
|
+
.action(() => init.then(app));
|
|
38
|
+
program
|
|
39
|
+
.command('server')
|
|
40
|
+
.description('Create an example NodeJS, Deno, Bun or Cloudflare Worker server')
|
|
41
|
+
.action(() => init.then(server));
|
|
42
|
+
program
|
|
43
|
+
.command('integrate')
|
|
44
|
+
.description('Integrate feathers.dev into an existing frontend or server')
|
|
45
|
+
.action(() => init.then(integrate));
|
|
46
|
+
program
|
|
47
|
+
.command('login')
|
|
48
|
+
.description('Log in with your email address')
|
|
49
|
+
.action(() => init.then(login));
|
|
50
|
+
program
|
|
51
|
+
.command('logout')
|
|
52
|
+
.description('Log out from the CLI')
|
|
53
|
+
.action(() => init.then(logout));
|
|
54
|
+
return program;
|
|
55
|
+
}
|
package/esm/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "module" }
|