@appsemble/cli 0.37.4 → 0.37.6

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.37.4/config/assets/logo.svg) Appsemble CLI
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.37.6/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.37.4/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.37.4)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.37.6/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.37.6)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)
8
8
 
9
9
  ## Table of Contents
@@ -342,5 +342,5 @@ appsemble run-cronjobs --interval 30
342
342
 
343
343
  ## License
344
344
 
345
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.37.4/LICENSE.md) ©
345
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.37.6/LICENSE.md) ©
346
346
  [Appsemble](https://appsemble.com)
package/commands/serve.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { createWriteStream, existsSync } from 'node:fs';
2
2
  import { mkdir, readFile } from 'node:fs/promises';
3
3
  import http from 'node:http';
4
- import { basename, extname, join, parse } from 'node:path';
4
+ import { basename, dirname, extname, join, parse } from 'node:path';
5
5
  import { Readable } from 'node:stream';
6
+ import { pipeline } from 'node:stream/promises';
6
7
  import { getAppBlocks, getAppRoles, normalize, parseBlockName, } from '@appsemble/lang-sdk';
7
- import { AppsembleError, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
8
+ import { AppsembleError, getBlockAssetDownloadUrl, isValidBlockAssetFilename, logger, opendirSafe, readData, writeData, } from '@appsemble/node-utils';
8
9
  import { asciiLogo } from '@appsemble/utils';
9
10
  import axios from 'axios';
10
11
  import csvToJson from 'csvtojson';
@@ -132,29 +133,36 @@ export async function handler(argv) {
132
133
  const cachedBlockManifest = join(blockCacheDir, 'manifest.json');
133
134
  const cacheExists = existsSync(cachedBlockManifest);
134
135
  if (!cacheExists || (cacheExists && argv['overwrite-block-cache'])) {
135
- const blockUrl = `/api/blocks/@${organization}/${blockName}/versions/${identifiableBlock.version}`;
136
+ const blockUrl = String(new URL(`/api/blocks/@${organization}/${blockName}/versions/${identifiableBlock.version}`, argv.remote));
136
137
  try {
137
- const { data: blockManifest } = await axios.get(String(new URL(blockUrl, argv.remote)));
138
- await writeData(cachedBlockManifest, blockManifest);
138
+ const { data: blockManifest } = await axios.get(blockUrl);
139
139
  const assetsDir = join(blockCacheDir, 'assets');
140
140
  if (!existsSync(assetsDir)) {
141
141
  await mkdir(assetsDir);
142
142
  }
143
143
  const blockFilesPromises = blockManifest.files.map(async (filename) => {
144
- const writer = createWriteStream(join(assetsDir, filename));
145
- const { data: content } = await axios.get(String(new URL(`${blockUrl}/asset?filename=${filename}`, argv.remote)), {
144
+ if (!isValidBlockAssetFilename(filename)) {
145
+ throw new AppsembleError(`Invalid block asset filename: ${filename}`);
146
+ }
147
+ const target = join(assetsDir, filename);
148
+ await mkdir(dirname(target), { recursive: true });
149
+ const { data: content } = await axios.get(getBlockAssetDownloadUrl(blockUrl, blockManifest.fileUrls, filename), {
146
150
  responseType: 'stream',
147
151
  });
148
- content.pipe(writer);
152
+ await pipeline(content, createWriteStream(target));
149
153
  });
150
154
  await Promise.all(blockFilesPromises);
151
- return blockManifest;
155
+ const localBlockManifest = { ...blockManifest };
156
+ delete localBlockManifest.fileUrls;
157
+ await writeData(cachedBlockManifest, localBlockManifest);
158
+ return localBlockManifest;
152
159
  }
153
160
  catch {
154
161
  throw new AppsembleError(`The server was unable to fetch the "${blockName}" block\nThis could be due to a misconfigured remote\nMake sure the passed remote supports fetching blocks such as https://appsemble.app`);
155
162
  }
156
163
  }
157
- const [blockManifest] = await readData(cachedBlockManifest);
164
+ const [blockManifest] = (await readData(cachedBlockManifest));
165
+ delete blockManifest.fileUrls;
158
166
  return blockManifest;
159
167
  });
160
168
  const localBlocks = await Promise.all(localBlocksPromises);
package/commands/start.js CHANGED
@@ -139,6 +139,9 @@ export function builder(yargs) {
139
139
  })
140
140
  .option('s3-secret-key', {
141
141
  desc: 'The secret key of the Amazon S3 compatible object storage server',
142
+ })
143
+ .option('block-assets-base-url', {
144
+ desc: 'The base URL for block assets stored in S3 compatible object storage',
142
145
  })
143
146
  .option('valkey-host', {
144
147
  desc: 'The host of the Valkey server to connect to.',
@@ -1,5 +1,5 @@
1
1
  import { hostname } from 'node:os';
2
- import { AppsembleError, assertKoaCondition, getKeytar, getService, logger, throwKoaError, } from '@appsemble/node-utils';
2
+ import { AppsembleError, assertKoaCondition, getKeyring, getService, logger, throwKoaError, } from '@appsemble/node-utils';
3
3
  import { checkbox } from '@inquirer/prompts';
4
4
  import Koa from 'koa';
5
5
  import open from 'open';
@@ -43,7 +43,7 @@ function waitForCredentials(url) {
43
43
  });
44
44
  }
45
45
  export async function login({ clientCredentials, remote }) {
46
- const { setPassword } = await getKeytar();
46
+ const { setPassword } = await getKeyring();
47
47
  const url = new URL('/settings/client-credentials', remote);
48
48
  let credentials = clientCredentials;
49
49
  if (credentials) {
@@ -61,7 +61,7 @@ export async function login({ clientCredentials, remote }) {
61
61
  logger.info(`Successfully stored credentials for ${clientId} 🕶`);
62
62
  }
63
63
  export async function remove({ remote }) {
64
- const { deletePassword, findCredentials } = await getKeytar();
64
+ const { deletePassword, findCredentials } = await getKeyring();
65
65
  const choices = await findCredentials(getService(remote));
66
66
  if (choices.length === 0) {
67
67
  logger.warn('No client credentials are currently in use.');
package/lib/config.js CHANGED
@@ -407,7 +407,7 @@ export async function getProjectWebpackConfig(buildConfig, mode, outputPath) {
407
407
  config.output = config.output || {};
408
408
  config.output.path = outputPath || publicPath;
409
409
  logger.verbose(`Patched webpack config output.path to ${config.output.path}`);
410
- config.output.publicPath = publicPath;
410
+ config.output.publicPath = mode === 'production' ? 'auto' : publicPath;
411
411
  logger.verbose(`Patched webpack config output.publicPath to ${config.output.publicPath}`);
412
412
  return config;
413
413
  }
@@ -40,7 +40,9 @@ export function createApiServer({ context }) {
40
40
  app.use(compose([
41
41
  conditional((ctx) => ctx.path.startsWith('/api') ||
42
42
  ctx.path === '/auth/oauth2/token' ||
43
- /\/apps\/\d+\/auth\/oauth2\/token/.test(ctx.path), cors()),
43
+ /\/apps\/\d+\/auth\/oauth2\/token/.test(ctx.path),
44
+ // Reflect the origin instead of '*', so credentialed app requests pass CORS.
45
+ cors({ origin: (ctx) => ctx.get('Origin') || '*', credentials: true })),
44
46
  koas(api(version, argv), [
45
47
  parameters(),
46
48
  bodyParser(),
package/lib/processCss.js CHANGED
@@ -2,7 +2,28 @@ import { readFile } from 'node:fs/promises';
2
2
  import postcss from 'postcss';
3
3
  import postcssImport from 'postcss-import';
4
4
  import postcssrc from 'postcss-load-config';
5
+ import postcssPresetEnv from 'postcss-preset-env';
5
6
  import postcssUrl from 'postcss-url';
7
+ /**
8
+ * Load the PostCSS plugins to process app CSS with.
9
+ *
10
+ * The PostCSS config of the project is used if it has one. Otherwise the CSS is processed with the
11
+ * Appsemble CLI's default PostCSS preset.
12
+ *
13
+ * @returns The PostCSS plugins to use.
14
+ */
15
+ async function loadPlugins() {
16
+ try {
17
+ const { plugins } = await postcssrc();
18
+ return plugins;
19
+ }
20
+ catch (error) {
21
+ if (!error.message?.startsWith('No PostCSS Config found')) {
22
+ throw error;
23
+ }
24
+ return [postcssPresetEnv({ stage: 0 })];
25
+ }
26
+ }
6
27
  /**
7
28
  * Verifies and processes a CSS file using PostCSS.
8
29
  *
@@ -11,8 +32,7 @@ import postcssUrl from 'postcss-url';
11
32
  */
12
33
  export async function processCss(path) {
13
34
  const data = await readFile(path, 'utf8');
14
- const postcssConfig = await postcssrc();
15
- const postCss = postcss(postcssConfig.plugins);
35
+ const postCss = postcss(await loadPlugins());
16
36
  postCss.use(postcssUrl({ url: 'inline' }));
17
37
  postCss.use(postcssImport({ plugins: postCss.plugins }));
18
38
  const { css } = await postCss.process(data, { from: path, to: undefined });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/cli",
3
- "version": "0.37.4",
3
+ "version": "0.37.6",
4
4
  "description": "The CLI for developing with Appsemble apps and blocks",
5
5
  "keywords": [
6
6
  "app",
@@ -44,10 +44,10 @@
44
44
  "test": "vitest"
45
45
  },
46
46
  "dependencies": {
47
- "@appsemble/node-utils": "0.37.4",
48
- "@appsemble/types": "0.37.4",
49
- "@appsemble/lang-sdk": "0.37.4",
50
- "@appsemble/utils": "0.37.4",
47
+ "@appsemble/node-utils": "0.37.6",
48
+ "@appsemble/types": "0.37.6",
49
+ "@appsemble/lang-sdk": "0.37.6",
50
+ "@appsemble/utils": "0.37.6",
51
51
  "@fortawesome/fontawesome-common-types": "^6.0.0",
52
52
  "@inquirer/prompts": "^8.0.0",
53
53
  "@koa/cors": "^5.0.0",
@@ -76,6 +76,7 @@
76
76
  "postcss": "^8.0.0",
77
77
  "postcss-import": "^15.0.0",
78
78
  "postcss-load-config": "^4.0.0",
79
+ "postcss-preset-env": "^9.0.0",
79
80
  "postcss-url": "^10.0.0",
80
81
  "prettier": "^3.0.0",
81
82
  "raw-body": "^2.0.0",
@@ -89,8 +90,8 @@
89
90
  "yargs": "^17.0.0"
90
91
  },
91
92
  "devDependencies": {
92
- "@appsemble/types": "0.37.4",
93
- "@appsemble/server": "0.37.4",
93
+ "@appsemble/types": "0.37.6",
94
+ "@appsemble/server": "0.37.6",
94
95
  "@types/concat-stream": "2.0.3",
95
96
  "@types/koa__cors": "5.0.1",
96
97
  "@types/koa-compress": "4.0.7",
@@ -107,11 +108,11 @@
107
108
  "sequelize": "6.37.8",
108
109
  "titleize": "4.0.0",
109
110
  "untildify": "6.0.0",
110
- "vitest": "2.1.9",
111
+ "vitest": "4.1.10",
111
112
  "yoctocolors-cjs": "2.1.3"
112
113
  },
113
114
  "optionalDependencies": {
114
- "keytar": "^7.0.0"
115
+ "@napi-rs/keyring": "^1.3.0"
115
116
  },
116
117
  "engines": {
117
118
  "node": ">=24"
@@ -1,10 +1,10 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { parseBlockName } from '@appsemble/lang-sdk';
4
- import { opendirSafe, } from '@appsemble/node-utils';
4
+ import { opendirSafe, replaceAssetFunctions, } from '@appsemble/node-utils';
5
5
  import { processCss } from '../../lib/processCss.js';
6
6
  export async function getAppBlockStyles({ context, name, }) {
7
- const { appPath } = context;
7
+ const { apiUrl, appPath, appsembleApp } = context;
8
8
  const [, blockName] = parseBlockName(name);
9
9
  let style = '';
10
10
  const themeDirPath = join(appPath, 'theme');
@@ -16,7 +16,8 @@ export async function getAppBlockStyles({ context, name, }) {
16
16
  if (themeStat.name.startsWith('@') && existsSync(blockDir)) {
17
17
  await opendirSafe(blockDir, async (file) => {
18
18
  if (file.endsWith('.css')) {
19
- style = await processCss(join(file));
19
+ const css = await processCss(join(file));
20
+ style = replaceAssetFunctions(css, appsembleApp.id, apiUrl);
20
21
  }
21
22
  });
22
23
  }
@@ -1,7 +1,10 @@
1
+ import { replaceAssetFunctions, } from '@appsemble/node-utils';
1
2
  export function getAppStyles({ context }) {
3
+ const { apiUrl, appsembleApp } = context;
4
+ const { coreStyle = '', id, sharedStyle = '' } = appsembleApp;
2
5
  return Promise.resolve({
3
- coreStyle: context.appsembleApp.coreStyle,
4
- sharedStyle: context.appsembleApp.sharedStyle,
6
+ coreStyle: replaceAssetFunctions(coreStyle, id, apiUrl),
7
+ sharedStyle: replaceAssetFunctions(sharedStyle, id, apiUrl),
5
8
  });
6
9
  }
7
10
  //# sourceMappingURL=getAppStyles.js.map
@@ -6,7 +6,7 @@ pages:
6
6
  - name: Example Page A
7
7
  blocks:
8
8
  - type: action-button
9
- version: 0.37.4
9
+ version: 0.37.6
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.37.4
20
+ version: 0.37.6
21
21
  parameters:
22
22
  icon: arrow-left
23
23
  actions:
@@ -5,7 +5,7 @@
5
5
  "*.css"
6
6
  ],
7
7
  "dependencies": {
8
- "@appsemble/sdk": "0.37.4",
8
+ "@appsemble/sdk": "0.37.6",
9
9
  "mini-jsx": "^4.0.0"
10
10
  }
11
11
  }
@@ -5,8 +5,8 @@
5
5
  "*.css"
6
6
  ],
7
7
  "dependencies": {
8
- "@appsemble/preact": "0.37.4",
9
- "@appsemble/sdk": "0.37.4",
8
+ "@appsemble/preact": "0.37.6",
9
+ "@appsemble/sdk": "0.37.6",
10
10
  "preact": "^10.0.0"
11
11
  }
12
12
  }
@@ -5,6 +5,6 @@
5
5
  "*.css"
6
6
  ],
7
7
  "dependencies": {
8
- "@appsemble/sdk": "0.37.4"
8
+ "@appsemble/sdk": "0.37.6"
9
9
  }
10
10
  }