fix: ES module and next server fixes.

This commit is contained in:
Sam Tolmay 2021-11-09 21:08:36 +02:00
parent dd1e86e435
commit 83bca458e4
No known key found for this signature in database
GPG Key ID: D004126FCD1A6DF0
57 changed files with 2392 additions and 1935 deletions

635
.pnp.cjs generated

File diff suppressed because it is too large Load Diff

249
.pnp.loader.mjs generated Normal file
View File

@ -0,0 +1,249 @@
import { URL, fileURLToPath, pathToFileURL } from 'url';
import fs from 'fs';
import path from 'path';
import moduleExports, { Module } from 'module';
var PathType;
(function(PathType2) {
PathType2[PathType2["File"] = 0] = "File";
PathType2[PathType2["Portable"] = 1] = "Portable";
PathType2[PathType2["Native"] = 2] = "Native";
})(PathType || (PathType = {}));
const npath = Object.create(path);
const ppath = Object.create(path.posix);
npath.cwd = () => process.cwd();
ppath.cwd = () => toPortablePath(process.cwd());
ppath.resolve = (...segments) => {
if (segments.length > 0 && ppath.isAbsolute(segments[0])) {
return path.posix.resolve(...segments);
} else {
return path.posix.resolve(ppath.cwd(), ...segments);
}
};
const contains = function(pathUtils, from, to) {
from = pathUtils.normalize(from);
to = pathUtils.normalize(to);
if (from === to)
return `.`;
if (!from.endsWith(pathUtils.sep))
from = from + pathUtils.sep;
if (to.startsWith(from)) {
return to.slice(from.length);
} else {
return null;
}
};
npath.fromPortablePath = fromPortablePath;
npath.toPortablePath = toPortablePath;
npath.contains = (from, to) => contains(npath, from, to);
ppath.contains = (from, to) => contains(ppath, from, to);
const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/;
const UNC_WINDOWS_PATH_REGEXP = /^\\\\(\.\\)?(.*)$/;
const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/;
const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/;
function fromPortablePath(p) {
if (process.platform !== `win32`)
return p;
let portablePathMatch, uncPortablePathMatch;
if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP))
p = portablePathMatch[1];
else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP))
p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`;
else
return p;
return p.replace(/\//g, `\\`);
}
function toPortablePath(p) {
if (process.platform !== `win32`)
return p;
let windowsPathMatch, uncWindowsPathMatch;
if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP))
p = `/${windowsPathMatch[1]}`;
else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP))
p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`;
return p.replace(/\\/g, `/`);
}
const builtinModules = new Set(Module.builtinModules || Object.keys(process.binding(`natives`)));
const isBuiltinModule = (request) => request.startsWith(`node:`) || builtinModules.has(request);
function readPackageScope(checkPath) {
const rootSeparatorIndex = checkPath.indexOf(npath.sep);
let separatorIndex;
do {
separatorIndex = checkPath.lastIndexOf(npath.sep);
checkPath = checkPath.slice(0, separatorIndex);
if (checkPath.endsWith(`${npath.sep}node_modules`))
return false;
const pjson = readPackage(checkPath + npath.sep);
if (pjson) {
return {
data: pjson,
path: checkPath
};
}
} while (separatorIndex > rootSeparatorIndex);
return false;
}
function readPackage(requestPath) {
const jsonPath = npath.resolve(requestPath, `package.json`);
if (!fs.existsSync(jsonPath))
return null;
return JSON.parse(fs.readFileSync(jsonPath, `utf8`));
}
async function tryReadFile(path2) {
try {
return await fs.promises.readFile(path2, `utf8`);
} catch (error) {
if (error.code === `ENOENT`)
return null;
throw error;
}
}
function tryParseURL(str) {
try {
return new URL(str);
} catch {
return null;
}
}
function getFileFormat(filepath) {
var _a;
const ext = path.extname(filepath);
switch (ext) {
case `.mjs`: {
return `module`;
}
case `.cjs`: {
return `commonjs`;
}
case `.wasm`: {
throw new Error(`Unknown file extension ".wasm" for ${filepath}`);
}
case `.json`: {
throw new Error(`Unknown file extension ".json" for ${filepath}`);
}
case `.js`: {
const pkg = readPackageScope(filepath);
if (pkg) {
return (_a = pkg.data.type) != null ? _a : `commonjs`;
}
}
}
return null;
}
async function getFormat$1(resolved, context, defaultGetFormat) {
const url = tryParseURL(resolved);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultGetFormat(resolved, context, defaultGetFormat);
const format = getFileFormat(fileURLToPath(url));
if (format) {
return {
format
};
}
return defaultGetFormat(resolved, context, defaultGetFormat);
}
async function getSource$1(urlString, context, defaultGetSource) {
const url = tryParseURL(urlString);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultGetSource(urlString, context, defaultGetSource);
return {
source: await fs.promises.readFile(fileURLToPath(url), `utf8`)
};
}
async function load$1(urlString, context, defaultLoad) {
const url = tryParseURL(urlString);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultLoad(urlString, context, defaultLoad);
const filePath = fileURLToPath(url);
const format = getFileFormat(filePath);
if (!format)
return defaultLoad(urlString, context, defaultLoad);
return {
format,
source: await fs.promises.readFile(filePath, `utf8`)
};
}
const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/;
async function resolve$1(originalSpecifier, context, defaultResolver) {
var _a;
const {findPnpApi} = moduleExports;
if (!findPnpApi || isBuiltinModule(originalSpecifier))
return defaultResolver(originalSpecifier, context, defaultResolver);
let specifier = originalSpecifier;
const url = tryParseURL(specifier);
if (url) {
if (url.protocol !== `file:`)
return defaultResolver(originalSpecifier, context, defaultResolver);
specifier = fileURLToPath(specifier);
}
const {parentURL, conditions = []} = context;
const issuer = parentURL ? fileURLToPath(parentURL) : process.cwd();
const pnpapi = (_a = findPnpApi(issuer)) != null ? _a : url ? findPnpApi(specifier) : null;
if (!pnpapi)
return defaultResolver(originalSpecifier, context, defaultResolver);
const dependencyNameMatch = specifier.match(pathRegExp);
let allowLegacyResolve = false;
if (dependencyNameMatch) {
const [, dependencyName, subPath] = dependencyNameMatch;
if (subPath === ``) {
const resolved = pnpapi.resolveToUnqualified(`${dependencyName}/package.json`, issuer);
if (resolved) {
const content = await tryReadFile(resolved);
if (content) {
const pkg = JSON.parse(content);
allowLegacyResolve = pkg.exports == null;
}
}
}
}
const result = pnpapi.resolveRequest(specifier, issuer, {
conditions: new Set(conditions),
extensions: allowLegacyResolve ? void 0 : []
});
if (!result)
throw new Error(`Resolving '${specifier}' from '${issuer}' failed`);
return {
url: pathToFileURL(result).href
};
}
const binding = process.binding(`fs`);
const originalfstat = binding.fstat;
const ZIP_FD = 2147483648;
binding.fstat = function(...args) {
const [fd, useBigint, req] = args;
if ((fd & ZIP_FD) !== 0 && useBigint === false && req === void 0) {
try {
const stats = fs.fstatSync(fd);
return new Float64Array([
stats.dev,
stats.mode,
stats.nlink,
stats.uid,
stats.gid,
stats.rdev,
stats.blksize,
stats.ino,
stats.size,
stats.blocks
]);
} catch {
}
}
return originalfstat.apply(this, args);
};
const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10));
const hasConsolidatedHooks = major > 16 || major === 16 && minor >= 12;
const resolve = resolve$1;
const getFormat = hasConsolidatedHooks ? void 0 : getFormat$1;
const getSource = hasConsolidatedHooks ? void 0 : getSource$1;
const load = hasConsolidatedHooks ? load$1 : void 0;
export { getFormat, getSource, load, resolve };

File diff suppressed because one or more lines are too long

768
.yarn/releases/yarn-3.1.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -72,4 +72,6 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.0.2.cjs
yarnPath: .yarn/releases/yarn-3.1.0.cjs
pnpEnableEsmLoader: true

View File

@ -55,5 +55,6 @@
},
"resolutions": {
"mongodb": "3.6.5"
}
},
"packageManager": "yarn@3.1.0"
}

View File

@ -14,15 +14,14 @@
limitations under the License.
*/
import createContext from './context/createContext';
import homePageId from './routes/rootConfig/homePageId';
import openIdAuthorizationUrl from './routes/auth/openIdAuthorizationUrl';
import openIdCallback from './routes/auth/openIdCallback';
import openIdLogoutUrl from './routes/auth/openIdLogoutUrl';
import pageConfig from './routes/page/pageConfig';
import pageHtml from './routes/page/pageHtml';
import rootConfig from './routes/rootConfig/rootConfig';
import request from './routes/request/request';
import createContext from './context/createContext.js';
import getHomePageId from './routes/rootConfig/getHomePageId.js';
import getPageConfig from './routes/page/getPageConfig.js';
import openIdAuthorizationUrl from './routes/auth/openIdAuthorizationUrl.js';
import openIdCallback from './routes/auth/openIdCallback.js';
import openIdLogoutUrl from './routes/auth/openIdLogoutUrl.js';
import request from './routes/request/request.js';
import rootConfig from './routes/rootConfig/rootConfig.js';
import {
AuthenticationError,
@ -30,21 +29,20 @@ import {
RequestError,
ServerError,
TokenExpiredError,
} from './context/errors';
} from './context/errors.js';
export {
AuthenticationError,
ConfigurationError,
createContext,
homePageId,
getHomePageId,
getPageConfig,
openIdAuthorizationUrl,
openIdCallback,
openIdLogoutUrl,
pageConfig,
pageHtml,
rootConfig,
request,
AuthenticationError,
ConfigurationError,
RequestError,
rootConfig,
ServerError,
TokenExpiredError,
};

View File

@ -14,10 +14,10 @@
limitations under the License.
*/
async function pageConfig({ authorize, readConfigFile }, { pageId }) {
async function getPageConfig({ authorize, readConfigFile }, { pageId }) {
const pageConfig = await readConfigFile(`pages/${pageId}/${pageId}.json`);
if (pageConfig && authorize(pageConfig)) return pageConfig;
return null;
}
export default pageConfig;
export default getPageConfig;

View File

@ -14,7 +14,7 @@
limitations under the License.
*/
import pageConfig from './pageConfig.js';
import getPageConfig from './getPageConfig.js';
import testContext from '../../test/testContext.js';
const mockReadConfigFile = jest.fn();
@ -25,7 +25,7 @@ beforeEach(() => {
mockReadConfigFile.mockReset();
});
test('pageConfig, public', async () => {
test('getPageConfig, public', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
@ -37,7 +37,7 @@ test('pageConfig, public', async () => {
}
return null;
});
const res = await pageConfig(context, { pageId: 'pageId' });
const res = await getPageConfig(context, { pageId: 'pageId' });
expect(res).toEqual({
id: 'page:pageId',
auth: {
@ -46,7 +46,7 @@ test('pageConfig, public', async () => {
});
});
test('pageConfig, protected, no user', async () => {
test('getPageConfig, protected, no user', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
@ -58,11 +58,11 @@ test('pageConfig, protected, no user', async () => {
}
return null;
});
const res = await pageConfig(context, { pageId: 'pageId' });
const res = await getPageConfig(context, { pageId: 'pageId' });
expect(res).toEqual(null);
});
test('pageConfig, protected, with user', async () => {
test('getPageConfig, protected, with user', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
@ -75,7 +75,7 @@ test('pageConfig, protected, with user', async () => {
return null;
});
const res = await pageConfig(
const res = await getPageConfig(
testContext({ readConfigFile: mockReadConfigFile, user: { sub: 'sub' } }),
{ pageId: 'pageId' }
);
@ -87,7 +87,7 @@ test('pageConfig, protected, with user', async () => {
});
});
test('pageConfig, page does not exist', async () => {
test('getPageConfig, page does not exist', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
@ -99,6 +99,6 @@ test('pageConfig, page does not exist', async () => {
}
return null;
});
const res = await pageConfig(context, { pageId: 'doesNotExist' });
const res = await getPageConfig(context, { pageId: 'doesNotExist' });
expect(res).toEqual(null);
});

View File

@ -1,26 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
async function pageHtml({ authorize, readConfigFile }, { pageId }) {
const pageHtml = await readConfigFile(`static/${pageId}.html`);
if (!pageHtml) return null;
// TODO: What if html exists but JSON config does not?
const pageConfig = await readConfigFile(`pages/${pageId}/${pageId}.json`);
if (authorize(pageConfig)) return pageHtml;
return null;
}
export default pageHtml;

View File

@ -1,106 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import pageHtml from './pageHtml.js';
import testContext from '../../test/testContext.js';
const mockReadConfigFile = jest.fn();
const context = testContext({ readConfigFile: mockReadConfigFile });
beforeEach(() => {
mockReadConfigFile.mockReset();
});
test('pageHtml, public', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
id: 'page:pageId',
auth: {
public: true,
},
};
}
if (path === 'static/pageId.html') {
return '<h1>pageId</h1>';
}
return null;
});
const res = await pageHtml(context, { pageId: 'pageId' });
expect(res).toEqual('<h1>pageId</h1>');
});
test('pageHtml, protected, no user', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
id: 'page:pageId',
auth: {
public: false,
},
};
}
if (path === 'static/pageId.html') {
return '<h1>pageId</h1>';
}
return null;
});
const res = await pageHtml(context, { pageId: 'pageId' });
expect(res).toEqual(null);
});
test('pageHtml, protected, with user', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
id: 'page:pageId',
auth: {
public: false,
},
};
}
if (path === 'static/pageId.html') {
return '<h1>pageId</h1>';
}
return null;
});
const res = await pageHtml(
testContext({ readConfigFile: mockReadConfigFile, user: { sub: 'sub' } }),
{ pageId: 'pageId' }
);
expect(res).toEqual('<h1>pageId</h1>');
});
test('pageHtml, page does not exist', async () => {
mockReadConfigFile.mockImplementation((path) => {
if (path === 'pages/pageId/pageId.json') {
return {
id: 'page:pageId',
auth: {
public: true,
},
};
}
if (path === 'static/pageId.html') {
return '<h1>pageId</h1>';
}
return null;
});
const res = await pageHtml(context, { pageId: 'doesNotExist' });
expect(res).toEqual(null);
});

View File

@ -0,0 +1,39 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { get } from '@lowdefy/helpers';
function findHomePageId({ config }, { menus }) {
if (get(config, 'homePageId')) {
return get(config, 'homePageId');
}
let defaultMenu = menus.find((menu) => menu.menuId === 'default');
if (!defaultMenu) {
// eslint-disable-next-line prefer-destructuring
defaultMenu = menus[0];
}
let homePageId = null;
homePageId = get(defaultMenu, 'links.0.pageId', { default: null });
if (!homePageId) {
homePageId = get(defaultMenu, 'links.0.links.0.pageId', { default: null });
}
if (!homePageId) {
homePageId = get(defaultMenu, 'links.0.links.0.links.0.pageId', { default: null });
}
return homePageId;
}
export default findHomePageId;

View File

@ -14,10 +14,10 @@
limitations under the License.
*/
import getHomePageId from './getHomePageId';
import testContext from '../../test/testContext';
import findHomePageId from './findHomePageId.js';
import testContext from '../../test/testContext.js';
test('getMenus, menu with configured home page id', () => {
test('findHomePageId, menu with configured home page id', () => {
const context = testContext({ config: { homePageId: 'homePageId' } });
const menus = [
{
@ -41,11 +41,11 @@ test('getMenus, menu with configured home page id', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('homePageId');
});
test('getMenus, get homePageId at first level', () => {
test('findHomePageId, get homePageId at first level', () => {
const context = testContext();
const menus = [
{
@ -61,11 +61,11 @@ test('getMenus, get homePageId at first level', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('page');
});
test('getMenus, get homePageId at second level', () => {
test('findHomePageId, get homePageId at second level', () => {
const context = testContext();
const menus = [
{
@ -89,11 +89,11 @@ test('getMenus, get homePageId at second level', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('page');
});
test('getMenus, get homePageId at third level', () => {
test('findHomePageId, get homePageId at third level', () => {
const context = testContext();
const menus = [
{
@ -125,11 +125,11 @@ test('getMenus, get homePageId at third level', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('page');
});
test('getMenus, no default menu, no configured homepage', () => {
test('findHomePageId, no default menu, no configured homepage', () => {
const context = testContext();
const menus = [
{
@ -145,11 +145,11 @@ test('getMenus, no default menu, no configured homepage', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('page');
});
test('getMenus, more than 1 menu, no configured homepage', () => {
test('findHomePageId, more than 1 menu, no configured homepage', () => {
const context = testContext();
const menus = [
{
@ -177,11 +177,11 @@ test('getMenus, more than 1 menu, no configured homepage', () => {
],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual('default-page');
});
test('getMenus, default menu has no links', () => {
test('findHomePageId, default menu has no links', () => {
const context = testContext();
const menus = [
{
@ -189,6 +189,6 @@ test('getMenus, default menu has no links', () => {
links: [],
},
];
const res = getHomePageId(context, { menus });
const res = findHomePageId(context, { menus });
expect(res).toEqual(null);
});

View File

@ -14,26 +14,14 @@
limitations under the License.
*/
import { get } from '@lowdefy/helpers';
import findHomePageId from '../rootConfig/findHomePageId.js';
import getMenus from '../rootConfig/menus/getMenus.js';
function getHomePageId({ config }, { menus }) {
if (get(config, 'homePageId')) {
return get(config, 'homePageId');
}
let defaultMenu = menus.find((menu) => menu.menuId === 'default');
if (!defaultMenu) {
// eslint-disable-next-line prefer-destructuring
defaultMenu = menus[0];
}
let homePageId = null;
homePageId = get(defaultMenu, 'links.0.pageId', { default: null });
if (!homePageId) {
homePageId = get(defaultMenu, 'links.0.links.0.pageId', { default: null });
}
if (!homePageId) {
homePageId = get(defaultMenu, 'links.0.links.0.links.0.pageId', { default: null });
}
return homePageId;
async function getHomePageId(context) {
// TODO: We can optimise here as we don't need to read menus if homepageId is configured
// but not sure if it is worth the added complexity
const menus = await getMenus(context);
return findHomePageId(context, { menus });
}
export default getHomePageId;

View File

@ -1,27 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import getHomePageId from '../rootConfig/getHomePageId';
import getMenus from '../rootConfig/menus/getMenus';
async function homePageId(context) {
// TODO: We can optimise here as we don't need to read menus if homepageId is configured
// but not sure if it is worth the added complexity
const menus = await getMenus(context);
return getHomePageId(context, { menus });
}
export default homePageId;

View File

@ -14,15 +14,15 @@
limitations under the License.
*/
import getHomePageId from './getHomePageId';
import getLowdefyGlobal from './getLowdefyGlobal';
import getMenus from './menus/getMenus';
import findHomePageId from './findHomePageId.js';
import getLowdefyGlobal from './getLowdefyGlobal.js';
import getMenus from './menus/getMenus.js';
async function rootConfig(context) {
const [lowdefyGlobal, menus] = await Promise.all([getLowdefyGlobal(context), getMenus(context)]);
return {
authenticated: context.authenticated,
homePageId: getHomePageId(context, { menus }),
homePageId: findHomePageId(context, { menus }),
lowdefyGlobal,
menus,
};

View File

@ -24,7 +24,8 @@
"type": "git",
"url": "https://github.com/lowdefy/lowdefy.git"
},
"main": "dist/index.js",
"type": "module",
"exports": "./dist/index.js",
"files": [
"dist/*"
],

View File

@ -21,7 +21,7 @@ async function run() {
await build({
// blocksServerUrl: 'https://blocks-cdn.lowdefy.com/v3.16.0',
logger: console,
buildDirectory: path.resolve(process.cwd(), '../servers/serverDev/.lowdefy/build'),
buildDirectory: path.resolve(process.cwd(), '../server/.lowdefy/build'),
// buildDirectory: path.resolve(process.cwd(), './.lowdefy/build'),
// cacheDirectory: path.resolve(process.cwd(), '../servers/serverDev/.lowdefy/.cache'),
cacheDirectory: path.resolve(process.cwd(), './.lowdefy/.cache'),

View File

@ -0,0 +1,54 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export default {
id: '404',
type: 'Result',
style: {
minHeight: '100vh',
},
properties: {
status: 404,
title: '404',
subTitle: 'Sorry, the page you are visiting does not exist.',
},
areas: {
extra: {
blocks: [
{
id: 'home',
type: 'Button',
properties: {
title: 'Go to home page',
type: 'Link',
icon: 'HomeOutlined',
},
events: {
onClick: [
{
id: 'home',
type: 'Link',
params: {
home: true,
},
},
],
},
},
],
},
},
};

View File

@ -1,38 +0,0 @@
{
"id": "404",
"type": "Result",
"style": {
"minHeight": "100vh"
},
"properties": {
"status": 404,
"title": "404",
"subTitle": "Sorry, the page you are visiting does not exist."
},
"areas": {
"extra": {
"blocks": [
{
"id": "home",
"type": "Button",
"properties": {
"title": "Go to home page",
"type": "Link",
"icon": "HomeOutlined"
},
"events": {
"onClick": [
{
"id": "home",
"type": "Link",
"params": {
"home": true
}
}
]
}
}
]
}
}
}

View File

@ -15,7 +15,7 @@
*/
import { type } from '@lowdefy/helpers';
import page404 from './404.json';
import page404 from './404.js';
async function addDefaultPages({ components }) {
// If not copied, the same object is mutated by build every time

View File

@ -15,7 +15,7 @@
*/
import { validate } from '@lowdefy/ajv';
import lowdefySchema from '../lowdefySchema.json';
import lowdefySchema from '../lowdefySchema.js';
import formatErrorMessage from '../utils/formatErrorMessage.js';
async function testSchema({ components, context }) {

View File

@ -18,7 +18,7 @@
import { type } from '@lowdefy/helpers';
import { validate } from '@lowdefy/ajv';
import lowdefySchema from '../lowdefySchema.json';
import lowdefySchema from '../lowdefySchema.js';
async function validateConfig({ components }) {
if (type.isNone(components.config)) {

View File

@ -1,88 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const template = `
<!--
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ LOWDEFY_PAGE_TITLE }}</title>
<link rel="manifest" href="{{ LOWDEFY_SERVER_BASE_PATH }}/public/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="{{ LOWDEFY_SERVER_BASE_PATH }}/public/icon.svg" />
<link rel="icon" type="image/png" href="{{ LOWDEFY_SERVER_BASE_PATH }}/public/icon-32.png" />
<link rel="apple-touch-icon" href="{{ LOWDEFY_SERVER_BASE_PATH }}/public/apple-touch-icon.png" />
<link
rel="preload"
href="/lowdefy/page/{{ LOWDEFY_PAGE_ID }}"
as="fetch"
crossorigin="anonymous"
/>
<link rel="preload" href="/lowdefy/root" as="fetch" crossorigin="anonymous" />
<script type="text/javascript">
const jsActions = {};
const jsOperators = {};
const getMethodLoader = (scope, reference) => (name, method) => {
if (typeof name !== 'string') {
throw new Error(\`\${scope} requires a string for the first argument.\`);
}
if (typeof method !== 'function') {
throw new Error(\`\${scope} requires a function for the second argument.\`);
}
reference[name] = method;
};
window.lowdefy = {
basePath: '{{ LOWDEFY_SERVER_BASE_PATH }}',
imports: {
jsActions,
jsOperators,
},
registerJsAction: getMethodLoader('registerJsAction', jsActions),
registerJsOperator: getMethodLoader('registerJsOperator', jsOperators),
};
</script>
<script defer src="{{ LOWDEFY_SERVER_BASE_PATH }}/client/runtime_{{ LOWDEFY_VERSION }}.js"></script>
<script defer src="{{ LOWDEFY_SERVER_BASE_PATH }}/client/main_{{ LOWDEFY_VERSION }}.js"></script></head>
{{ LOWDEFY_APP_HEAD_HTML }}
{{ LOWDEFY_PAGE_HEAD_HTML }}
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="emotion"></div>
<div id="root"></div>
{{ LOWDEFY_APP_BODY_HTML }}
{{ LOWDEFY_PAGE_BODY_HTML }}
</body>
</html>
`;
export default template;

View File

@ -1,53 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { get, type } from '@lowdefy/helpers';
import { nunjucksFunction } from '@lowdefy/nunjucks';
import template from './template.js';
const templateFn = nunjucksFunction(template);
function pageHtml({ context, page }) {
return templateFn({
LOWDEFY_VERSION: context.version,
LOWDEFY_PAGE_ID: page.pageId,
// TODO: Don't use properties.title since it might be an operator
LOWDEFY_PAGE_TITLE: get(page, 'properties.title', { default: 'Lowdefy App' }),
LOWDEFY_SERVER_BASE_PATH: get(context, 'serverBasePath', { default: '' }),
// TODO: Implement
LOWDEFY_APP_HEAD_HTML: '',
LOWDEFY_PAGE_HEAD_HTML: '',
LOWDEFY_APP_BODY_HTML: '',
LOWDEFY_PAGE_BODY_HTML: '',
});
}
async function writePageHtml({ context, page, templateFn }) {
await context.writeBuildArtifact({
filePath: `static/${page.pageId}.html`,
content: pageHtml({ context, page, templateFn }),
});
}
async function writeHtml({ components, context }) {
if (type.isNone(components.pages)) return;
const writePromises = components.pages.map((page) => writePageHtml({ context, page }));
return Promise.all(writePromises);
}
export { pageHtml };
export default writeHtml;

View File

@ -1,77 +0,0 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import writeHtml, { pageHtml } from './writeHtml.js';
import testContext from '../../test/testContext.js';
const mockWriteBuildArtifact = jest.fn();
const context = testContext({ writeBuildArtifact: mockWriteBuildArtifact });
beforeEach(() => {
mockWriteBuildArtifact.mockReset();
});
test('writeHtml write page html', async () => {
const components = {
pages: [
{
id: 'page:page1',
pageId: 'page1',
blockId: 'page1',
requests: [],
},
],
};
await writeHtml({ components, context });
expect(mockWriteBuildArtifact.mock.calls[0][0].filePath).toEqual('static/page1.html');
});
test('writeHtml multiple pages', async () => {
const components = {
pages: [
{
id: 'page:page1',
pageId: 'page1',
blockId: 'page1',
requests: [],
},
{
id: 'page:page2',
pageId: 'page2',
blockId: 'page2',
requests: [],
},
],
};
await writeHtml({ components, context });
expect(mockWriteBuildArtifact.mock.calls[0][0].filePath).toEqual('static/page1.html');
expect(mockWriteBuildArtifact.mock.calls[1][0].filePath).toEqual('static/page2.html');
});
test('writeHtml no pages', async () => {
const components = {
pages: [],
};
await writeHtml({ components, context });
expect(mockWriteBuildArtifact.mock.calls).toEqual([]);
});
test('writeHtml pages undefined', async () => {
const components = {};
await writeHtml({ components, context });
expect(mockWriteBuildArtifact.mock.calls).toEqual([]);
});

View File

@ -16,8 +16,6 @@
limitations under the License.
*/
import packageJson from '../package.json';
import createGetMeta from './utils/meta/getMeta.js';
import createWriteBuildArtifact from './utils/files/writeBuildArtifact.js';
import createReadConfigFile from './utils/files/readConfigFile.js';
@ -36,7 +34,6 @@ import writeApp from './build/writeApp.js';
import writeConfig from './build/writeConfig.js';
import writeConnections from './build/writeConnections.js';
import writeGlobal from './build/writeGlobal.js';
import writeHtml from './build/writeHtml/writeHtml.js';
import writeMenus from './build/writeMenus.js';
import writePages from './build/writePages.js';
import writeRequests from './build/writeRequests.js';
@ -52,7 +49,8 @@ function createContext(options) {
logger,
readConfigFile: createReadConfigFile({ configDirectory }),
refResolver,
version: packageJson.version,
version: '3.22.0',
// version: packageJson.version,
writeBuildArtifact: createWriteBuildArtifact({ buildDirectory }),
};
return context;
@ -76,7 +74,6 @@ async function build(options) {
await writeConnections({ components, context });
await writeRequests({ components, context });
await writePages({ components, context });
await writeHtml({ components, context });
await writeConfig({ components, context });
await writeGlobal({ components, context });
await writeMenus({ components, context });

View File

@ -0,0 +1,665 @@
export default {
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'http://lowdefy.com/appSchema.json',
type: 'object',
title: 'Lowdefy App Schema',
definitions: {
action: {
type: 'object',
additionalProperties: false,
required: ['id', 'type'],
properties: {
async: {
type: 'boolean',
errorMessage: {
type: 'Action "async" should be a boolean.',
},
},
id: {
type: 'string',
errorMessage: {
type: 'Action "id" should be a string.',
},
},
messages: {},
params: {},
skip: {},
type: {
type: 'string',
errorMessage: {
type: 'Action "type" should be a string.',
},
},
},
errorMessage: {
type: 'Action should be an object.',
required: {
id: 'Action should have required property "id".',
type: 'Action should have required property "type".',
},
},
},
app: {
type: 'object',
additionalProperties: false,
properties: {
html: {
type: 'object',
errorMessage: {
type: 'App "app.html" should be an object.',
},
properties: {
appendBody: {
type: 'string',
errorMessage: {
type: 'App "app.html.appendBody" should be a string.',
},
},
appendHead: {
type: 'string',
errorMessage: {
type: 'App "app.html.appendHead" should be a string.',
},
},
},
},
},
},
authConfig: {
type: 'object',
additionalProperties: false,
errorMessage: {
type: 'App "config.auth" should be an object.',
},
properties: {
openId: {
type: 'object',
additionalProperties: false,
errorMessage: {
type: 'App "config.auth.openId" should be an object.',
},
properties: {
rolesField: {
type: 'string',
description: '.',
errorMessage: {
type: 'App "config.auth.openId.rolesField" should be a string.',
},
},
logoutRedirectUri: {
type: 'string',
description:
'The URI to redirect the user to after logout. Can be a Nunjucks template string with client_id, host, id_token_hint, and openid_domain as template data.',
errorMessage: {
type: 'App "config.auth.openId.logoutRedirectUri" should be a string.',
},
},
scope: {
type: 'string',
description: 'The OpenID Connect scope to request.',
default: 'openid profile email',
errorMessage: {
type: 'App "config.auth.openId.scope" should be a string.',
},
},
},
},
pages: {
type: 'object',
additionalProperties: false,
errorMessage: {
type: 'App "config.auth.pages" should be an object.',
},
properties: {
protected: {
type: ['array', 'boolean'],
errorMessage: {
type: 'App "config.auth.pages.protected.$" should be an array of strings.',
},
items: {
type: 'string',
description:
'Page ids for which authentication is required. When specified, all unspecified pages will be public.',
errorMessage: {
type: 'App "config.auth.pages.protected.$" should be an array of strings.',
},
},
},
public: {
type: ['array', 'boolean'],
errorMessage: {
type: 'App "config.auth.pages.public.$" should be an array of strings.',
},
items: {
type: 'string',
description:
'Page ids for which authentication is not required. When specified, all unspecified pages will be protected.',
errorMessage: {
type: 'App "config.auth.pages.public.$" should be an array of strings.',
},
},
},
roles: {
type: 'object',
patternProperties: {
'^.*$': {
type: 'array',
items: {
type: 'string',
},
errorMessage: {
type: 'App "config.auth.pages.roles.[role]" should be an array of strings.',
},
},
},
errorMessage: {
type: 'App "config.auth.pages.roles" should be an object.',
},
},
},
},
jwt: {
type: 'object',
additionalProperties: false,
errorMessage: {
type: 'App "config.auth.jwt" should be an object.',
},
properties: {
expiresIn: {
type: ['string', 'number'],
default: '4h',
description:
'The length of time a user token should be valid. Can be expressed as a number in seconds, or a vercel/ms string (https://github.com/vercel/ms)',
errorMessage: {
type: 'App "config.auth.jwt.expiresIn" should be a string or number.',
},
},
loginStateExpiresIn: {
type: ['string', 'number'],
default: '5min',
description:
'The length of time an authorization request token should be valid. Can be expressed as a number in seconds, or a vercel/ms string (https://github.com/vercel/ms)',
errorMessage: {
type: 'App "config.auth.jwt.loginStateExpiresIn" should be a string or number.',
},
},
},
},
},
},
block: {
type: 'object',
additionalProperties: false,
required: ['id', 'type'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'Block "id" should be a string.',
},
},
type: {
type: 'string',
errorMessage: {
type: 'Block "type" should be a string.',
},
},
field: {
type: 'string',
errorMessage: {
type: 'Block "field" should be a string.',
},
},
properties: {
type: 'object',
},
layout: {
type: 'object',
errorMessage: {
type: 'Block "layout" should be an object.',
},
},
loading: {
type: 'object',
errorMessage: {
type: 'Block "loading" should be an object.',
},
},
style: {
type: 'object',
errorMessage: {
type: 'Block "style" should be an object.',
},
},
visible: {},
blocks: {
type: 'array',
items: {
$ref: '#/definitions/block',
},
errorMessage: {
type: 'Block "blocks" should be an array.',
},
},
requests: {
type: 'array',
items: {
$ref: '#/definitions/request',
},
errorMessage: {
type: 'Block "requests" should be an array.',
},
},
required: {},
validate: {
type: 'array',
items: {
type: 'object',
errorMessage: {
type: 'Block "validate" should be an array of objects.',
},
},
errorMessage: {
type: 'Block "validate" should be an array.',
},
},
events: {
type: 'object',
patternProperties: {
'^.*$': {
anyOf: [
{
type: 'array',
items: {
$ref: '#/definitions/action',
},
},
{
type: 'object',
additionalProperties: false,
properties: {
try: {
type: 'array',
items: {
$ref: '#/definitions/action',
},
},
catch: {
type: 'array',
items: {
$ref: '#/definitions/action',
},
},
debounce: {
type: 'object',
additionalProperties: false,
properties: {
immediate: {
type: 'boolean',
errorMessage: {
type: 'Event "debounce.immediate" should be an boolean.',
},
},
ms: {
type: 'number',
errorMessage: {
type: 'Event "debounce.ms" should be a number.',
},
},
},
},
},
},
],
},
},
errorMessage: {
type: 'Block "events" should be an object.',
},
},
areas: {
type: 'object',
patternProperties: {
'^.*$': {
type: 'object',
properties: {
blocks: {
type: 'array',
items: {
$ref: '#/definitions/block',
},
errorMessage: {
type: 'Block "areas.{areaKey}.blocks" should be an array.',
},
},
},
errorMessage: {
type: 'Block "areas.{areaKey}" should be an object.',
},
},
},
errorMessage: {
type: 'Block "areas" should be an object.',
},
},
},
errorMessage: {
type: 'Block should be an object.',
required: {
id: 'Block should have required property "id".',
type: 'Block should have required property "type".',
},
},
},
connection: {
type: 'object',
additionalProperties: false,
required: ['id', 'type'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'Connection "id" should be a string.',
},
},
type: {
type: 'string',
errorMessage: {
type: 'Connection "type" should be a string.',
},
},
properties: {
type: 'object',
errorMessage: {
type: 'Connection "properties" should be an object.',
},
},
},
errorMessage: {
type: 'Connection should be an object.',
required: {
id: 'Connection should have required property "id".',
type: 'Connection should have required property "type".',
},
},
},
menu: {
type: 'object',
additionalProperties: false,
required: ['id'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'Menu "id" should be a string.',
},
},
properties: {
type: 'object',
errorMessage: {
type: 'Menu "properties" should be an object.',
},
},
links: {
type: 'array',
items: {
$ref: '#/definitions/menuItem',
},
errorMessage: {
type: 'Menu "links" should be an array.',
},
},
},
errorMessage: {
type: 'Menu should be an object.',
required: {
id: 'Menu should have required property "id".',
},
},
},
menuGroup: {
type: 'object',
additionalProperties: false,
required: ['id', 'type'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'MenuGroup "id" should be a string.',
},
},
type: {
type: 'string',
errorMessage: {
type: 'MenuGroup "type" should be a string.',
},
},
properties: {
type: 'object',
errorMessage: {
type: 'MenuGroup "properties" should be an object.',
},
},
links: {
type: 'array',
items: {
$ref: '#/definitions/menuItem',
},
errorMessage: {
type: 'MenuGroup "links" should be an array.',
},
},
},
errorMessage: {
type: 'MenuGroup should be an object.',
required: {
id: 'MenuGroup should have required property "id".',
type: 'MenuGroup should have required property "type".',
},
},
},
menuItem: {
anyOf: [
{
$ref: '#/definitions/menuGroup',
},
{
$ref: '#/definitions/menuLink',
},
],
},
menuLink: {
type: 'object',
additionalProperties: false,
required: ['id', 'type'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'MenuLink "id" should be a string.',
},
},
type: {
type: 'string',
errorMessage: {
type: 'MenuLink "type" should be a string.',
},
},
pageId: {
type: 'string',
errorMessage: {
type: 'MenuLink "pageId" should be a string.',
},
},
url: {
type: 'string',
errorMessage: {
type: 'MenuLink "url" should be a string.',
},
},
properties: {
type: 'object',
errorMessage: {
type: 'MenuLink "properties" should be an object.',
},
},
},
errorMessage: {
type: 'MenuLink should be an object.',
required: {
id: 'MenuLink should have required property "id".',
type: 'MenuLink should have required property "type".',
},
},
},
request: {
type: 'object',
additionalProperties: false,
required: ['id', 'type', 'connectionId'],
properties: {
id: {
type: 'string',
errorMessage: {
type: 'Request "id" should be a string.',
},
},
type: {
type: 'string',
errorMessage: {
type: 'Request "type" should be a string.',
},
},
connectionId: {
type: 'string',
errorMessage: {
type: 'Request "connectionId" should be a string.',
},
},
payload: {
type: 'object',
errorMessage: {
type: 'Request "payload" should be an object.',
},
},
properties: {
type: 'object',
errorMessage: {
type: 'Request "properties" should be an object.',
},
},
},
errorMessage: {
type: 'Request should be an object.',
required: {
id: 'Request should have required property "id".',
type: 'Request should have required property "type".',
connectionId: 'Request should have required property "connectionId".',
},
},
},
},
additionalProperties: false,
required: ['lowdefy'],
properties: {
name: {
type: 'string',
errorMessage: {
type: 'App "name" should be a string.',
},
},
lowdefy: {
type: 'string',
errorMessage: {
type: 'Lowdefy version in field "lowdefy" should be a string.',
},
},
licence: {
type: 'string',
errorMessage: {
type: 'App "licence" should be a string.',
},
},
app: {
$ref: '#/definitions/app',
},
cli: {
type: 'object',
errorMessage: {
type: 'App "cli" should be an object.',
},
},
config: {
type: 'object',
errorMessage: {
type: 'App "config" should be an object.',
},
additionalProperties: false,
properties: {
homePageId: {
type: 'string',
description:
'Page id to use as homepage. When visiting home route "/", the router will redirect to this page. If not provided, the first page in default or first menu will be used as the homePageId.',
errorMessage: {
type: 'App "config.homePageId" should be a string.',
},
},
auth: {
$ref: '#/definitions/authConfig',
},
},
},
types: {
type: 'object',
patternProperties: {
'^.*$': {
url: 'string',
errorMessage: {
enum: 'Type "url" should be a string.',
},
},
},
errorMessage: {
type: 'App "types" should be an object.',
},
},
global: {
type: 'object',
errorMessage: {
type: 'App "global" should be an object.',
},
},
connections: {
type: 'array',
items: {
$ref: '#/definitions/connection',
},
errorMessage: {
type: 'App "connections" should be an array.',
},
},
menus: {
type: 'array',
items: {
$ref: '#/definitions/menu',
},
errorMessage: {
type: 'App "menus" should be an array.',
},
},
pages: {
type: 'array',
items: {
$ref: '#/definitions/block',
},
errorMessage: {
type: 'App "pages" should be an array.',
},
},
},
errorMessage: {
type: 'Lowdefy configuration should be an object.',
required: {
lowdefy: 'Lowdefy configuration should have required property "lowdefy".',
},
},
};

View File

@ -1,659 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://lowdefy.com/appSchema.json",
"type": "object",
"title": "Lowdefy App Schema",
"definitions": {
"action": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type"],
"properties": {
"async": {
"type": "boolean",
"errorMessage": {
"type": "Action \"async\" should be a boolean."
}
},
"id": {
"type": "string",
"errorMessage": {
"type": "Action \"id\" should be a string."
}
},
"messages": {},
"params": {},
"skip": {},
"type": {
"type": "string",
"errorMessage": {
"type": "Action \"type\" should be a string."
}
}
},
"errorMessage": {
"type": "Action should be an object.",
"required": {
"id": "Action should have required property \"id\".",
"type": "Action should have required property \"type\"."
}
}
},
"app": {
"type": "object",
"additionalProperties": false,
"properties": {
"html": {
"type": "object",
"errorMessage": {
"type": "App \"app.html\" should be an object."
},
"properties": {
"appendBody": {
"type": "string",
"errorMessage": {
"type": "App \"app.html.appendBody\" should be a string."
}
},
"appendHead": {
"type": "string",
"errorMessage": {
"type": "App \"app.html.appendHead\" should be a string."
}
}
}
}
}
},
"authConfig": {
"type": "object",
"additionalProperties": false,
"errorMessage": {
"type": "App \"config.auth\" should be an object."
},
"properties": {
"openId": {
"type": "object",
"additionalProperties": false,
"errorMessage": {
"type": "App \"config.auth.openId\" should be an object."
},
"properties": {
"rolesField": {
"type": "string",
"description": ".",
"errorMessage": {
"type": "App \"config.auth.openId.rolesField\" should be a string."
}
},
"logoutRedirectUri": {
"type": "string",
"description": "The URI to redirect the user to after logout. Can be a Nunjucks template string with client_id, host, id_token_hint, and openid_domain as template data.",
"errorMessage": {
"type": "App \"config.auth.openId.logoutRedirectUri\" should be a string."
}
},
"scope": {
"type": "string",
"description": "The OpenID Connect scope to request.",
"default": "openid profile email",
"errorMessage": {
"type": "App \"config.auth.openId.scope\" should be a string."
}
}
}
},
"pages": {
"type": "object",
"additionalProperties": false,
"errorMessage": {
"type": "App \"config.auth.pages\" should be an object."
},
"properties": {
"protected": {
"type": ["array", "boolean"],
"errorMessage": {
"type": "App \"config.auth.pages.protected.$\" should be an array of strings."
},
"items": {
"type": "string",
"description": "Page ids for which authentication is required. When specified, all unspecified pages will be public.",
"errorMessage": {
"type": "App \"config.auth.pages.protected.$\" should be an array of strings."
}
}
},
"public": {
"type": ["array", "boolean"],
"errorMessage": {
"type": "App \"config.auth.pages.public.$\" should be an array of strings."
},
"items": {
"type": "string",
"description": "Page ids for which authentication is not required. When specified, all unspecified pages will be protected.",
"errorMessage": {
"type": "App \"config.auth.pages.public.$\" should be an array of strings."
}
}
},
"roles": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "array",
"items": {
"type": "string"
},
"errorMessage": {
"type": "App \"config.auth.pages.roles.[role]\" should be an array of strings."
}
}
},
"errorMessage": {
"type": "App \"config.auth.pages.roles\" should be an object."
}
}
}
},
"jwt": {
"type": "object",
"additionalProperties": false,
"errorMessage": {
"type": "App \"config.auth.jwt\" should be an object."
},
"properties": {
"expiresIn": {
"type": ["string", "number"],
"default": "4h",
"description": "The length of time a user token should be valid. Can be expressed as a number in seconds, or a vercel/ms string (https://github.com/vercel/ms)",
"errorMessage": {
"type": "App \"config.auth.jwt.expiresIn\" should be a string or number."
}
},
"loginStateExpiresIn": {
"type": ["string", "number"],
"default": "5min",
"description": "The length of time an authorization request token should be valid. Can be expressed as a number in seconds, or a vercel/ms string (https://github.com/vercel/ms)",
"errorMessage": {
"type": "App \"config.auth.jwt.loginStateExpiresIn\" should be a string or number."
}
}
}
}
}
},
"block": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "Block \"id\" should be a string."
}
},
"type": {
"type": "string",
"errorMessage": {
"type": "Block \"type\" should be a string."
}
},
"field": {
"type": "string",
"errorMessage": {
"type": "Block \"field\" should be a string."
}
},
"properties": {
"type": "object"
},
"layout": {
"type": "object",
"errorMessage": {
"type": "Block \"layout\" should be an object."
}
},
"loading": {
"type": "object",
"errorMessage": {
"type": "Block \"loading\" should be an object."
}
},
"style": {
"type": "object",
"errorMessage": {
"type": "Block \"style\" should be an object."
}
},
"visible": {},
"blocks": {
"type": "array",
"items": {
"$ref": "#/definitions/block"
},
"errorMessage": {
"type": "Block \"blocks\" should be an array."
}
},
"requests": {
"type": "array",
"items": {
"$ref": "#/definitions/request"
},
"errorMessage": {
"type": "Block \"requests\" should be an array."
}
},
"required": {},
"validate": {
"type": "array",
"items": {
"type": "object",
"errorMessage": {
"type": "Block \"validate\" should be an array of objects."
}
},
"errorMessage": {
"type": "Block \"validate\" should be an array."
}
},
"events": {
"type": "object",
"patternProperties": {
"^.*$": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/definitions/action"
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"try": {
"type": "array",
"items": {
"$ref": "#/definitions/action"
}
},
"catch": {
"type": "array",
"items": {
"$ref": "#/definitions/action"
}
},
"debounce": {
"type": "object",
"additionalProperties": false,
"properties": {
"immediate": {
"type": "boolean",
"errorMessage": {
"type": "Event \"debounce.immediate\" should be an boolean."
}
},
"ms": {
"type": "number",
"errorMessage": {
"type": "Event \"debounce.ms\" should be a number."
}
}
}
}
}
}
]
}
},
"errorMessage": {
"type": "Block \"events\" should be an object."
}
},
"areas": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"properties": {
"blocks": {
"type": "array",
"items": {
"$ref": "#/definitions/block"
},
"errorMessage": {
"type": "Block \"areas.{areaKey}.blocks\" should be an array."
}
}
},
"errorMessage": {
"type": "Block \"areas.{areaKey}\" should be an object."
}
}
},
"errorMessage": {
"type": "Block \"areas\" should be an object."
}
}
},
"errorMessage": {
"type": "Block should be an object.",
"required": {
"id": "Block should have required property \"id\".",
"type": "Block should have required property \"type\"."
}
}
},
"connection": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "Connection \"id\" should be a string."
}
},
"type": {
"type": "string",
"errorMessage": {
"type": "Connection \"type\" should be a string."
}
},
"properties": {
"type": "object",
"errorMessage": {
"type": "Connection \"properties\" should be an object."
}
}
},
"errorMessage": {
"type": "Connection should be an object.",
"required": {
"id": "Connection should have required property \"id\".",
"type": "Connection should have required property \"type\"."
}
}
},
"menu": {
"type": "object",
"additionalProperties": false,
"required": ["id"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "Menu \"id\" should be a string."
}
},
"properties": {
"type": "object",
"errorMessage": {
"type": "Menu \"properties\" should be an object."
}
},
"links": {
"type": "array",
"items": {
"$ref": "#/definitions/menuItem"
},
"errorMessage": {
"type": "Menu \"links\" should be an array."
}
}
},
"errorMessage": {
"type": "Menu should be an object.",
"required": {
"id": "Menu should have required property \"id\"."
}
}
},
"menuGroup": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "MenuGroup \"id\" should be a string."
}
},
"type": {
"type": "string",
"errorMessage": {
"type": "MenuGroup \"type\" should be a string."
}
},
"properties": {
"type": "object",
"errorMessage": {
"type": "MenuGroup \"properties\" should be an object."
}
},
"links": {
"type": "array",
"items": {
"$ref": "#/definitions/menuItem"
},
"errorMessage": {
"type": "MenuGroup \"links\" should be an array."
}
}
},
"errorMessage": {
"type": "MenuGroup should be an object.",
"required": {
"id": "MenuGroup should have required property \"id\".",
"type": "MenuGroup should have required property \"type\"."
}
}
},
"menuItem": {
"anyOf": [
{
"$ref": "#/definitions/menuGroup"
},
{
"$ref": "#/definitions/menuLink"
}
]
},
"menuLink": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "MenuLink \"id\" should be a string."
}
},
"type": {
"type": "string",
"errorMessage": {
"type": "MenuLink \"type\" should be a string."
}
},
"pageId": {
"type": "string",
"errorMessage": {
"type": "MenuLink \"pageId\" should be a string."
}
},
"url": {
"type": "string",
"errorMessage": {
"type": "MenuLink \"url\" should be a string."
}
},
"properties": {
"type": "object",
"errorMessage": {
"type": "MenuLink \"properties\" should be an object."
}
}
},
"errorMessage": {
"type": "MenuLink should be an object.",
"required": {
"id": "MenuLink should have required property \"id\".",
"type": "MenuLink should have required property \"type\"."
}
}
},
"request": {
"type": "object",
"additionalProperties": false,
"required": ["id", "type", "connectionId"],
"properties": {
"id": {
"type": "string",
"errorMessage": {
"type": "Request \"id\" should be a string."
}
},
"type": {
"type": "string",
"errorMessage": {
"type": "Request \"type\" should be a string."
}
},
"connectionId": {
"type": "string",
"errorMessage": {
"type": "Request \"connectionId\" should be a string."
}
},
"payload": {
"type": "object",
"errorMessage": {
"type": "Request \"payload\" should be an object."
}
},
"properties": {
"type": "object",
"errorMessage": {
"type": "Request \"properties\" should be an object."
}
}
},
"errorMessage": {
"type": "Request should be an object.",
"required": {
"id": "Request should have required property \"id\".",
"type": "Request should have required property \"type\".",
"connectionId": "Request should have required property \"connectionId\"."
}
}
}
},
"additionalProperties": false,
"required": ["lowdefy"],
"properties": {
"name": {
"type": "string",
"errorMessage": {
"type": "App \"name\" should be a string."
}
},
"lowdefy": {
"type": "string",
"errorMessage": {
"type": "Lowdefy version in field \"lowdefy\" should be a string."
}
},
"licence": {
"type": "string",
"errorMessage": {
"type": "App \"licence\" should be a string."
}
},
"app": {
"$ref": "#/definitions/app"
},
"cli": {
"type": "object",
"errorMessage": {
"type": "App \"cli\" should be an object."
}
},
"config": {
"type": "object",
"errorMessage": {
"type": "App \"config\" should be an object."
},
"additionalProperties": false,
"properties": {
"homePageId": {
"type": "string",
"description": "Page id to use as homepage. When visiting home route \"/\", the router will redirect to this page. If not provided, the first page in default or first menu will be used as the homePageId.",
"errorMessage": {
"type": "App \"config.homePageId\" should be a string."
}
},
"auth": {
"$ref": "#/definitions/authConfig"
}
}
},
"types": {
"type": "object",
"patternProperties": {
"^.*$": {
"url": "string",
"errorMessage": {
"enum": "Type \"url\" should be a string."
}
}
},
"errorMessage": {
"type": "App \"types\" should be an object."
}
},
"global": {
"type": "object",
"errorMessage": {
"type": "App \"global\" should be an object."
}
},
"connections": {
"type": "array",
"items": {
"$ref": "#/definitions/connection"
},
"errorMessage": {
"type": "App \"connections\" should be an array."
}
},
"menus": {
"type": "array",
"items": {
"$ref": "#/definitions/menu"
},
"errorMessage": {
"type": "App \"menus\" should be an array."
}
},
"pages": {
"type": "array",
"items": {
"$ref": "#/definitions/block"
},
"errorMessage": {
"type": "App \"pages\" should be an array."
}
}
},
"errorMessage": {
"type": "Lowdefy configuration should be an object.",
"required": {
"lowdefy": "Lowdefy configuration should have required property \"lowdefy\"."
}
}
}

View File

@ -15,9 +15,11 @@
*/
import metaLocations from './metaLocations.js';
import packageJson from '../../../package.json';
// import packageJson from '../../../package.json';
const { version } = packageJson;
// const { version } = packageJson;
const version = '3.22.0';
test('metaLocations default URL', async () => {
expect(metaLocations({ version })).toEqual({

View File

@ -2,7 +2,7 @@
# Manual changes might be lost - proceed with caution!
__metadata:
version: 4
version: 5
cacheKey: 8
"@ant-design/colors@npm:^6.0.0":
@ -3921,77 +3921,77 @@ __metadata:
"@next/swc-android-arm64@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-android-arm64@npm:12.0.3"
checksum: 615c95d182685ec47ef58c377385b7aa8b460a3a70feb89123f24d58004cc5e1f5e37befb745846c920a5b6a4d69fc05ae81547264e5e0b4b11cc08f6dd89dd4
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@next/swc-darwin-arm64@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-darwin-arm64@npm:12.0.3"
checksum: f6c0bfbb099d530c7eca30a865fbbf01ff7abde58d3314e33d85ab3275238f07601f30b95876f3afe059caf8ec8ba41b19b8c3e4b92a6cf3c5c2bf9d21f768ac
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@next/swc-darwin-x64@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-darwin-x64@npm:12.0.3"
checksum: 22906cd7a86becf03082042847745e1666193269b389227ed078fc315bcdbaaa9dbd0725d010c346725f3c6f8591e6474a8b152e798fbb1975b02c015c378f61
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@next/swc-linux-arm-gnueabihf@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-linux-arm-gnueabihf@npm:12.0.3"
checksum: 3474c5681d77710a664eddb50d06f2df00436caea28df1ffb6ab58e4d1b9683896fa35aab645bee95d92b4484bb2a45bd16ee5628f3639a1a240785a5cc7f604
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@next/swc-linux-arm64-gnu@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-linux-arm64-gnu@npm:12.0.3"
checksum: ede87d91e5943a92a47184bca29fd9ce97e2a25d318307a83e64f2b45adde91505cc8ef8352b91799f0126e5d8faf528eb8e4ae9efa025016613bec8bd5e4eb5
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@next/swc-linux-arm64-musl@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-linux-arm64-musl@npm:12.0.3"
checksum: f6cbe1e7dd9593d849bc279bf4239cc4705e2e833561455208df5c6011badcf9be48f21bbb04ae629c86af87d56f3d18a573a6ed3577774c8decc3e0a3b9c9c3
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@next/swc-linux-x64-gnu@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-linux-x64-gnu@npm:12.0.3"
checksum: 0b0bc4b7c142f05d2db0ba97f1e3612e60c91a0e0da2ce72a6376622b91dbff99150fa0192f834abde36d462ef6e732db2145b93b380b931f9aa77ef9fbf39d1
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@next/swc-linux-x64-musl@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-linux-x64-musl@npm:12.0.3"
checksum: 6678c0a7b921a25ef2d0e11ee2317656dc56e3849bbd9f143a93b8745d483910693851a8956d4615bf1d8c355d38ed4e08a6141f577eeeb627f6ec0f20ddb757
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@next/swc-win32-arm64-msvc@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-win32-arm64-msvc@npm:12.0.3"
checksum: 0c2d9dddd331215fe4de34891ffd8fa072b245a65f64d770b119adaa2f5781b18a183a313acb4b2b8476f58aebf72ada9a86069e6e146af2273016ad48f711ad
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@next/swc-win32-ia32-msvc@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-win32-ia32-msvc@npm:12.0.3"
checksum: dc79d45f9ec027df828b3174a85a05d68fb6562e5407d4e4ebea9313649993d55c93451eb208f7a32623b77550a892806eef7a6c89b8a42d1347d43774863d5a
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@next/swc-win32-x64-msvc@npm:12.0.3":
version: 12.0.3
resolution: "@next/swc-win32-x64-msvc@npm:12.0.3"
checksum: 2c2f2540c8965dfc429b7a176328b03ee52ef5f8a08973238c55abbd50929c7745b71c4b0b9bdb2a4c54f3e877589f71e519db9c4b89e0ac2a22de7896e3272b
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@ -4451,84 +4451,84 @@ __metadata:
"@swc/core-android-arm64@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-android-arm64@npm:1.2.107"
checksum: a9588937181d177f48c4c3c061cc77a31871dd370e53e0baf8ec652c9e5f005b227e2e4f7978f3466c209d74946e514dc550a5f34c441edc4fa2c0f9fefa4cfa
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-darwin-arm64@npm:1.2.107"
checksum: 7db65a8c21478c86c5e93d6ca5db16fcdc2bdecd3e62e2962907fc51935937fc8ce867af0869efa1dc17f7d85aa95312a027ebb6919ff7a3951509554610aa1c
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@swc/core-darwin-x64@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-darwin-x64@npm:1.2.107"
checksum: 055bdc095dfc3138eb11efb758a8c4f50ab4abebae7fdfcfc8dd6ef25492b74e8b4a58265c640db9d90752049a1f55d611196342a762a6a29dbdfa2df60ae56b
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@swc/core-freebsd-x64@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-freebsd-x64@npm:1.2.107"
checksum: 073921a6d5ea2bd02779d932ee51b246ae37bcb3917f9d07fbc24e9a10d291a8ad50a6d4e8726b08dd65bf9530416cc59c4ae0ab8aa5b20016c9b227e5939116
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@swc/core-linux-arm-gnueabihf@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.2.107"
checksum: 7fb04edde2ef32ea599c1a04736a5d63e7d9ac800531effee110241bb8ad4df2c7898b39c02ac614ad6752b92608ae9e99e023365af2d9ce9314efe590c9978e
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@swc/core-linux-arm64-gnu@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-linux-arm64-gnu@npm:1.2.107"
checksum: cbfe4d5973948d60cb71c0faf95c935cfcef1cab9b4b3a577e0325f869adf3de487025c5d39ba6b67185fb268e1d6ccaed9d07950329fbf5c438f8003948134f
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@swc/core-linux-arm64-musl@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-linux-arm64-musl@npm:1.2.107"
checksum: c345e0dba1b890fa03df21037a2dc8313aab9243f755b138f3015e5231139b33729f08c1f0f68634cdd80b5f70606c889b43767c8c39ea84bcd8de63b58b9559
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@swc/core-linux-x64-gnu@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-linux-x64-gnu@npm:1.2.107"
checksum: 1f815379f2fa3e7514b67c41680db8c759a2ec991b8f923df02d5265a9b73e712e00aaa092f2fa389413a3b180ef428fcc9acd776634c50f9fc5fac497f169e8
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@swc/core-linux-x64-musl@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-linux-x64-musl@npm:1.2.107"
checksum: 2d5d3e594f23cd1da0abf988944265cb919cb312872a70e9d28a9eeb0e950ccb3dc113c331285062e1b4819d2f3104d0bae132fbea72ac6f07a5940cc9ed873c
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@swc/core-win32-arm64-msvc@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-win32-arm64-msvc@npm:1.2.107"
checksum: afc81079d1f9137831d3975bc553c51cbc2851fd9bc375d115f49704df258acc69586354240a691109750dbd77ff986b6e4ca74b7a29c21fc5a787fe70df5351
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@swc/core-win32-ia32-msvc@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-win32-ia32-msvc@npm:1.2.107"
checksum: 62533c010f1a8d77d7f2175afa0063e0671f1435adf70cc07bc575de4e31df4e7c07829cd52c84cc9344e154e25e8d45e5eb4035d293d021d161fb0970647aba
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@swc/core-win32-x64-msvc@npm:^1.2.107":
version: 1.2.107
resolution: "@swc/core-win32-x64-msvc@npm:1.2.107"
checksum: 95b731e92ba9ac8de0d4e8ec1d9226eacd8bea41d2ddcf6d4d180a1adb083d96683fc04809c1a1f7aad56815f71c942e102aeda972af9996455d098b7bc53941
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@ -10440,15 +10440,16 @@ __metadata:
dependencies:
node-gyp: latest
checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f
conditions: os=darwin
languageName: node
linkType: hard
"fsevents@patch:fsevents@^2.3.2#~builtin<compat/fsevents>, fsevents@patch:fsevents@~2.3.1#~builtin<compat/fsevents>, fsevents@patch:fsevents@~2.3.2#~builtin<compat/fsevents>":
version: 2.3.2
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=1cc4b2"
resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=18f3a7"
dependencies:
node-gyp: latest
checksum: 78db9daf1f6526a49cefee3917cc988f62dc7f25b5dd80ad6de4ffc4af7f0cab7491ac737626ff53e482a111bc53aac9e411fe3602458eca36f6a003ecf69c16
conditions: os=darwin
languageName: node
linkType: hard
@ -12676,7 +12677,7 @@ __metadata:
languageName: node
linkType: hard
"jest-resolve@npm:27.3.1, jest-resolve@npm:^27.3.1":
"jest-resolve@npm:^27.3.1":
version: 27.3.1
resolution: "jest-resolve@npm:27.3.1"
dependencies:
@ -18384,21 +18385,21 @@ resolve@^2.0.0-next.3:
"resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.12.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.14.2#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.9.0#~builtin<compat/resolve>":
version: 1.20.0
resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin<compat/resolve>::version=1.20.0&hash=00b1ff"
resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin<compat/resolve>::version=1.20.0&hash=07638b"
dependencies:
is-core-module: ^2.2.0
path-parse: ^1.0.6
checksum: bed00be983cd20a8af0e7840664f655c4b269786dbd9595c5f156cd9d8a0050e65cdbbbdafc30ee9b6245b230c78a2c8ab6447a52545b582f476c29adb188cc5
checksum: a0dd7d16a8e47af23afa9386df2dff10e3e0debb2c7299a42e581d9d9b04d7ad5d2c53f24f1e043f7b3c250cbdc71150063e53d0b6559683d37f790b7c8c3cd5
languageName: node
linkType: hard
"resolve@patch:resolve@^2.0.0-next.3#~builtin<compat/resolve>":
version: 2.0.0-next.3
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin<compat/resolve>::version=2.0.0-next.3&hash=00b1ff"
resolution: "resolve@patch:resolve@npm%3A2.0.0-next.3#~builtin<compat/resolve>::version=2.0.0-next.3&hash=07638b"
dependencies:
is-core-module: ^2.2.0
path-parse: ^1.0.6
checksum: eb88c5e53843bc022215744307a5f5664446c0fdb8f43c33456dce98d5ee6b3162d0cd0a177bb6f1c3d5c8bf01391ac7ab2de0e936e35318725fb40ba7efdaf6
checksum: 21684b4d99a4877337cdbd5484311c811b3e8910edb5d868eec85c6e6550b0f570d911f9a384f9e176172d6713f2715bd0b0887fa512cb8c6aeece018de6a9f8
languageName: node
linkType: hard