@blocklet/meta 1.7.9 → 1.7.12

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/lib/constants.js CHANGED
@@ -22,6 +22,7 @@ const BlockletStatus = Object.freeze({
22
22
  restarting: 11, // Deprecated
23
23
  corrupted: 12,
24
24
  waiting: 13,
25
+ deleted: 14,
25
26
  });
26
27
 
27
28
  const fromBlockletStatus = fromEntry(BlockletStatus);
@@ -173,8 +174,8 @@ module.exports = Object.freeze({
173
174
 
174
175
  BLOCKLET_DEFAULT_VERSION: '1.0.0',
175
176
 
176
- BLOCKLET_LATEST_SPEC_VERSION: '1.2.4',
177
- BLOCKLET_LATEST_REQUIREMENT_SERVER: '>=1.6.2',
177
+ BLOCKLET_LATEST_SPEC_VERSION: '1.2.7',
178
+ BLOCKLET_LATEST_REQUIREMENT_SERVER: '>=1.7.0',
178
179
  BLOCKLET_LATEST_REQUIREMENT_ABTNODE: '>=1.5.15', // Deprecated
179
180
 
180
181
  BLOCKLET_CONFIGURABLE_KEY: {
@@ -17,7 +17,7 @@ const parseNavigation = (navigation, blocklet, prefix = '/', level = 1) => {
17
17
  };
18
18
 
19
19
  if (nav.link) {
20
- item.link = normalizePathPrefix(`${prefix}${nav.link || '/'}`);
20
+ item.link = nav.link.startsWith('/') ? normalizePathPrefix(`${prefix}${nav.link || '/'}`) : nav.link;
21
21
  }
22
22
 
23
23
  if (level === 1) {
package/lib/schema.js CHANGED
@@ -24,11 +24,25 @@ const {
24
24
  } = require('./constants');
25
25
 
26
26
  const WELLKNOWN_PATH_PREFIX = '/.well-known';
27
- const MAX_TITLE_LENGTH = 48;
27
+ const MAX_TITLE_LENGTH = 22;
28
28
  const MAX_NAME_LENGTH = 32;
29
29
 
30
30
  const Joi = JOI.extend(semver).extend(semverRange).extend(fileExtension).extend(didExtension);
31
31
 
32
+ const titleSchema = Joi.string()
33
+ .trim()
34
+ .min(3)
35
+ .custom((value) => {
36
+ if (cjkLength(value) > MAX_TITLE_LENGTH) {
37
+ throw new Error(
38
+ `title length should not exceed ${MAX_TITLE_LENGTH} (see: https://www.npmjs.com/package/cjk-length)`
39
+ );
40
+ }
41
+
42
+ return value;
43
+ });
44
+ const descriptionSchema = Joi.string().trim().min(3).max(160);
45
+
32
46
  const environmentSchema = Joi.object({
33
47
  name: Joi.string().trim().required(),
34
48
  description: Joi.string().trim().required(),
@@ -149,6 +163,8 @@ const statsSchema = Joi.object({
149
163
 
150
164
  const childrenSchema = Joi.object({
151
165
  name: Joi.string().min(1).required(),
166
+ title: titleSchema,
167
+ description: descriptionSchema,
152
168
  resolved: Joi.alternatives().try(Joi.string().uri(), Joi.string()).required(),
153
169
  mountPoints: Joi.array() // deprecated
154
170
  .items(
@@ -189,18 +205,6 @@ const signatureSchema = Joi.object({
189
205
  delegation: Joi.string(),
190
206
  });
191
207
 
192
- const titleSchema = Joi.string()
193
- .trim()
194
- .min(3)
195
- .custom((value) => {
196
- if (cjkLength(value) > MAX_TITLE_LENGTH) {
197
- throw new Error(`Blocklet title is too long. Max length is ${MAX_TITLE_LENGTH}`);
198
- }
199
-
200
- return value;
201
- });
202
- const descriptionSchema = Joi.string().trim().min(3).max(160);
203
-
204
208
  const navigationItemSchema = Joi.object({
205
209
  title: Joi.string().required(),
206
210
  link: Joi.string(),
@@ -400,12 +404,6 @@ const createBlockletSchema = (
400
404
  // TODO: this field should be refactored in future version
401
405
  engine: Joi.alternatives().try(engineSchema, Joi.array().items(engineSchema)).optional(),
402
406
 
403
- // TODO: following fields only exist for backward compatibility
404
- publicUrl: Joi.string().optional().allow(''),
405
- adminUrl: Joi.string().optional().allow(''),
406
- configUrl: Joi.string().optional().allow(''),
407
- docUrl: Joi.string().optional().allow(''),
408
-
409
407
  // NOTE: following fields are appended by registry or bundling process
410
408
  screenshots: Joi.array().items(Joi.string().min(1)).optional().default([]),
411
409
  logoUrl: Joi.string().optional().allow(''),
@@ -422,13 +420,11 @@ const createBlockletSchema = (
422
420
  // navigation & theme
423
421
  navigation: navigationSchema,
424
422
  theme: themeSchema,
425
- })
426
- .rename('public_url', 'publicUrl', { ignoreUndefined: true, override: true })
427
- .rename('admin_url', 'adminUrl', { ignoreUndefined: true, override: true })
428
- .rename('config_url', 'configUrl', { ignoreUndefined: true, override: true })
429
- .rename('tags', 'keywords', { ignoreUndefined: true, override: true })
430
- .rename('requiredEnvironments', 'environments', { ignoreUndefined: true, override: true })
431
- .options({ stripUnknown: true, noDefaults: false, ...schemaOptions });
423
+
424
+ // NOTE: following fields only exist in blocklet server and cannot be set manually
425
+ bundleName: Joi.string(),
426
+ bundleDid: Joi.DID().trim(),
427
+ }).options({ stripUnknown: true, noDefaults: false, ...schemaOptions });
432
428
  };
433
429
 
434
430
  module.exports = {
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "auth",
3
+ "description": "Neat and easy to use user authentication and authorization service for blocklets",
4
+ "version": "1.0.0",
5
+ "schema": {
6
+ "JSONSchema": {
7
+ "title": "Config Auth Service",
8
+ "description": "Customize how Auth Service works",
9
+ "type": "object",
10
+ "required": ["profileFields", "webWalletUrl"],
11
+ "properties": {
12
+ "whoCanAccess": {
13
+ "type": "string",
14
+ "title": "Who can access the blocklet?",
15
+ "enum": ["owner", "invited", "all"],
16
+ "default": "all"
17
+ },
18
+ "profileFields": {
19
+ "type": "array",
20
+ "title": "What info do you want user to provide when login?",
21
+ "items": {
22
+ "type": "string",
23
+ "enum": ["fullName", "email", "avatar", "phone"]
24
+ },
25
+ "default": ["fullName", "email", "avatar"],
26
+ "uniqueItems": true
27
+ },
28
+ "allowSwitchProfile": {
29
+ "type": "boolean",
30
+ "title": "Does you app allow user to switch profile?",
31
+ "default": true
32
+ },
33
+ "webWalletUrl": {
34
+ "type": "string",
35
+ "title": "The URL of your preferred web wallet instance",
36
+ "pattern": "^https?://",
37
+ "default": "https://web.abtwallet.io"
38
+ },
39
+ "ignoreUrls": {
40
+ "type": "array",
41
+ "title": "Which URLs do not need to be protected. e.g: /public/**",
42
+ "items": {
43
+ "type": "string",
44
+ "minLength": 1
45
+ },
46
+ "default": []
47
+ },
48
+ "blockUnauthenticated": {
49
+ "type": "boolean",
50
+ "title": "Do you want Auth Service block unauthenticated requests for you?",
51
+ "default": false
52
+ },
53
+ "blockUnauthorized": {
54
+ "type": "boolean",
55
+ "title": "Do you want Auth Service block unauthorized requests for you?",
56
+ "default": false
57
+ }
58
+ }
59
+ },
60
+ "UISchema": {
61
+ "profileFields": {
62
+ "ui:widget": "checkboxes"
63
+ }
64
+ }
65
+ }
66
+ }
package/lib/service.js ADDED
@@ -0,0 +1,101 @@
1
+ const cloneDeep = require('lodash/cloneDeep');
2
+ const Ajv = require('ajv').default;
3
+
4
+ const { NODE_SERVICES } = require('@abtnode/constant');
5
+
6
+ const ajv = new Ajv({
7
+ useDefaults: true,
8
+ removeAdditional: 'all',
9
+ });
10
+
11
+ const SERVICES = {};
12
+
13
+ const setService = (meta) => {
14
+ const validate = ajv.compile(meta.schema.JSONSchema);
15
+
16
+ // parse empty custom config to get default config
17
+ const defaultConfig = {};
18
+ validate(defaultConfig);
19
+
20
+ SERVICES[meta.name] = {
21
+ meta,
22
+ validate,
23
+ defaultConfig,
24
+ };
25
+ };
26
+
27
+ setService(require('./service-configs/auth.json'));
28
+
29
+ // backward compatible
30
+ SERVICES[NODE_SERVICES.AUTH_SERVICE] = {
31
+ ...SERVICES[NODE_SERVICES.AUTH],
32
+ meta: {
33
+ ...SERVICES[NODE_SERVICES.AUTH].meta,
34
+ name: NODE_SERVICES.AUTH_SERVICE,
35
+ },
36
+ };
37
+
38
+ const getServiceMetas = ({ stringifySchema = false } = {}) => {
39
+ const list = Object.values(SERVICES).map(({ meta }) => {
40
+ const data = cloneDeep(meta);
41
+ if (stringifySchema) {
42
+ data.schema = JSON.stringify(meta.schema);
43
+ }
44
+ return data;
45
+ });
46
+
47
+ return list;
48
+ };
49
+
50
+ const getService = (serviceName) => {
51
+ if (!serviceName) {
52
+ throw new Error('service name should not be empty');
53
+ }
54
+
55
+ const service = SERVICES[serviceName];
56
+ if (!service) {
57
+ throw new Error(`service ${serviceName} does not exist`);
58
+ }
59
+
60
+ return service;
61
+ };
62
+
63
+ const getServiceConfig = (serviceName, customConfig, { validate } = {}) => {
64
+ const service = getService(serviceName);
65
+
66
+ const data = cloneDeep(customConfig || {});
67
+ // this method may have side effect thar will fill default value to customConfig
68
+ const valid = service.validate(data || {});
69
+
70
+ if (validate && !valid) {
71
+ const message = service.validate.errors.map((x) => `${x.instancePath} ${x.message}`).join(', ');
72
+ throw new Error(`Invalid blocklet service config: ${message}`);
73
+ }
74
+
75
+ return data;
76
+ };
77
+
78
+ const getDefaultServiceConfig = (serviceName) => {
79
+ const { defaultConfig } = getService(serviceName);
80
+
81
+ return defaultConfig;
82
+ };
83
+
84
+ const findService = (services, name) => {
85
+ const names = [name];
86
+
87
+ // backward compatible
88
+ if (name === NODE_SERVICES.AUTH) {
89
+ names.push(NODE_SERVICES.AUTH_SERVICE);
90
+ }
91
+
92
+ return (services || []).find((x) => names.includes(x.name));
93
+ };
94
+
95
+ module.exports = {
96
+ getServiceMetas,
97
+ getServiceConfig,
98
+ getDefaultServiceConfig,
99
+ findService,
100
+ setService,
101
+ };
package/lib/util.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  const get = require('lodash/get');
3
+ const slugify = require('slugify');
3
4
 
4
5
  const { NODE_SERVICES, SLOT_FOR_IP_DNS_SITE, WHO_CAN_ACCESS } = require('@abtnode/constant');
5
6
 
@@ -170,6 +171,14 @@ const fixBlockletStatus = (blocklet) => {
170
171
  child.source = fromBlockletSource(child.source);
171
172
  }
172
173
  });
174
+ if (blocklet.settings) {
175
+ (blocklet.settings.children || []).forEach((child) => {
176
+ child.status = fromBlockletStatus(child.status);
177
+ if (child.source !== undefined) {
178
+ child.source = fromBlockletSource(child.source);
179
+ }
180
+ });
181
+ }
173
182
  };
174
183
 
175
184
  const findWebInterface = (blocklet) => {
@@ -223,6 +232,8 @@ const getWhoCanAccess = (blocklet) => {
223
232
 
224
233
  const replaceSlotToIp = (url, ip) => (url || '').replace(SLOT_FOR_IP_DNS_SITE, (ip || '').replace(/\./g, '-'));
225
234
 
235
+ const urlFriendly = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./_]/g, '-'));
236
+
226
237
  module.exports = {
227
238
  isFreeBlocklet,
228
239
  isComponentBlocklet,
@@ -238,4 +249,5 @@ module.exports = {
238
249
  findServiceFromMeta,
239
250
  getWhoCanAccess,
240
251
  replaceSlotToIp,
252
+ urlFriendly,
241
253
  };
package/lib/validate.js CHANGED
@@ -1,14 +1,7 @@
1
- const Ajv = require('ajv').default;
2
- const cloneDeep = require('lodash/cloneDeep');
3
-
4
1
  const { createBlockletSchema } = require('./schema');
2
+ const { getServiceConfig } = require('./service');
5
3
 
6
- const fixAndValidateService = (meta, serviceMetas) => {
7
- const ajv = new Ajv({
8
- useDefaults: true,
9
- removeAdditional: 'all',
10
- });
11
-
4
+ const fixAndValidateService = (meta) => {
12
5
  if (!meta.interfaces) {
13
6
  return meta;
14
7
  }
@@ -19,26 +12,7 @@ const fixAndValidateService = (meta, serviceMetas) => {
19
12
  meta.interfaces.forEach((d) => {
20
13
  if (d.services && d.services.length) {
21
14
  d.services.forEach((s) => {
22
- const config = cloneDeep(s.config) || {};
23
-
24
- // validate service.config if serviceMetaList is exist
25
- if (serviceMetas) {
26
- const serviceMeta = serviceMetas.find((x) => x.name === s.name);
27
- if (!serviceMeta) {
28
- throw new Error(`Invalid blocklet service: ${s.name} does not exist`);
29
- }
30
-
31
- const validate = ajv.compile(serviceMeta.schema.JSONSchema);
32
- // this method may have side effect thar will fill default value to config
33
- const valid = validate(config);
34
-
35
- if (!valid) {
36
- const message = validate.errors.map((x) => `${x.dataPath} ${x.message}`).join(', ');
37
- throw new Error(`Invalid blocklet service config: ${message}`);
38
- }
39
- }
40
-
41
- s.config = config;
15
+ s.config = getServiceConfig(s.name, s.config, { validate: true });
42
16
  });
43
17
  }
44
18
  });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.9",
6
+ "version": "1.7.12",
7
7
  "description": "Library to parse/validate/fix blocklet meta",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -18,18 +18,18 @@
18
18
  "author": "wangshijun <wangshijun2020@gmail.com> (http://github.com/wangshijun)",
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@abtnode/constant": "1.7.9",
22
- "@abtnode/util": "1.7.9",
23
- "@arcblock/did": "^1.16.4",
24
- "@arcblock/did-ext": "^1.16.4",
25
- "@arcblock/did-util": "^1.16.4",
26
- "@arcblock/jwt": "^1.16.4",
27
- "@arcblock/nft": "^1.16.4",
28
- "@ocap/asset": "^1.16.4",
29
- "@ocap/mcrypto": "^1.16.4",
30
- "@ocap/util": "^1.16.4",
31
- "@ocap/wallet": "^1.16.4",
32
- "ajv": "^7.0.3",
21
+ "@abtnode/constant": "1.7.12",
22
+ "@abtnode/util": "1.7.12",
23
+ "@arcblock/did": "^1.16.6",
24
+ "@arcblock/did-ext": "^1.16.6",
25
+ "@arcblock/did-util": "^1.16.6",
26
+ "@arcblock/jwt": "^1.16.6",
27
+ "@arcblock/nft": "^1.16.6",
28
+ "@ocap/asset": "^1.16.6",
29
+ "@ocap/mcrypto": "^1.16.6",
30
+ "@ocap/util": "^1.16.6",
31
+ "@ocap/wallet": "^1.16.6",
32
+ "ajv": "^8.11.0",
33
33
  "cjk-length": "^1.0.0",
34
34
  "debug": "^4.3.3",
35
35
  "fs-extra": "^10.0.1",
@@ -40,11 +40,12 @@
40
40
  "js-yaml": "^4.1.0",
41
41
  "json-stable-stringify": "^1.0.1",
42
42
  "lodash": "^4.17.21",
43
+ "slugify": "^1.4.6",
43
44
  "url-join": "^4.0.1",
44
45
  "validate-npm-package-name": "^3.0.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "jest": "^27.4.5"
48
49
  },
49
- "gitHead": "285f4fedd41fcb8e1814ce5d8250ac10616e67e0"
50
+ "gitHead": "afc78b9cb92448676149262fb02432bc256a5524"
50
51
  }