@cocreate/plugins 1.3.0 → 1.4.1
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/.github/workflows/automated.yml +23 -11
- package/CHANGELOG.md +28 -0
- package/demo/index.html +18 -70
- package/package.json +12 -14
- package/release.config.js +11 -3
- package/src/index.js +174 -168
- package/src/operators.js +293 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
name: Automated Workflow
|
|
2
|
+
|
|
2
3
|
on:
|
|
3
4
|
push:
|
|
4
5
|
branches:
|
|
5
6
|
- master
|
|
7
|
+
|
|
6
8
|
jobs:
|
|
7
9
|
about:
|
|
8
10
|
runs-on: ubuntu-latest
|
|
@@ -18,27 +20,37 @@ jobs:
|
|
|
18
20
|
with:
|
|
19
21
|
direction: overwrite-github
|
|
20
22
|
githubToken: "${{ secrets.GITHUB }}"
|
|
23
|
+
|
|
21
24
|
release:
|
|
22
25
|
runs-on: ubuntu-latest
|
|
23
26
|
steps:
|
|
24
27
|
- name: Checkout
|
|
25
28
|
uses: actions/checkout@v4
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0 # Required so semantic-release can trace git tags/history
|
|
31
|
+
|
|
26
32
|
- name: Setup Node.js
|
|
27
33
|
uses: actions/setup-node@v4
|
|
28
34
|
with:
|
|
29
|
-
node-version: 22
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@semantic-release/changelog
|
|
35
|
+
node-version: 22
|
|
36
|
+
|
|
37
|
+
- name: Install Semantic Release & Plugins
|
|
38
|
+
# Installs semantic-release and its plugins on the runner
|
|
39
|
+
run: |
|
|
40
|
+
npm install -g semantic-release \
|
|
41
|
+
@semantic-release/changelog \
|
|
42
|
+
@semantic-release/npm \
|
|
43
|
+
@semantic-release/github \
|
|
36
44
|
@semantic-release/git
|
|
37
|
-
|
|
45
|
+
|
|
46
|
+
- name: Run Semantic Release (Native)
|
|
47
|
+
id: semantic
|
|
48
|
+
# This will automatically pick up your export default config file in the repository root
|
|
49
|
+
run: npx semantic-release
|
|
38
50
|
env:
|
|
39
|
-
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
51
|
+
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
40
52
|
NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
|
|
53
|
+
|
|
41
54
|
outputs:
|
|
42
55
|
new_release_published: "${{ steps.semantic.outputs.new_release_published }}"
|
|
43
|
-
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
|
44
|
-
|
|
56
|
+
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## [1.4.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.4.0...v1.4.1) (2026-07-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* bump dependencies ([0bcc639](https://github.com/CoCreate-app/CoCreate-plugins/commit/0bcc6392a3f033f26f9d8d1145e10a6df7de7ab2))
|
|
7
|
+
|
|
8
|
+
# [1.4.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.3.1...v1.4.0) (2026-07-17)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* enhance plugin initialization and resource preloading logic ([ee17bc1](https://github.com/CoCreate-app/CoCreate-plugins/commit/ee17bc12b47c09085cd2527ef18a6fce36f21123))
|
|
14
|
+
* improve plugin processing and operator execution logic ([486faa8](https://github.com/CoCreate-app/CoCreate-plugins/commit/486faa81839a49d4958ccb75721b161102cc0168))
|
|
15
|
+
* update automated workflow and release configuration for improved semantic release handling ([06e65c9](https://github.com/CoCreate-app/CoCreate-plugins/commit/06e65c9eddd29398f3bd824e295089722faf9fad))
|
|
16
|
+
|
|
17
|
+
## [1.3.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.3.0...v1.3.1) (2026-07-11)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* bump package versions ([3f992ea](https://github.com/CoCreate-app/CoCreate-plugins/commit/3f992ea145328efac344e4f470fe19a87d5209e9))
|
|
23
|
+
* clean attribute name for method matching in processPlugin function ([7389d12](https://github.com/CoCreate-app/CoCreate-plugins/commit/7389d12bd6f4206c506456d64341ceef4b304313))
|
|
24
|
+
* correct minor typos in code comments and variable names for clarity. ([7e43622](https://github.com/CoCreate-app/CoCreate-plugins/commit/7e4362289c9f51ac6673873802d5b58a7992638e))
|
|
25
|
+
* demo plugin initialization syntax and remove redundant tests ([c126bc8](https://github.com/CoCreate-app/CoCreate-plugins/commit/c126bc87edfb9669ea6c8a79e13a0cd0c09a1bd3))
|
|
26
|
+
* removed post install and add add packages to apprved list ([548be97](https://github.com/CoCreate-app/CoCreate-plugins/commit/548be977ba710af907ad8e95f7ebcd3a75462326))
|
|
27
|
+
* semantic version handling ([7606d54](https://github.com/CoCreate-app/CoCreate-plugins/commit/7606d54cd447a3cf10c3f86d54bca1f0aa7fe1a1))
|
|
28
|
+
|
|
1
29
|
# [1.3.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.2.1...v1.3.0) (2026-03-11)
|
|
2
30
|
|
|
3
31
|
|
package/demo/index.html
CHANGED
|
@@ -38,83 +38,31 @@
|
|
|
38
38
|
<p class="text-secondary small">This button sets multiple properties on the Toastify instance and then calls the execution method, all in order.</p>
|
|
39
39
|
<div class="live-preview flex-column">
|
|
40
40
|
<button type="button" class="btn btn-primary px-4"
|
|
41
|
-
plugin=
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
plugin='{
|
|
42
|
+
"Toastify": {
|
|
43
|
+
"text": "Hello from a single object!",
|
|
44
|
+
"duration": 3000,
|
|
45
|
+
"style": {
|
|
46
|
+
"background": "#0d6efd"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"Toastify.showToast": []
|
|
50
|
+
}'>
|
|
46
51
|
Trigger Sequential Toast
|
|
47
52
|
</button>
|
|
48
53
|
<div class="code-block w-100">
|
|
49
|
-
plugin=
|
|
50
|
-
Toastify
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
plugin='{<br>
|
|
55
|
+
"Toastify": {<br>
|
|
56
|
+
"text": "...",<br>
|
|
57
|
+
"duration": 3000<br>
|
|
58
|
+
},<br>
|
|
59
|
+
"Toastify.showToast": []<br>
|
|
60
|
+
}'
|
|
61
|
+
</div>
|
|
53
62
|
</div>
|
|
54
63
|
</div>
|
|
55
64
|
</div>
|
|
56
65
|
|
|
57
|
-
<!-- TEST 2: JSON Object + Method Call -->
|
|
58
|
-
<div class="card">
|
|
59
|
-
<div class="card-header">2. Object-Based Initialization</div>
|
|
60
|
-
<div class="card-body">
|
|
61
|
-
<p class="text-secondary small">Initializing using a full JSON object, followed immediately by a method call.</p>
|
|
62
|
-
<div class="live-preview flex-column">
|
|
63
|
-
<button type="button" class="btn btn-dark px-4"
|
|
64
|
-
plugin="Toastify"
|
|
65
|
-
Toastify()='{"text": "JSON Object Booted!", "gravity": "bottom", "position": "left"}'
|
|
66
|
-
Toastify.showToast()="">
|
|
67
|
-
Trigger JSON Boot
|
|
68
|
-
</button>
|
|
69
|
-
<div class="code-block w-100">
|
|
70
|
-
plugin="Toastify" <br>
|
|
71
|
-
Toastify='{"text": "..."}' <br>
|
|
72
|
-
$Toastify.showToast()=""
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<!-- TEST 3: Interleaved $this and Plugin Logic -->
|
|
79
|
-
<div class="card">
|
|
80
|
-
<div class="card-header">3. Interleaved Element & Plugin Logic</div>
|
|
81
|
-
<div class="card-body">
|
|
82
|
-
<p class="text-secondary small">This test changes the button's own style using <code>$this</code> before triggering the plugin logic.</p>
|
|
83
|
-
<div class="live-preview flex-column">
|
|
84
|
-
<button type="button" class="btn btn-outline-danger px-4"
|
|
85
|
-
$this.style.borderRadius="0px"
|
|
86
|
-
$this.innerText="Order Processed"
|
|
87
|
-
plugin="Toastify"
|
|
88
|
-
Toastify.text="Element changed first!"
|
|
89
|
-
Toastify.showToast()>
|
|
90
|
-
Change Me & Toast
|
|
91
|
-
</button>
|
|
92
|
-
<div class="code-block w-100">
|
|
93
|
-
$this.style.borderRadius="0px" <br>
|
|
94
|
-
plugin="Toastify" <br>
|
|
95
|
-
Toastify.showToast()=""
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<!-- TEST 4: Input State Manipulation -->
|
|
102
|
-
<div class="card">
|
|
103
|
-
<div class="card-header">4. Input Property Manipulation</div>
|
|
104
|
-
<div class="card-body">
|
|
105
|
-
<p class="text-secondary small">Directly setting properties on an input element on load.</p>
|
|
106
|
-
<div class="live-preview">
|
|
107
|
-
<input type="text" class="form-control"
|
|
108
|
-
$this.value="CoCreate Linear Engine"
|
|
109
|
-
$this.style.border="2px solid #0d6efd"
|
|
110
|
-
$this.placeholder="Wait for it..."
|
|
111
|
-
plugin="Toastify"
|
|
112
|
-
Toastify.text="Input properties applied"
|
|
113
|
-
Toastify.duration="2000"
|
|
114
|
-
Toastify.showToast()="">
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
66
|
|
|
119
67
|
</div>
|
|
120
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/plugins",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "CoCreate plugins",
|
|
5
5
|
"author": "CoCreate LLC",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"start": "
|
|
12
|
-
"build": "
|
|
13
|
-
"dev": "
|
|
14
|
-
"postinstall": "node -e \"const { execSync } = require('child_process'); try { execSync('coc --version', { stdio: 'ignore' }); } catch (error) { try { execSync('npm install -g @cocreate/cli', { stdio: 'inherit' }); console.log('Installed \"@cocreate/cli\" globally.'); } catch (error) { console.error('Failed to install \"@cocreate/cli\" globally:', error); } }\""
|
|
11
|
+
"start": "webpack --config webpack.config.js",
|
|
12
|
+
"build": "webpack --mode=production --config webpack.config.js",
|
|
13
|
+
"dev": "webpack --config webpack.config.js --watch"
|
|
15
14
|
},
|
|
16
15
|
"repository": {
|
|
17
16
|
"type": "git",
|
|
@@ -26,16 +25,15 @@
|
|
|
26
25
|
"url": "https://github.com/sponsors/CoCreate-app"
|
|
27
26
|
},
|
|
28
27
|
"main": "./src/index.js",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@cocreate/observer": "^1.20.0"
|
|
30
|
+
},
|
|
29
31
|
"devDependencies": {
|
|
30
|
-
"
|
|
31
|
-
"esbuild": "^0.25.2",
|
|
32
|
-
"esbuild-loader": "^4.3.0",
|
|
33
|
-
"mini-css-extract-plugin": "^1.5.0",
|
|
34
|
-
"webpack": "^5.24.4",
|
|
35
|
-
"webpack-cli": "^4.5.0",
|
|
36
|
-
"webpack-log": "^3.0.1"
|
|
32
|
+
"@cocreate/webpack": "^1.6.0"
|
|
37
33
|
},
|
|
38
|
-
"
|
|
39
|
-
"@cocreate/observer
|
|
34
|
+
"allowScripts": {
|
|
35
|
+
"@cocreate/observer@1.19.0": true,
|
|
36
|
+
"@cocreate/utils@1.42.2": true,
|
|
37
|
+
"@cocreate/webpack@1.4.3": true
|
|
40
38
|
}
|
|
41
39
|
}
|
package/release.config.js
CHANGED
|
@@ -10,12 +10,20 @@ module.exports = {
|
|
|
10
10
|
changelogFile: "CHANGELOG.md",
|
|
11
11
|
},
|
|
12
12
|
],
|
|
13
|
-
"@semantic-release/npm",
|
|
14
|
-
"@semantic-release/github",
|
|
13
|
+
"@semantic-release/npm",
|
|
15
14
|
[
|
|
16
|
-
"@semantic-release/
|
|
15
|
+
"@semantic-release/github",
|
|
17
16
|
{
|
|
17
|
+
successComment: false,
|
|
18
|
+
failTitle: false,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"@semantic-release/git",
|
|
23
|
+
{
|
|
24
|
+
// Only stage and commit the changelog and package.json
|
|
18
25
|
assets: ["CHANGELOG.md", "package.json"],
|
|
26
|
+
message: "chore(release): ${nextRelease.version} [skip ci]",
|
|
19
27
|
},
|
|
20
28
|
],
|
|
21
29
|
],
|
package/src/index.js
CHANGED
|
@@ -1,51 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
3
18
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* @property {Array<string|Object>} [js] - List of JS files to load.
|
|
7
|
-
* @property {Array<string>} [css] - List of CSS files to load.
|
|
8
|
-
*/
|
|
19
|
+
import Observer from "@cocreate/observer";
|
|
20
|
+
import { processOperators } from "./operators.js";
|
|
9
21
|
|
|
10
|
-
// --- CONFIGURATION ---
|
|
11
22
|
const plugins = {};
|
|
12
|
-
|
|
13
|
-
// --- CORE ENGINE ---
|
|
14
23
|
const scriptCache = new Map();
|
|
15
24
|
const cssMarker = typeof document !== 'undefined' ? document.querySelector('link[plugins]') : null;
|
|
16
25
|
|
|
17
26
|
/**
|
|
18
|
-
*
|
|
27
|
+
* Initializes plugins on a collection of HTML elements.
|
|
28
|
+
* @param {HTMLElement|HTMLElement[]|NodeList} elements
|
|
19
29
|
*/
|
|
20
|
-
function init(elements) {
|
|
30
|
+
export function init(elements) {
|
|
21
31
|
if (!elements) return;
|
|
22
|
-
|
|
32
|
+
const collection = (elements instanceof HTMLElement || elements instanceof Element)
|
|
33
|
+
? [elements]
|
|
34
|
+
: Array.from(elements);
|
|
35
|
+
|
|
23
36
|
collection.forEach(el => {
|
|
24
|
-
if (el
|
|
37
|
+
if (el) {
|
|
38
|
+
processPlugin(el);
|
|
39
|
+
}
|
|
25
40
|
});
|
|
26
41
|
}
|
|
27
42
|
|
|
28
|
-
|
|
29
|
-
* Processes resources and executes attributes in strict DOM order.
|
|
30
|
-
* The attribute list IS the script. We iterate once and execute everything in sequence.
|
|
31
|
-
*/
|
|
32
|
-
async function processPlugin(el) {
|
|
33
|
-
const rawAttr = el.getAttribute("plugin");
|
|
34
|
-
const pluginNames = rawAttr ? rawAttr.split(',').map(s => s.trim()) : [];
|
|
35
|
-
|
|
36
|
-
// 1. RESOURCE PRE-LOADING
|
|
43
|
+
async function preloadResources(pluginNames) {
|
|
37
44
|
for (const name of pluginNames) {
|
|
38
45
|
const pluginDef = plugins[name];
|
|
39
|
-
|
|
40
46
|
if (pluginDef && !window[name]) {
|
|
41
|
-
if (pluginDef.css)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if (pluginDef.css) {
|
|
48
|
+
pluginDef.css.forEach(href => {
|
|
49
|
+
if (!document.querySelector(`link[href="${href}"]`)) {
|
|
50
|
+
const link = document.createElement("link");
|
|
51
|
+
link.rel = "stylesheet";
|
|
52
|
+
link.href = href;
|
|
53
|
+
if (cssMarker) cssMarker.parentNode.insertBefore(link, cssMarker);
|
|
54
|
+
else document.head.appendChild(link);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
49
58
|
|
|
50
59
|
if (pluginDef.js) {
|
|
51
60
|
for (const item of pluginDef.js) {
|
|
@@ -70,162 +79,161 @@ async function processPlugin(el) {
|
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
}
|
|
82
|
+
}
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
async function processPlugin(el) {
|
|
85
|
+
if (!el.attributes) return;
|
|
86
|
+
|
|
87
|
+
// Direct attributes collection map lookup to avoid binding/context prototype errors
|
|
88
|
+
const pluginAttrNode = el.attributes['plugin'] || el.attributes.getNamedItem?.('plugin');
|
|
89
|
+
const rawPluginAttr = pluginAttrNode?.value?.trim();
|
|
90
|
+
if (!rawPluginAttr) return;
|
|
91
|
+
|
|
92
|
+
const context = {
|
|
93
|
+
element: el,
|
|
94
|
+
this: el,
|
|
95
|
+
plugin: null, // Tracks active sibling plugin instance
|
|
96
|
+
data: { ...el.dataset },
|
|
97
|
+
event: {},
|
|
98
|
+
crud: window.CoCreate?.crud || null
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
let configs = [];
|
|
103
|
+
|
|
104
|
+
// Unified Direct Parser: Pure JSON parsing with simple zero-argument fallback
|
|
105
|
+
if (rawPluginAttr.startsWith('{') || rawPluginAttr.startsWith('[')) {
|
|
106
|
+
const parsedConfig = JSON.parse(rawPluginAttr);
|
|
107
|
+
configs = Array.isArray(parsedConfig) ? parsedConfig : [parsedConfig];
|
|
108
|
+
} else {
|
|
109
|
+
// Symmetrical Fallback: Plain strings mapped straight to constructor blocks
|
|
110
|
+
configs = [{ [rawPluginAttr]: [] }];
|
|
85
111
|
}
|
|
112
|
+
|
|
113
|
+
const targetPlugins = new Set();
|
|
114
|
+
configs.forEach(conf => {
|
|
115
|
+
Object.keys(conf).forEach(k => {
|
|
116
|
+
if (!k.startsWith('$')) {
|
|
117
|
+
targetPlugins.add(k.split('.')[0]);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await preloadResources(Array.from(targetPlugins));
|
|
86
123
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
// Execute each configuration block sequentially
|
|
125
|
+
for (const configBlock of configs) {
|
|
126
|
+
await executeConfigBlock(context, configBlock);
|
|
127
|
+
}
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.error("[Plugin System] Failed to parse and execute plugin configuration:", err, rawPluginAttr);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
91
132
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
133
|
+
function instantiatePlugin(ConstructorFunc, argsArray) {
|
|
134
|
+
if (typeof ConstructorFunc !== 'function') return ConstructorFunc;
|
|
135
|
+
|
|
136
|
+
// Check standard ES6 class structure
|
|
137
|
+
const isEs6Class = /^\s*class\s+/.test(ConstructorFunc.toString());
|
|
138
|
+
|
|
139
|
+
if (isEs6Class) {
|
|
140
|
+
return new ConstructorFunc(...argsArray);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Fallback Step A: Attempt standard function invocation (e.g. particlesJS)
|
|
145
|
+
return ConstructorFunc(...argsArray);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
// Fallback Step B: If invocation throws constructor error (e.g. Babel transpiled ES5 classes)
|
|
148
|
+
if (err instanceof TypeError) {
|
|
149
|
+
return new ConstructorFunc(...argsArray);
|
|
150
|
+
}
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
95
154
|
|
|
96
|
-
|
|
97
|
-
|
|
155
|
+
async function executeConfigBlock(context, configBlock) {
|
|
156
|
+
for (const [key, value] of Object.entries(configBlock)) {
|
|
157
|
+
// 1. If key is an operator, process it natively via AST engine
|
|
158
|
+
if (key.startsWith("$")) {
|
|
159
|
+
await processOperators(context, { [key]: value });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
98
162
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (window[activePluginName]) {
|
|
111
|
-
el[activePluginName] = window[activePluginName];
|
|
112
|
-
}
|
|
163
|
+
// 2. Intercept standard lifecycle methods to bind them directly as functions on the Element
|
|
164
|
+
if (["getValue", "setValue", "read", "write"].includes(key)) {
|
|
165
|
+
context.element[key] = async function(...args) {
|
|
166
|
+
const localContext = {
|
|
167
|
+
...context,
|
|
168
|
+
event: { ...context.event, value: args[0], arguments: args }
|
|
169
|
+
};
|
|
170
|
+
return await processOperators(localContext, value);
|
|
171
|
+
};
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
113
174
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// Await initialization from the operator engine
|
|
118
|
-
const initResult = await processOperatorsAsync(el, initExpr, [], null, [], new Map([["$this", el]]));
|
|
119
|
-
|
|
120
|
-
if (initResult !== undefined && initResult !== null && initResult !== "") {
|
|
121
|
-
// Overwrite the temporary library function with the returned instance
|
|
122
|
-
el[activePluginName] = initResult;
|
|
123
|
-
existingInstance = initResult;
|
|
124
|
-
}
|
|
175
|
+
const parts = key.split('.');
|
|
176
|
+
const rootPluginName = parts[0];
|
|
125
177
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
178
|
+
// 3. Resolve target from window object using dot notation
|
|
179
|
+
let ConstructorFunc = window;
|
|
180
|
+
for (const part of parts) {
|
|
181
|
+
if (ConstructorFunc) {
|
|
182
|
+
ConstructorFunc = ConstructorFunc[part];
|
|
130
183
|
}
|
|
184
|
+
}
|
|
131
185
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (isDirectThis) {
|
|
139
|
-
resolvedParts.push('$this');
|
|
140
|
-
pointer = el;
|
|
141
|
-
} else {
|
|
142
|
-
resolvedParts.push(activePluginName);
|
|
143
|
-
pointer = existingInstance || el; // Fallback to element if instance isn't populated
|
|
186
|
+
// 4. If it's a function, treat it as a constructor to initialize the plugin
|
|
187
|
+
if (typeof ConstructorFunc === 'function' && !context.element[rootPluginName]) {
|
|
188
|
+
let constructorArgs = [];
|
|
189
|
+
if (value !== undefined) {
|
|
190
|
+
const evaluatedArgs = await processOperators(context, value);
|
|
191
|
+
constructorArgs = Array.isArray(evaluatedArgs) ? evaluatedArgs : [evaluatedArgs];
|
|
144
192
|
}
|
|
145
193
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (pointer != null) {
|
|
153
|
-
if (pointer[cleanPart] !== undefined) {
|
|
154
|
-
matchedKey = part;
|
|
155
|
-
} else {
|
|
156
|
-
let lower = cleanPart.toLowerCase();
|
|
157
|
-
let realKey = null;
|
|
158
|
-
|
|
159
|
-
let currentObj = pointer;
|
|
160
|
-
while (currentObj) {
|
|
161
|
-
let props = Object.getOwnPropertyNames(currentObj);
|
|
162
|
-
let found = props.find(p => p.toLowerCase() === lower);
|
|
163
|
-
if (found) {
|
|
164
|
-
realKey = found;
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
currentObj = Object.getPrototypeOf(currentObj);
|
|
168
|
-
}
|
|
194
|
+
// Create the plugin instance and bind it directly to the element under its root name
|
|
195
|
+
const instance = instantiatePlugin(ConstructorFunc, constructorArgs);
|
|
196
|
+
context.element[rootPluginName] = instance;
|
|
197
|
+
context.plugin = instance; // Update context.plugin reference for sibling method chaining
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
169
200
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
let correctedAttrName = resolvedParts.join('.');
|
|
180
|
-
|
|
181
|
-
// --- DECORATION & EXECUTION ---
|
|
182
|
-
// Plugins must be prefixed with $ for the operator engine's property resolution
|
|
183
|
-
let prefixedAttrName = correctedAttrName;
|
|
184
|
-
if (activePluginName && !isDirectThis) {
|
|
185
|
-
prefixedAttrName = "$" + correctedAttrName;
|
|
201
|
+
// 5. Explicit Instance Method Chaining (e.g. "FullCalendar.render")
|
|
202
|
+
let methodTarget = context.element[rootPluginName];
|
|
203
|
+
if (methodTarget && parts.length > 1) {
|
|
204
|
+
let method = methodTarget;
|
|
205
|
+
let parent = methodTarget;
|
|
206
|
+
for (let i = 1; i < parts.length; i++) {
|
|
207
|
+
parent = method;
|
|
208
|
+
if (method) method = method[parts[i]];
|
|
186
209
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
expression = prefixedAttrName;
|
|
193
|
-
} else {
|
|
194
|
-
let safeValue = val;
|
|
195
|
-
if (!val.includes('$') && isNaN(val) && val !== 'true' && val !== 'false' && val !== 'null') {
|
|
196
|
-
if (!val.startsWith("'") && !val.startsWith('"')) safeValue = `'${val}'`;
|
|
210
|
+
if (typeof method === 'function') {
|
|
211
|
+
let methodArgs = [];
|
|
212
|
+
if (value !== undefined) {
|
|
213
|
+
const evaluatedArgs = await processOperators(context, value);
|
|
214
|
+
methodArgs = Array.isArray(evaluatedArgs) ? evaluatedArgs : [evaluatedArgs];
|
|
197
215
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// Execute using the existing processOperatorsAsync system.
|
|
202
|
-
const result = await processOperatorsAsync(el, expression, [], null, [], new Map([["$this", el]]));
|
|
203
|
-
|
|
204
|
-
// CAPTURE & ASSIGN: If the element still lacks the instance, save the result of the execution to the element
|
|
205
|
-
if (activePluginName && !el[activePluginName] && result !== undefined && result !== null && result !== "") {
|
|
206
|
-
el[activePluginName] = result;
|
|
216
|
+
await method.apply(parent, methodArgs);
|
|
217
|
+
continue;
|
|
207
218
|
}
|
|
208
|
-
|
|
209
|
-
} catch (e) {
|
|
210
|
-
console.warn(`[Plugin System] Sequential Execution Error (${attrName}):`, e);
|
|
211
219
|
}
|
|
212
|
-
}
|
|
213
220
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const initResult = await processOperatorsAsync(el, `$${name}()`, [], null, [], new Map([["$this", el]]));
|
|
221
|
-
if (initResult !== undefined && initResult !== null && initResult !== "") {
|
|
222
|
-
el[name] = initResult;
|
|
221
|
+
// 6. Sibling Method Chaining (e.g. "render" directly on context.plugin)
|
|
222
|
+
if (context.plugin && typeof context.plugin[key] === 'function') {
|
|
223
|
+
let methodArgs = [];
|
|
224
|
+
if (value !== undefined) {
|
|
225
|
+
const evaluatedArgs = await processOperators(context, value);
|
|
226
|
+
methodArgs = Array.isArray(evaluatedArgs) ? evaluatedArgs : [evaluatedArgs];
|
|
223
227
|
}
|
|
228
|
+
await context.plugin[key].apply(context.plugin, methodArgs);
|
|
229
|
+
continue;
|
|
224
230
|
}
|
|
231
|
+
|
|
232
|
+
// 7. Fallback: Process operators or generic actions on properties
|
|
233
|
+
await processOperators(context, { [key]: value });
|
|
225
234
|
}
|
|
226
235
|
}
|
|
227
236
|
|
|
228
|
-
// Global Startup
|
|
229
237
|
if (typeof document !== 'undefined') {
|
|
230
238
|
const selector = "[plugin]";
|
|
231
239
|
Observer.init({
|
|
@@ -243,6 +251,4 @@ if (typeof document !== 'undefined') {
|
|
|
243
251
|
})
|
|
244
252
|
.catch(() => {})
|
|
245
253
|
.finally(() => init(document.querySelectorAll(selector)));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export default { init, plugins };
|
|
254
|
+
}
|
package/src/operators.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
18
|
+
|
|
19
|
+
import { getValueFromObject, queryElements } from "@cocreate/utils";
|
|
20
|
+
|
|
21
|
+
const IsomorphicGlobals = {
|
|
22
|
+
Math,
|
|
23
|
+
Number,
|
|
24
|
+
String,
|
|
25
|
+
Boolean,
|
|
26
|
+
Date,
|
|
27
|
+
JSON,
|
|
28
|
+
Array,
|
|
29
|
+
Object,
|
|
30
|
+
parseInt,
|
|
31
|
+
parseFloat,
|
|
32
|
+
encodeURIComponent,
|
|
33
|
+
decodeURIComponent,
|
|
34
|
+
btoa: (str) => {
|
|
35
|
+
if (typeof btoa === 'function') return btoa(String(str));
|
|
36
|
+
return Buffer.from(String(str)).toString("base64");
|
|
37
|
+
},
|
|
38
|
+
atob: (b64) => {
|
|
39
|
+
if (typeof atob === 'function') return atob(b64);
|
|
40
|
+
return Buffer.from(String(b64), "base64").toString("utf-8");
|
|
41
|
+
},
|
|
42
|
+
concat: (...args) => args.flat().map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(''),
|
|
43
|
+
|
|
44
|
+
// Core Asynchronous Flow Control Operators
|
|
45
|
+
await: {
|
|
46
|
+
all: async (promises) => Promise.all(promises),
|
|
47
|
+
any: async (promises) => Promise.any(promises),
|
|
48
|
+
race: async (promises) => Promise.race(promises),
|
|
49
|
+
allSettled: async (promises) => Promise.allSettled(promises),
|
|
50
|
+
delay: (ms) => new Promise(resolve => setTimeout(resolve, Number(ms) || 0))
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const BrowserGlobals = {
|
|
55
|
+
get $organization_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("organization_id") : ""; },
|
|
56
|
+
get $user_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("user_id") : ""; },
|
|
57
|
+
get $client_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("clientId") : ""; },
|
|
58
|
+
get $session_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("session_id") : ""; },
|
|
59
|
+
get $innerWidth() { return typeof window !== 'undefined' ? window.innerWidth : 0; },
|
|
60
|
+
get $innerHeight() { return typeof window !== 'undefined' ? window.innerHeight : 0; },
|
|
61
|
+
get $href() { return typeof window !== 'undefined' ? window.location.href.replace(/\/$/, "") : ""; },
|
|
62
|
+
get $origin() { return typeof window !== 'undefined' ? window.location.origin : ""; },
|
|
63
|
+
get $protocol() { return typeof window !== 'undefined' ? window.location.protocol : ""; },
|
|
64
|
+
get $hostname() { return typeof window !== 'undefined' ? window.location.hostname : ""; },
|
|
65
|
+
get $host() { return typeof window !== 'undefined' ? window.location.host : ""; },
|
|
66
|
+
get $port() { return typeof window !== 'undefined' ? window.location.port : ""; },
|
|
67
|
+
get $pathname() { return typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, "") : ""; },
|
|
68
|
+
get $hash() { return typeof window !== 'undefined' ? window.location.hash : ""; },
|
|
69
|
+
get $subdomain() {
|
|
70
|
+
if (typeof window === 'undefined') return "";
|
|
71
|
+
const parts = window.location.hostname.split('.');
|
|
72
|
+
return parts.length > 2 ? parts[0] : "";
|
|
73
|
+
},
|
|
74
|
+
get $relativePath() {
|
|
75
|
+
if (typeof window === 'undefined') return "./";
|
|
76
|
+
const currentPath = window.location.pathname.replace(/\/[^\/]*$/, "");
|
|
77
|
+
const depth = currentPath.split("/").filter(Boolean).length;
|
|
78
|
+
return depth > 0 ? "../".repeat(depth) : "./";
|
|
79
|
+
},
|
|
80
|
+
get $path() {
|
|
81
|
+
if (typeof window === 'undefined') return "";
|
|
82
|
+
let path = window.location.pathname;
|
|
83
|
+
if (path.split("/").pop().includes(".")) path = path.replace(/\/[^\/]+$/, "/");
|
|
84
|
+
return path === "/" ? "" : path;
|
|
85
|
+
},
|
|
86
|
+
$true: () => true,
|
|
87
|
+
$false: () => false,
|
|
88
|
+
$parse: (value = "") => {
|
|
89
|
+
try { return JSON.parse(value); } catch (e) { return value; }
|
|
90
|
+
},
|
|
91
|
+
$query: function(selector) {
|
|
92
|
+
if (typeof document === 'undefined') return undefined;
|
|
93
|
+
let element = this?.element || null;
|
|
94
|
+
if (typeof selector === 'string') {
|
|
95
|
+
selector = selector.trim().replace(/^['"]|['"]$/g, '');
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const results = queryElements({ element, selector });
|
|
99
|
+
return results && results.length > 0 ? results[0] : undefined;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.warn(`[AST Engine] Invalid $query selector => "${selector}"`, error);
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
$queryAll: function(selector) {
|
|
106
|
+
if (typeof document === 'undefined') return [];
|
|
107
|
+
let element = this?.element || null;
|
|
108
|
+
if (typeof selector === 'string') {
|
|
109
|
+
selector = selector.trim().replace(/^['"]|['"]$/g, '');
|
|
110
|
+
if (selector.includes('$document')) return [];
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const results = queryElements({ element, selector });
|
|
114
|
+
return results ? Array.from(results) : [];
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn(`[AST Engine] Invalid $queryAll selector => "${selector}"`, error);
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const getSandboxScope = (context) => {
|
|
123
|
+
const isBrowser = typeof window !== 'undefined';
|
|
124
|
+
return {
|
|
125
|
+
...IsomorphicGlobals,
|
|
126
|
+
...(isBrowser ? BrowserGlobals : {}),
|
|
127
|
+
...context
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Main recursive traversal loop of the AST Compiler.
|
|
133
|
+
* Analyzes object structures from the inside-out to resolve nested expression boundaries.
|
|
134
|
+
*/
|
|
135
|
+
export async function processOperators(context, execute) {
|
|
136
|
+
if (execute instanceof Promise) {
|
|
137
|
+
execute = await execute;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (execute === null || execute === undefined) {
|
|
141
|
+
return execute;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (Array.isArray(execute)) {
|
|
145
|
+
const evaluatedArray = [];
|
|
146
|
+
for (let i = 0; i < execute.length; i++) {
|
|
147
|
+
evaluatedArray[i] = await processOperators(context, execute[i]);
|
|
148
|
+
}
|
|
149
|
+
return evaluatedArray;
|
|
150
|
+
} else if (typeof execute === "object") {
|
|
151
|
+
const keys = Object.keys(execute);
|
|
152
|
+
|
|
153
|
+
// Match operators starting with "$" except raw database storage parameters
|
|
154
|
+
const operatorKey = keys.find(k => k.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(k));
|
|
155
|
+
|
|
156
|
+
if (operatorKey && keys.length === 1) {
|
|
157
|
+
return await executeOperator(context, operatorKey, execute[operatorKey]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const evaluatedObject = {};
|
|
161
|
+
for (const key of keys) {
|
|
162
|
+
let finalKey = key;
|
|
163
|
+
|
|
164
|
+
if (key.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(key)) {
|
|
165
|
+
const resolvedKey = await executeOperator(context, key);
|
|
166
|
+
if (typeof resolvedKey === "string" || typeof resolvedKey === "number") {
|
|
167
|
+
finalKey = String(resolvedKey);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let finalValue = execute[key];
|
|
172
|
+
if (typeof finalValue === "string" && finalValue.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(finalValue)) {
|
|
173
|
+
finalValue = await executeOperator(context, finalValue);
|
|
174
|
+
} else if (Array.isArray(finalValue) || (typeof finalValue === "object" && finalValue !== null)) {
|
|
175
|
+
finalValue = await processOperators(context, finalValue);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
evaluatedObject[finalKey] = finalValue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return evaluatedObject;
|
|
182
|
+
} else if (typeof execute === "string" && execute.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(execute)) {
|
|
183
|
+
return await executeOperator(context, execute);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return execute;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function executeOperator(context, operator, args) {
|
|
190
|
+
const localContext = {
|
|
191
|
+
state: {},
|
|
192
|
+
...context
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Evaluate core context property mappings
|
|
196
|
+
if (args === undefined) {
|
|
197
|
+
if (operator.startsWith("$state.")) {
|
|
198
|
+
return getValueFromObject(localContext.state, operator.substring(7));
|
|
199
|
+
} else if (operator === "$state") {
|
|
200
|
+
return localContext.state;
|
|
201
|
+
} else if (operator.startsWith("$data.")) {
|
|
202
|
+
return getValueFromObject(localContext.data, operator.substring(6));
|
|
203
|
+
} else if (operator === "$data") {
|
|
204
|
+
return localContext.data;
|
|
205
|
+
} else if (operator.startsWith("$event.")) {
|
|
206
|
+
return getValueFromObject(localContext.event, operator.substring(7));
|
|
207
|
+
} else if (operator === "$event") {
|
|
208
|
+
return localContext.event;
|
|
209
|
+
} else if (operator.startsWith("$request.")) {
|
|
210
|
+
return getValueFromObject(localContext.request, operator.substring(9));
|
|
211
|
+
} else if (operator === "$request") {
|
|
212
|
+
return localContext.request;
|
|
213
|
+
} else if (operator.startsWith("$header.")) {
|
|
214
|
+
const headerKey = operator.substring(8).toLowerCase();
|
|
215
|
+
return localContext.request?.headers?.[headerKey];
|
|
216
|
+
} else if (operator === "$rawBody") {
|
|
217
|
+
return localContext.rawBody;
|
|
218
|
+
} else if (operator === "$this" || operator === "$element") {
|
|
219
|
+
return localContext.element;
|
|
220
|
+
} else if (operator === "$plugin" || operator === "$instance") {
|
|
221
|
+
return localContext.plugin;
|
|
222
|
+
} else if (operator === "$value") {
|
|
223
|
+
const el = localContext.element;
|
|
224
|
+
return el && typeof el.getValue === 'function' ? el.getValue() : "";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let isExplicitNew = false;
|
|
229
|
+
let cleanPath = operator.startsWith("$") ? operator.substring(1) : operator;
|
|
230
|
+
|
|
231
|
+
if (cleanPath.startsWith("new.")) {
|
|
232
|
+
isExplicitNew = true;
|
|
233
|
+
cleanPath = cleanPath.substring(4);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const parts = cleanPath.split('.');
|
|
237
|
+
const SandboxScope = getSandboxScope(localContext);
|
|
238
|
+
|
|
239
|
+
let target = SandboxScope[parts[0]];
|
|
240
|
+
let parentContext = SandboxScope;
|
|
241
|
+
|
|
242
|
+
if (target === undefined && localContext.element) {
|
|
243
|
+
target = localContext.element[parts[0]];
|
|
244
|
+
parentContext = localContext.element;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (target !== undefined) {
|
|
248
|
+
for (let i = 1; i < parts.length; i++) {
|
|
249
|
+
parentContext = target;
|
|
250
|
+
target = target[parts[i]];
|
|
251
|
+
if (target === undefined) break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (typeof target === 'function') {
|
|
255
|
+
let argsArray = [];
|
|
256
|
+
if (args !== undefined) {
|
|
257
|
+
if (typeof args === 'object' && args !== null && (args.$args !== undefined || args.$arguments !== undefined)) {
|
|
258
|
+
const explicitArgs = args.$args !== undefined ? args.$args : args.$arguments;
|
|
259
|
+
|
|
260
|
+
if (Array.isArray(explicitArgs)) {
|
|
261
|
+
argsArray = [];
|
|
262
|
+
for (let i = 0; i < explicitArgs.length; i++) {
|
|
263
|
+
argsArray.push(await processOperators(localContext, explicitArgs[i]));
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
const evaluatedSingle = await processOperators(localContext, explicitArgs);
|
|
267
|
+
argsArray = [evaluatedSingle];
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
const evaluatedSingle = await processOperators(localContext, args);
|
|
271
|
+
argsArray = [evaluatedSingle];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const isEs6Class = /^\s*class\s+/.test(target.toString());
|
|
276
|
+
|
|
277
|
+
if (isExplicitNew || isEs6Class) {
|
|
278
|
+
return new target(...argsArray);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return await target.apply(parentContext, argsArray);
|
|
282
|
+
} else if (target !== undefined) {
|
|
283
|
+
return target;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return operator;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export { processOperators as processOperatorsAsync };
|
|
291
|
+
export const customOperators = {};
|
|
292
|
+
|
|
293
|
+
export default processOperators;
|