@appsemble/cli 0.22.1 → 0.22.2

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 CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.22.1/config/assets/logo.svg) Appsemble CLI
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.22.2/config/assets/logo.svg) Appsemble CLI
2
2
 
3
3
  > Manage apps and blocks from the command line.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/cli)](https://www.npmjs.com/package/@appsemble/cli)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.22.1/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.22.1)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.22.2/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.22.2)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)
8
8
 
9
9
  ## Table of Contents
@@ -282,5 +282,5 @@ appsemble run-cronjobs --interval 30
282
282
 
283
283
  ## License
284
284
 
285
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.22.1/LICENSE.md) ©
285
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.22.2/LICENSE.md) ©
286
286
  [Appsemble](https://appsemble.com)
@@ -1,8 +1,7 @@
1
- import { AppsembleError, logger } from '@appsemble/node-utils';
1
+ import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
2
2
  import fg from 'fast-glob';
3
3
  import normalizePath from 'normalize-path';
4
4
  import { updateApp } from '../../lib/app.js';
5
- import { authenticate } from '../../lib/authentication.js';
6
5
  import { coerceFile } from '../../lib/coercers.js';
7
6
  export const command = 'update <paths...>';
8
7
  export const description = 'Update an app based on a specified YAML file or directory.';
@@ -1,10 +1,9 @@
1
1
  import { parse } from 'node:path';
2
- import { AppsembleError, logger } from '@appsemble/node-utils';
2
+ import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
3
3
  import fg from 'fast-glob';
4
4
  import normalizePath from 'normalize-path';
5
5
  import { resolveAppIdAndRemote } from '../../lib/app.js';
6
6
  import { publishAsset } from '../../lib/asset.js';
7
- import { authenticate } from '../../lib/authentication.js';
8
7
  export const command = 'publish <paths...>';
9
8
  export const description = 'Publish assets for an existing app.';
10
9
  export function builder(yargs) {
@@ -1,5 +1,4 @@
1
- import { logger } from '@appsemble/node-utils';
2
- import { authenticate } from '../../lib/authentication.js';
1
+ import { authenticate, logger } from '@appsemble/node-utils';
3
2
  import { deleteBlock } from '../../lib/block.js';
4
3
  export const command = 'delete <block>';
5
4
  export const description = 'Delete a specific block version. Using this outside local development is discouraged.';
@@ -1,7 +1,6 @@
1
- import { logger } from '@appsemble/node-utils';
1
+ import { authenticate, logger } from '@appsemble/node-utils';
2
2
  import fg from 'fast-glob';
3
3
  import normalizePath from 'normalize-path';
4
- import { authenticate } from '../../lib/authentication.js';
5
4
  import { buildBlock, getBlockConfig, publishBlock } from '../../lib/block.js';
6
5
  export const command = 'publish <paths...>';
7
6
  export const description = 'Publish a block.';
@@ -1,4 +1,4 @@
1
- import { authenticate } from '../../lib/authentication.js';
1
+ import { authenticate } from '@appsemble/node-utils';
2
2
  import { coerceFile } from '../../lib/coercers.js';
3
3
  import { createOrganization } from '../../lib/organization.js';
4
4
  export const command = 'create <id>';
@@ -1,5 +1,6 @@
1
1
  import * as create from './create.js';
2
2
  import * as update from './update.js';
3
+ import * as upsert from './upsert.js';
3
4
  export { noop as handler } from '@appsemble/utils';
4
5
  export const command = 'organization';
5
6
  export const description = 'Commands related to organizations.';
@@ -7,6 +8,7 @@ export function builder(yargs) {
7
8
  return yargs
8
9
  .command(create)
9
10
  .command(update)
11
+ .command(upsert)
10
12
  .demandCommand(1);
11
13
  }
12
14
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { authenticate } from '../../lib/authentication.js';
1
+ import { authenticate } from '@appsemble/node-utils';
2
2
  import { coerceFile } from '../../lib/coercers.js';
3
3
  import { updateOrganization } from '../../lib/organization.js';
4
4
  export const command = 'update <id>';
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { type ReadStream } from 'node:fs';
3
+ import { type Argv } from 'yargs';
4
+ import { type BaseArguments } from '../../types.js';
5
+ interface UpsertOrganizationArguments extends BaseArguments {
6
+ description: string;
7
+ email: string;
8
+ id: string;
9
+ name: string;
10
+ website: string;
11
+ icon: ReadStream;
12
+ }
13
+ export declare const command = "upsert <id>";
14
+ export declare const description = "Create a new organization for the given id or update if there exists one already.";
15
+ export declare function builder(yargs: Argv): Argv<any>;
16
+ export declare function handler({ clientCredentials, description: desc, email, icon, id, name, remote, website, }: UpsertOrganizationArguments): Promise<void>;
17
+ export {};
@@ -0,0 +1,39 @@
1
+ import { authenticate } from '@appsemble/node-utils';
2
+ import { coerceFile } from '../../lib/coercers.js';
3
+ import { upsertOrganization } from '../../lib/organization.js';
4
+ export const command = 'upsert <id>';
5
+ export const description = 'Create a new organization for the given id or update if there exists one already.';
6
+ export function builder(yargs) {
7
+ return yargs
8
+ .positional('id', {
9
+ describe: 'Id of the organization to create or update',
10
+ })
11
+ .option('name', {
12
+ describe: 'The name of the organization',
13
+ })
14
+ .option('email', {
15
+ describe: 'The email address users may use to contact the organization',
16
+ })
17
+ .option('website', {
18
+ describe: 'The website of the organization',
19
+ })
20
+ .option('description', {
21
+ describe: 'A short of the organization',
22
+ })
23
+ .option('icon', {
24
+ describe: 'The file location of the icon representing the organization.',
25
+ coerce: coerceFile,
26
+ });
27
+ }
28
+ export async function handler({ clientCredentials, description: desc, email, icon, id, name, remote, website, }) {
29
+ await authenticate(remote, 'organizations:write', clientCredentials);
30
+ await upsertOrganization({
31
+ description: desc,
32
+ email,
33
+ icon,
34
+ id,
35
+ name,
36
+ website,
37
+ });
38
+ }
39
+ //# sourceMappingURL=upsert.js.map
@@ -1,8 +1,7 @@
1
- import { AppsembleError, logger } from '@appsemble/node-utils';
1
+ import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
2
2
  import fg from 'fast-glob';
3
3
  import normalizePath from 'normalize-path';
4
4
  import { resolveAppIdAndRemote } from '../../lib/app.js';
5
- import { authenticate } from '../../lib/authentication.js';
6
5
  import { publishResource } from '../../lib/resource.js';
7
6
  export const command = 'publish <resource-name> <paths...>';
8
7
  export const description = 'Publish resources based on a specified JSON file or directory.';
@@ -1,8 +1,7 @@
1
- import { AppsembleError, logger } from '@appsemble/node-utils';
1
+ import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
2
2
  import fg from 'fast-glob';
3
3
  import normalizePath from 'normalize-path';
4
4
  import { resolveAppIdAndRemote } from '../../lib/app.js';
5
- import { authenticate } from '../../lib/authentication.js';
6
5
  import { updateResource } from '../../lib/resource.js';
7
6
  export const command = 'update <resource-name> <paths...>';
8
7
  export const description = 'Update resources based on a specified JSON file or directory. Entries without the property `id` will be skipped.';
@@ -1,5 +1,5 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { resolveAppIdAndRemote } from '../../lib/app.js';
2
- import { authenticate } from '../../lib/authentication.js';
3
3
  import { createTeam } from '../../lib/team.js';
4
4
  export const command = 'create <name>';
5
5
  export const description = 'Create a new team for an app.';
@@ -1,5 +1,5 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { resolveAppIdAndRemote } from '../../lib/app.js';
2
- import { authenticate } from '../../lib/authentication.js';
3
3
  import { deleteTeam } from '../../lib/team.js';
4
4
  export const command = 'delete';
5
5
  export const description = 'Delete an existing team from an app.';
@@ -1,5 +1,5 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { resolveAppIdAndRemote } from '../../../lib/app.js';
2
- import { authenticate } from '../../../lib/authentication.js';
3
3
  import { deleteMember } from '../../../lib/team.js';
4
4
  export const command = 'delete <user>';
5
5
  export const description = 'Delete a new member to an existing team from an app.';
@@ -1,5 +1,5 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { resolveAppIdAndRemote } from '../../../lib/app.js';
2
- import { authenticate } from '../../../lib/authentication.js';
3
3
  import { inviteMember } from '../../../lib/team.js';
4
4
  export const command = 'invite <user>';
5
5
  export const description = 'Invite a new member to an existing team from an app.';
@@ -1,6 +1,6 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { TeamRole } from '@appsemble/utils';
2
3
  import { resolveAppIdAndRemote } from '../../../lib/app.js';
3
- import { authenticate } from '../../../lib/authentication.js';
4
4
  import { updateMember } from '../../../lib/team.js';
5
5
  export const command = 'update <user> <role>';
6
6
  export const description = 'Update an existing team member.';
@@ -1,5 +1,5 @@
1
+ import { authenticate } from '@appsemble/node-utils';
1
2
  import { resolveAppIdAndRemote } from '../../lib/app.js';
2
- import { authenticate } from '../../lib/authentication.js';
3
3
  import { updateTeam } from '../../lib/team.js';
4
4
  export const command = 'update';
5
5
  export const description = 'Update an existing team for an app.';
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import process from 'node:process';
3
- import { configureLogger, handleError, version } from '@appsemble/node-utils';
3
+ import { configureLogger, CREDENTIALS_ENV_VAR, handleError, version } from '@appsemble/node-utils';
4
4
  import { cosmiconfig } from 'cosmiconfig';
5
5
  import yargs from 'yargs';
6
6
  import * as app from './commands/app/index.js';
@@ -17,7 +17,6 @@ import * as runCronJobs from './commands/runCronJobs.js';
17
17
  import * as serve from './commands/serve.js';
18
18
  import * as start from './commands/start.js';
19
19
  import * as team from './commands/team/index.js';
20
- import { CREDENTIALS_ENV_VAR } from './lib/authentication.js';
21
20
  import { coerceRemote } from './lib/coercers.js';
22
21
  import { initAxios } from './lib/initAxios.js';
23
22
  process.title = 'appsemble';
package/lib/app.js CHANGED
@@ -2,11 +2,10 @@ import { createReadStream, existsSync } from 'node:fs';
2
2
  import { mkdir, readdir, readFile, stat } from 'node:fs/promises';
3
3
  import { basename, join, parse, relative, resolve } from 'node:path';
4
4
  import { inspect } from 'node:util';
5
- import { AppsembleError, logger, opendirSafe, readData, writeData } from '@appsemble/node-utils';
5
+ import { AppsembleError, authenticate, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
6
6
  import { extractAppMessages, has, normalizeBlockName } from '@appsemble/utils';
7
7
  import axios from 'axios';
8
8
  import FormData from 'form-data';
9
- import { authenticate } from './authentication.js';
10
9
  import { traverseBlockThemes } from './block.js';
11
10
  import { coerceRemote } from './coercers.js';
12
11
  import { printAxiosError } from './output.js';
@@ -1,13 +1,3 @@
1
1
  import { type BaseArguments } from '../types.js';
2
- export declare const CREDENTIALS_ENV_VAR = "APPSEMBLE_CLIENT_CREDENTIALS";
3
2
  export declare function login({ clientCredentials, remote }: BaseArguments): Promise<void>;
4
3
  export declare function remove({ remote }: BaseArguments): Promise<void>;
5
- /**
6
- * Login to the server using OAuth2 client credentials.
7
- *
8
- * @param remote Host to fetch token from.
9
- * @param scope The OAuth2 scope to request. This may be space separated to request
10
- * multiple scopes.
11
- * @param inputCredentials Client credentials passed from the command line.
12
- */
13
- export declare function authenticate(remote: string, scope: string, inputCredentials: string): Promise<void>;
@@ -1,18 +1,12 @@
1
1
  import { hostname } from 'node:os';
2
- import { AppsembleError, logger } from '@appsemble/node-utils';
3
- import axios from 'axios';
2
+ import { AppsembleError, getKeytar, getService, logger } from '@appsemble/node-utils';
4
3
  import inquirer from 'inquirer';
5
4
  import Koa from 'koa';
6
5
  import open from 'open';
7
6
  import raw from 'raw-body';
8
- export const CREDENTIALS_ENV_VAR = 'APPSEMBLE_CLIENT_CREDENTIALS';
9
- const authorizedRemotes = new Set();
10
7
  function validate(credentials) {
11
8
  return /.+:.+/.test(credentials);
12
9
  }
13
- function getService(remote) {
14
- return `appsemble://${new URL(remote).host}`;
15
- }
16
10
  function waitForCredentials(url) {
17
11
  return new Promise((resolve) => {
18
12
  const app = new Koa();
@@ -48,45 +42,6 @@ function waitForCredentials(url) {
48
42
  open(String(url));
49
43
  });
50
44
  }
51
- async function getKeytar() {
52
- try {
53
- const { default: keytar } = await import('keytar');
54
- return keytar;
55
- }
56
- catch {
57
- throw new AppsembleError('Couldn’t find module keytar. Either install libsecret and reinstall @appsemble/cli, or pass --client-credentials on the command line.');
58
- }
59
- }
60
- async function getClientCredentials(remote, inputCredentials) {
61
- if (inputCredentials) {
62
- return inputCredentials;
63
- }
64
- const envCredentials = process.env[CREDENTIALS_ENV_VAR];
65
- if (envCredentials) {
66
- logger.info(`Detected client credentials from ${CREDENTIALS_ENV_VAR} environment variable`);
67
- return envCredentials;
68
- }
69
- const { findCredentials } = await getKeytar();
70
- const choices = await findCredentials(getService(remote));
71
- if (choices.length === 0) {
72
- throw new AppsembleError(`No client credentials found. Register them using:\n\nappsemble login --remote ${remote}`);
73
- }
74
- let choice;
75
- if (choices.length === 1) {
76
- [choice] = choices;
77
- }
78
- else {
79
- ({ choice } = await inquirer.prompt([
80
- {
81
- name: 'choice',
82
- message: 'Select client id to use',
83
- choices: choices.map((value) => ({ name: value.account, value })),
84
- type: 'list',
85
- },
86
- ]));
87
- }
88
- return `${choice.account}:${choice.password}`;
89
- }
90
45
  export async function login({ clientCredentials, remote }) {
91
46
  const { setPassword } = await getKeytar();
92
47
  const url = new URL('/settings/client-credentials', remote);
@@ -125,33 +80,4 @@ export async function remove({ remote }) {
125
80
  logger.info(`Successfully delete client id ${clientId}`);
126
81
  }));
127
82
  }
128
- /**
129
- * Login to the server using OAuth2 client credentials.
130
- *
131
- * @param remote Host to fetch token from.
132
- * @param scope The OAuth2 scope to request. This may be space separated to request
133
- * multiple scopes.
134
- * @param inputCredentials Client credentials passed from the command line.
135
- */
136
- export async function authenticate(remote, scope, inputCredentials) {
137
- const credentials = await getClientCredentials(remote, inputCredentials);
138
- if (authorizedRemotes.has(remote)) {
139
- logger.verbose(`Already logged in to ${remote}`);
140
- return;
141
- }
142
- logger.verbose(`Logging in to ${remote}`);
143
- const { data } = await axios.post('/oauth2/token', new URLSearchParams({ grant_type: 'client_credentials', scope }), {
144
- headers: { authorization: `Basic ${Buffer.from(credentials).toString('base64')}` },
145
- baseURL: remote,
146
- });
147
- authorizedRemotes.add(remote);
148
- axios.interceptors.request.use((config) => {
149
- if (config.baseURL === remote) {
150
- config.headers.set('authorization', `Bearer ${data.access_token}`);
151
- }
152
- return config;
153
- });
154
- logger.info(`Logged in to ${remote} successfully`);
155
- logger.verbose(`Login scope: ${scope}`);
156
- }
157
83
  //# sourceMappingURL=authentication.js.map
@@ -1,14 +1,6 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import { type ReadStream } from 'node:fs';
3
- interface CreateOrganizationArguments {
4
- description: string;
5
- email: string;
6
- id: string;
7
- name: string;
8
- website: string;
9
- icon: ReadStream;
10
- }
11
- interface UpdateOrganizationArguments {
3
+ interface OrganizationArguments {
12
4
  description: string;
13
5
  email: string;
14
6
  id: string;
@@ -16,6 +8,7 @@ interface UpdateOrganizationArguments {
16
8
  name: string;
17
9
  website: string;
18
10
  }
19
- export declare function createOrganization({ description, email, icon, id, name, website, }: CreateOrganizationArguments): Promise<void>;
20
- export declare function updateOrganization({ description, email, icon, id, name, website, }: UpdateOrganizationArguments): Promise<void>;
11
+ export declare function createOrganization({ description, email, icon, id, name, website, }: OrganizationArguments): Promise<void>;
12
+ export declare function updateOrganization({ description, email, icon, id, name, website, }: OrganizationArguments): Promise<void>;
13
+ export declare function upsertOrganization({ description, email, icon, id, name, website, }: OrganizationArguments): Promise<void>;
21
14
  export {};
@@ -54,4 +54,18 @@ export async function updateOrganization({ description, email, icon, id, name, w
54
54
  await axios.patch(`/api/organizations/${id}`, formData);
55
55
  logger.info(`Successfully updated organization ${id}${name ? ` (${name})` : ''}`);
56
56
  }
57
+ export async function upsertOrganization({ description, email, icon, id, name, website, }) {
58
+ try {
59
+ await axios.get(`/api/organizations/${id}`);
60
+ updateOrganization({ description, email, icon, id, name, website });
61
+ }
62
+ catch (error) {
63
+ if (axios.isAxiosError(error) && error.response && error.response.status === 404) {
64
+ createOrganization({ description, email, icon, id, name, website });
65
+ }
66
+ else {
67
+ logger.error(error);
68
+ }
69
+ }
70
+ }
57
71
  //# sourceMappingURL=organization.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/cli",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "description": "The CLI for developing with Appsemble apps and blocks",
5
5
  "keywords": [
6
6
  "app",
@@ -42,9 +42,9 @@
42
42
  "test": "vitest"
43
43
  },
44
44
  "dependencies": {
45
- "@appsemble/node-utils": "0.22.1",
46
- "@appsemble/types": "0.22.1",
47
- "@appsemble/utils": "0.22.1",
45
+ "@appsemble/node-utils": "0.22.2",
46
+ "@appsemble/types": "0.22.2",
47
+ "@appsemble/utils": "0.22.2",
48
48
  "@fortawesome/fontawesome-common-types": "^6.0.0",
49
49
  "@koa/cors": "^4.0.0",
50
50
  "@odata/parser": "^0.2.0",
@@ -85,7 +85,7 @@
85
85
  "yargs": "^17.0.0"
86
86
  },
87
87
  "devDependencies": {
88
- "@appsemble/types": "0.22.1",
88
+ "@appsemble/types": "0.22.2",
89
89
  "@types/concat-stream": "^2.0.0",
90
90
  "@types/inquirer": "^9.0.0",
91
91
  "@types/normalize-path": "^3.0.0",
@@ -6,7 +6,7 @@ pages:
6
6
  - name: Example Page A
7
7
  blocks:
8
8
  - type: action-button
9
- version: 0.22.1
9
+ version: 0.22.2
10
10
  parameters:
11
11
  icon: arrow-right
12
12
  actions:
@@ -17,7 +17,7 @@ pages:
17
17
  - name: Example Page B
18
18
  blocks:
19
19
  - type: action-button
20
- version: 0.22.1
20
+ version: 0.22.2
21
21
  parameters:
22
22
  icon: arrow-left
23
23
  actions:
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/sdk": "0.22.1",
5
+ "@appsemble/sdk": "0.22.2",
6
6
  "mini-jsx": "^3.0.0"
7
7
  }
8
8
  }
@@ -2,8 +2,8 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/preact": "0.22.1",
6
- "@appsemble/sdk": "0.22.1",
5
+ "@appsemble/preact": "0.22.2",
6
+ "@appsemble/sdk": "0.22.2",
7
7
  "preact": "^10.0.0"
8
8
  }
9
9
  }
@@ -2,6 +2,6 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@appsemble/sdk": "0.22.1"
5
+ "@appsemble/sdk": "0.22.2"
6
6
  }
7
7
  }