apostrophe 4.30.1-beta.1 → 4.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +15 -0
- package/CHANGELOG.md +22 -2
- package/claude-tools/detect-handles.js +46 -0
- package/claude-tools/minimal-hang-test.js +28 -0
- package/claude-tools/mongo-close-test.js +11 -0
- package/claude-tools/stdin-ref-test.js +14 -0
- package/eslint.config.js +3 -1
- package/modules/@apostrophecms/area/index.js +94 -2
- package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -40
- package/modules/@apostrophecms/area/ui/apos/components/AposBreadcrumbOperations.vue +0 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +0 -1
- package/modules/@apostrophecms/attachment/index.js +4 -1
- package/modules/@apostrophecms/db/index.js +68 -27
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +5 -3
- package/modules/@apostrophecms/express/index.js +2 -0
- package/modules/@apostrophecms/file/index.js +9 -8
- package/modules/@apostrophecms/http/index.js +1 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +3 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +2 -2
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +3 -0
- package/modules/@apostrophecms/job/index.js +9 -7
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridColumn.vue +0 -1
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridManager.vue +0 -1
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +10 -2
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +3 -3
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +52 -23
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +6 -1
- package/modules/@apostrophecms/oembed/index.js +2 -1
- package/modules/@apostrophecms/piece-type/index.js +2 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +7 -2
- package/modules/@apostrophecms/recently-edited/ui/apos/components/AposCellTitle.vue +1 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +21 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +7 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +10 -0
- package/modules/@apostrophecms/styles/ui/apos/components/TheAposStyles.vue +1 -0
- package/modules/@apostrophecms/template/index.js +117 -11
- package/modules/@apostrophecms/template/lib/jsxLoader.js +128 -0
- package/modules/@apostrophecms/template/lib/jsxRender.js +490 -0
- package/modules/@apostrophecms/template/lib/jsxRuntime.js +276 -0
- package/modules/@apostrophecms/template/lib/nunjucksLoader.js +11 -36
- package/modules/@apostrophecms/template/lib/viewWatcher.js +113 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellLastEdited.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSelect.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +10 -4
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +6 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +2 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
- package/modules/@apostrophecms/uploadfs/index.js +3 -0
- package/modules/@apostrophecms/util/index.js +20 -3
- package/package.json +14 -10
- package/test/add-missing-schema-fields-project/node_modules/.package-lock.json +131 -0
- package/test/add-missing-schema-fields-project/test.js +22 -3
- package/test/assets.js +110 -67
- package/test/db-tools.js +365 -0
- package/test/db.js +24 -15
- package/test/default-adapter.js +256 -0
- package/test/external-front.js +419 -1
- package/test/files.js +28 -0
- package/test/job.js +1 -1
- package/test/modules/jsx-area-test/index.js +23 -0
- package/test/modules/jsx-area-test/views/bad-area.jsx +7 -0
- package/test/modules/jsx-area-test/views/with-area-ctx.jsx +13 -0
- package/test/modules/jsx-area-test/views/with-area.jsx +7 -0
- package/test/modules/jsx-area-test/views/with-widget-ctx.jsx +12 -0
- package/test/modules/jsx-area-test/views/with-widget.jsx +7 -0
- package/test/modules/jsx-async-widget/index.js +6 -0
- package/test/modules/jsx-async-widget/views/widget.jsx +11 -0
- package/test/modules/jsx-bridge-test/index.js +1 -0
- package/test/modules/jsx-bridge-test/views/cross-module.jsx +7 -0
- package/test/modules/jsx-bridge-test/views/disambig-name-only.jsx +7 -0
- package/test/modules/jsx-bridge-test/views/disambig-target.jsx +8 -0
- package/test/modules/jsx-bridge-test/views/disambig-with-template-name.jsx +7 -0
- package/test/modules/jsx-bridge-test/views/include-html.jsx +7 -0
- package/test/modules/jsx-bridge-test/views/include-target.html +4 -0
- package/test/modules/jsx-bridge-test/views/jsx-extends-via-extend.jsx +9 -0
- package/test/modules/jsx-bridge-test/views/jsx-extends.jsx +9 -0
- package/test/modules/jsx-bridge-test/views/jsx-layout.jsx +14 -0
- package/test/modules/jsx-bridge-test/views/njk-extends.jsx +14 -0
- package/test/modules/jsx-bridge-test/views/njk-layout.html +9 -0
- package/test/modules/jsx-bridge-test/views/short-form.jsx +7 -0
- package/test/modules/jsx-bridge-test/views/short-target.jsx +3 -0
- package/test/modules/jsx-component-test/index.js +15 -0
- package/test/modules/jsx-component-test/views/greet.html +1 -0
- package/test/modules/jsx-component-test/views/uses-component.jsx +8 -0
- package/test/modules/jsx-ctx-widget/index.js +6 -0
- package/test/modules/jsx-ctx-widget/views/widget.jsx +4 -0
- package/test/modules/jsx-mixed-test/index.js +9 -0
- package/test/modules/jsx-mixed-test/views/apos-full.jsx +21 -0
- package/test/modules/jsx-mixed-test/views/async-list.jsx +12 -0
- package/test/modules/jsx-mixed-test/views/lib/format.js +3 -0
- package/test/modules/jsx-mixed-test/views/localized.jsx +3 -0
- package/test/modules/jsx-mixed-test/views/partial.jsx +3 -0
- package/test/modules/jsx-mixed-test/views/safe-helper.jsx +3 -0
- package/test/modules/jsx-mixed-test/views/syntax-error.jsx +3 -0
- package/test/modules/jsx-mixed-test/views/throws.jsx +5 -0
- package/test/modules/jsx-mixed-test/views/uses-import.jsx +5 -0
- package/test/modules/jsx-mixed-test/views/uses-require.jsx +5 -0
- package/test/modules/jsx-watcher-cross-test/index.js +5 -0
- package/test/modules/jsx-watcher-cross-test/views/cross-template.jsx +3 -0
- package/test/modules/jsx-watcher-test/index.js +5 -0
- package/test/modules/jsx-watcher-test/views/watcher-test.jsx +3 -0
- package/test/modules/template-jsx-options-test/index.js +12 -0
- package/test/modules/template-jsx-options-test/views/options-test.jsx +9 -0
- package/test/modules/template-jsx-subclass-test/index.js +3 -0
- package/test/modules/template-jsx-subclass-test/views/override-test.jsx +3 -0
- package/test/modules/template-jsx-test/index.js +9 -0
- package/test/modules/template-jsx-test/views/boolean-attrs.jsx +11 -0
- package/test/modules/template-jsx-test/views/class-and-for.jsx +7 -0
- package/test/modules/template-jsx-test/views/dangerously-set.jsx +3 -0
- package/test/modules/template-jsx-test/views/escape-attr.jsx +3 -0
- package/test/modules/template-jsx-test/views/escape-body.jsx +3 -0
- package/test/modules/template-jsx-test/views/inherit-test.jsx +3 -0
- package/test/modules/template-jsx-test/views/list.jsx +7 -0
- package/test/modules/template-jsx-test/views/override-test.jsx +3 -0
- package/test/modules/template-jsx-test/views/svg-attrs.jsx +27 -0
- package/test/modules/template-jsx-test/views/test.jsx +3 -0
- package/test/modules/template-jsx-test/views/void-elements.jsx +9 -0
- package/test/templates-jsx-watcher.js +135 -0
- package/test/templates-jsx.js +537 -0
- package/test/utils.js +103 -0
- package/test-lib/util.js +50 -14
- package/lib/mongodb-connect.js +0 -62
package/test-lib/util.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
const { createId } = require('@paralleldrive/cuid2');
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
const testDbProtocol = process.env.APOS_TEST_DB_PROTOCOL || 'mongodb';
|
|
4
|
+
|
|
5
|
+
// Build a test database URI for postgres based on the shortName.
|
|
6
|
+
// Returns undefined for mongodb, letting the default logic handle it.
|
|
7
|
+
function getTestDbUri(shortName) {
|
|
8
|
+
if (testDbProtocol === 'postgres') {
|
|
9
|
+
// PostgreSQL database names cannot contain hyphens
|
|
10
|
+
const dbName = shortName.replace(/-/g, '_');
|
|
11
|
+
return `postgres://localhost:5432/${dbName}`;
|
|
12
|
+
}
|
|
13
|
+
if (testDbProtocol === 'multipostgres') {
|
|
14
|
+
// Multi-schema mode: shared real database, per-test schema
|
|
15
|
+
const schemaName = shortName.replace(/-/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
|
|
16
|
+
return `multipostgres://localhost:5432/apos_test-${schemaName}`;
|
|
17
|
+
}
|
|
18
|
+
if (testDbProtocol === 'sqlite') {
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const dbName = shortName.replace(/-/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
|
|
22
|
+
return `sqlite://${path.join(os.tmpdir(), `apos_test_${dbName}.db`)}`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
3
25
|
|
|
4
26
|
// Properly clean up an apostrophe instance and drop its
|
|
5
27
|
// database collections to create a sane environment for the next test.
|
|
@@ -10,23 +32,23 @@ const mongodbConnect = require('../lib/mongodb-connect');
|
|
|
10
32
|
// If `apos` is null, no work is done.
|
|
11
33
|
|
|
12
34
|
async function destroy(apos) {
|
|
13
|
-
if (!apos) {
|
|
35
|
+
if (!apos || apos._destroyed) {
|
|
14
36
|
return;
|
|
15
37
|
}
|
|
38
|
+
apos._destroyed = true;
|
|
39
|
+
const dbModule = apos.modules['@apostrophecms/db'];
|
|
40
|
+
const { uri } = dbModule;
|
|
41
|
+
const dbName = apos.db && (apos.db.databaseName || apos.db._name);
|
|
16
42
|
await apos.destroy();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// TODO at some point accommodate nonsense like testing remote databases
|
|
20
|
-
// that won't let us use dropDatabase, no shell available etc., but the
|
|
21
|
-
// important principle here is that we should not have to have an apos
|
|
22
|
-
// object to clean up the database, otherwise we have to get hold of one
|
|
23
|
-
// when initialization failed and that's really not apostrophe's concern
|
|
24
|
-
if (dbName && uri) {
|
|
25
|
-
const client = await mongodbConnect(`${uri}${dbName}`);
|
|
26
|
-
const db = client.db(dbName);
|
|
27
|
-
await db.dropDatabase();
|
|
28
|
-
await client.close();
|
|
43
|
+
if (!uri || !dbName) {
|
|
44
|
+
return;
|
|
29
45
|
}
|
|
46
|
+
// Make a fresh connection (the original was closed by destroy)
|
|
47
|
+
// and use it to drop the test database
|
|
48
|
+
const client = await dbModule.connectToAdapter(uri);
|
|
49
|
+
const db = client.db(dbName);
|
|
50
|
+
await db.dropDatabase();
|
|
51
|
+
await client.close();
|
|
30
52
|
};
|
|
31
53
|
|
|
32
54
|
async function create(options = {}) {
|
|
@@ -55,6 +77,18 @@ async function create(options = {}) {
|
|
|
55
77
|
express.options.session.secret = express.options.session.secret || 'test';
|
|
56
78
|
config.modules['@apostrophecms/express'] = express;
|
|
57
79
|
}
|
|
80
|
+
// When APOS_TEST_DB_PROTOCOL=postgres, automatically configure the db
|
|
81
|
+
// module to use a postgres URI unless already explicitly configured
|
|
82
|
+
const testUri = getTestDbUri(config.shortName);
|
|
83
|
+
if (testUri) {
|
|
84
|
+
config.modules = config.modules || {};
|
|
85
|
+
const dbModule = config.modules['@apostrophecms/db'] || {};
|
|
86
|
+
dbModule.options = dbModule.options || {};
|
|
87
|
+
if (!dbModule.options.uri && !dbModule.options.client) {
|
|
88
|
+
dbModule.options.uri = testUri;
|
|
89
|
+
}
|
|
90
|
+
config.modules['@apostrophecms/db'] = dbModule;
|
|
91
|
+
}
|
|
58
92
|
return require('../index.js')(config);
|
|
59
93
|
}
|
|
60
94
|
|
|
@@ -151,5 +185,7 @@ module.exports = {
|
|
|
151
185
|
loginAs,
|
|
152
186
|
logout,
|
|
153
187
|
getUserJar,
|
|
188
|
+
getTestDbUri,
|
|
189
|
+
testDbProtocol,
|
|
154
190
|
timeout: (process.env.TEST_TIMEOUT && parseInt(process.env.TEST_TIMEOUT)) || 20000
|
|
155
191
|
};
|
package/lib/mongodb-connect.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const mongo = require('@apostrophecms/emulate-mongo-3-driver');
|
|
2
|
-
const dns = require('dns');
|
|
3
|
-
|
|
4
|
-
// Connect to MongoDB, using the modern topology and parser, and
|
|
5
|
-
// a tolerant policy to successfully connect to "localhost" even if
|
|
6
|
-
// the first record returned by the resolver doesn't reach mongodb's
|
|
7
|
-
// bind address because localhost resolves first to ::1 (ipv6) and
|
|
8
|
-
// mongodb lists only on 127.0.0.1 (ipv4) by default. For broadest
|
|
9
|
-
// compatibility we don't assume we know this will happen, we try all the
|
|
10
|
-
// addresses that localhost actually resolves to and succeed with the
|
|
11
|
-
// first one that works.
|
|
12
|
-
|
|
13
|
-
module.exports = async (uri, options) => {
|
|
14
|
-
const connectOptions = {
|
|
15
|
-
useUnifiedTopology: true,
|
|
16
|
-
useNewUrlParser: true,
|
|
17
|
-
...options
|
|
18
|
-
};
|
|
19
|
-
let parsed;
|
|
20
|
-
try {
|
|
21
|
-
parsed = new URL(uri);
|
|
22
|
-
} catch (e) {
|
|
23
|
-
// Parse failed, e.g. old school replica set URI
|
|
24
|
-
// with commas, just let the mongo driver handle it
|
|
25
|
-
return mongo.MongoClient.connect(uri, connectOptions);
|
|
26
|
-
}
|
|
27
|
-
if (!parsed || (parsed.protocol !== 'mongodb:') || (parsed.hostname !== 'localhost')) {
|
|
28
|
-
return mongo.MongoClient.connect(parsed.toString(), connectOptions);
|
|
29
|
-
}
|
|
30
|
-
const records = await dns.promises.lookup('localhost', { all: true });
|
|
31
|
-
if (!records.length) {
|
|
32
|
-
// The computer that reaches this point has bigger problems 😅
|
|
33
|
-
throw new Error('Unable to resolve localhost to an IP address.');
|
|
34
|
-
}
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
let failed = 0;
|
|
37
|
-
let succeeded = false;
|
|
38
|
-
records.forEach(attempt);
|
|
39
|
-
async function attempt(record) {
|
|
40
|
-
try {
|
|
41
|
-
const parsed = new URL(uri);
|
|
42
|
-
parsed.hostname = record.address;
|
|
43
|
-
const result = await mongo.MongoClient.connect(parsed.toString(), connectOptions);
|
|
44
|
-
if (!succeeded) {
|
|
45
|
-
succeeded = true;
|
|
46
|
-
resolve(result);
|
|
47
|
-
} else {
|
|
48
|
-
// We succeeded in reaching localhost at both ip4 and ip6,
|
|
49
|
-
// but we only need one of them to succeed
|
|
50
|
-
await result.close();
|
|
51
|
-
}
|
|
52
|
-
} catch (e) {
|
|
53
|
-
failed++;
|
|
54
|
-
if (failed === records.length) {
|
|
55
|
-
// None succeeded, so reject with the last error
|
|
56
|
-
// (which one we reject with doesn't really matter)
|
|
57
|
-
reject(e);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
};
|