@pleger/esa-js 0.2.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.
@@ -0,0 +1,121 @@
1
+ # AspectScript on GitHub
2
+
3
+ This guide explains how to use the repository from the command line and GitHub.
4
+
5
+ Repository:
6
+ - https://github.com/pleger/aspectscript
7
+
8
+ Playground (GitHub Pages):
9
+ - https://pleger.github.io/aspectscript/
10
+
11
+ ## 1) Setup
12
+
13
+ Clone and install dependencies:
14
+
15
+ ```bash
16
+ git clone https://github.com/pleger/aspectscript.git
17
+ cd aspectscript
18
+ npm install
19
+ ```
20
+
21
+ ## 2) Run scripts or examples from CLI
22
+
23
+ Use the script runner:
24
+
25
+ ```bash
26
+ npm run run:script -- tests/test-ex.js
27
+ ```
28
+
29
+ You can run any `.js` file with AspectScript support (AJS, PCs, Testing):
30
+
31
+ ```bash
32
+ npm run run:script -- path/to/your-script.js
33
+ ```
34
+
35
+ Notes:
36
+ - `load(...)` lines are ignored automatically (for compatibility with legacy test files).
37
+ - The runner instruments JavaScript before execution, then runs it in an AspectScript-enabled VM context.
38
+
39
+ Export join point trace as machine-readable JSON:
40
+
41
+ ```bash
42
+ npm run run:script -- tests/test-ex.js --trace-json trace.json
43
+ ```
44
+
45
+ Disable transform cache:
46
+
47
+ ```bash
48
+ npm run run:script -- tests/test-ex.js --no-cache
49
+ ```
50
+
51
+ ## 3) Use the AspectScript CLI command
52
+
53
+ You can also use the packaged CLI:
54
+
55
+ ```bash
56
+ npx aspectscript run tests/test-ex.js
57
+ npx aspectscript run tests/test-ex.js --trace-json trace.json
58
+ npx aspectscript run tests/test-ex.js --no-cache
59
+ npx aspectscript test
60
+ npx aspectscript test --cache-stats
61
+ npx aspectscript test --failed
62
+ ```
63
+
64
+ ## 4) Run tests from CLI
65
+
66
+ Run all tests:
67
+
68
+ ```bash
69
+ npm test
70
+ ```
71
+
72
+ Run tests by prefix:
73
+
74
+ ```bash
75
+ node run-tests.js testDS
76
+ node run-tests.js testRee
77
+ node run-tests.js test21
78
+ node run-tests.js --cache-stats
79
+ node run-tests.js --no-cache
80
+ ```
81
+
82
+ ## 5) Run only failed tests
83
+
84
+ After a test run, failed tests are written to `tests/lastFails.txt`.
85
+
86
+ Re-run only those failed tests:
87
+
88
+ ```bash
89
+ npm run test:failed
90
+ ```
91
+
92
+ Equivalent direct command:
93
+
94
+ ```bash
95
+ node run-tests.js --failed
96
+ ```
97
+
98
+ ## 6) Diagnostics
99
+
100
+ Runtime errors now include an AspectScript context line, for example:
101
+
102
+ ```text
103
+ [AspectScript] join point: [exec: move] | level: 0 | receiver: Point
104
+ ```
105
+
106
+ This helps identify where failures happen in advice/pointcut execution.
107
+
108
+ ## 7) Citation
109
+
110
+ This implementation is based on:
111
+
112
+ Rodolfo Toledo, Paul Leger, and Eric Tanter. *AspectScript: Expressive Aspects for the Web*. AOSD'10, March 15-19, 2010, Rennes and St. Malo, France. ACM.
113
+
114
+ ## 8) Conformance Suite
115
+
116
+ Run paper-aligned conformance scenarios:
117
+
118
+ ```bash
119
+ npm run test:conformance
120
+ node run-conformance.js
121
+ ```
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paul Leger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NPM_PUBLISH.md ADDED
@@ -0,0 +1,51 @@
1
+ # npm Publish Readiness
2
+
3
+ This project is configured for npm packaging, with:
4
+ - `main` entry (`aspectscript.js`)
5
+ - `types` entry (`index.d.ts`)
6
+ - `bin` command (`aspectscript`)
7
+
8
+ Current state:
9
+ - `package.json` is publish-ready (`"private": false`).
10
+
11
+ ## Checklist
12
+
13
+ 1. Set package metadata
14
+
15
+ ```json
16
+ {
17
+ "name": "@pleger/esa-js",
18
+ "version": "0.2.0",
19
+ "description": "ESA-JS and AspectScript runtime and tooling",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/pleger/ESA.git"
24
+ }
25
+ }
26
+ ```
27
+
28
+ 2. Dry run package contents
29
+
30
+ ```bash
31
+ npm pack --dry-run
32
+ ```
33
+
34
+ 3. Verify commands
35
+
36
+ ```bash
37
+ npm test
38
+ npm run test:conformance
39
+ npx aspectscript --help
40
+ ```
41
+
42
+ 4. Publish
43
+
44
+ ```bash
45
+ npm publish --access public
46
+ ```
47
+
48
+ ## Optional hardening
49
+
50
+ - Add `"files"` in `package.json` to control published artifacts explicitly.
51
+ - Add CI workflow for `npm test` + `npm run test:conformance` before release.
package/PATTERNS.md ADDED
@@ -0,0 +1,85 @@
1
+ # AspectScript Patterns
2
+
3
+ This document gives practical patterns you can copy and adapt.
4
+
5
+ ## 1) Logging all executions of a function
6
+
7
+ ```js
8
+ function service() {
9
+ return 42;
10
+ }
11
+
12
+ AJS.before(PCs.exec("service"), function (jp) {
13
+ console.log("[before]", String(jp), "args:", jp.args);
14
+ });
15
+
16
+ service();
17
+ ```
18
+
19
+ ## 2) Enforce a simple authorization rule
20
+
21
+ ```js
22
+ const session = { role: "guest" };
23
+
24
+ function deleteRecord() {
25
+ return "deleted";
26
+ }
27
+
28
+ AJS.around(PCs.exec("deleteRecord"), function (jp) {
29
+ if (session.role !== "admin") {
30
+ throw new Error("Not authorized");
31
+ }
32
+ return jp.proceed();
33
+ });
34
+ ```
35
+
36
+ ## 3) Prevent recursive self-triggering with `noBR`
37
+
38
+ ```js
39
+ function tick(n) {
40
+ if (n > 0) {
41
+ tick(n - 1);
42
+ }
43
+ }
44
+
45
+ AJS.before(PCs.noBR(PCs.exec("tick")), function () {
46
+ console.log("advice once per recursion chain");
47
+ });
48
+ ```
49
+
50
+ ## 4) Add explicit domain events
51
+
52
+ ```js
53
+ AJS.before(PCs.event("save"), function (jp) {
54
+ console.log("save event", jp.recordId);
55
+ });
56
+
57
+ AJS.event("save", { recordId: 42 }, function () {
58
+ // protected block
59
+ });
60
+ ```
61
+
62
+ ## 5) Export trace data for tooling
63
+
64
+ ```js
65
+ AJS.tracer.enable();
66
+
67
+ function runFlow() {
68
+ // your code
69
+ }
70
+
71
+ runFlow();
72
+ console.log(AJS.tracer.toJSON(true));
73
+ ```
74
+
75
+ Node CLI equivalent:
76
+
77
+ ```bash
78
+ npx aspectscript run your-script.js --trace-json trace.json
79
+ ```
80
+
81
+ ## 6) Pitfalls to avoid
82
+
83
+ - Do not mutate shared join point fields expecting cross-advice visibility; ordering matters and tests rely on deterministic behavior.
84
+ - Use `AJS.down(...)` intentionally; lowering levels can re-introduce loops in badly scoped pointcuts.
85
+ - Prefer specific pointcuts (`PCs.exec("name")`) over catch-all predicates in performance-sensitive code.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # ESA / AspectScript
2
+
3
+ An implementation of ESA-JS (Expressive Stateful Aspects) and AspectScript for JavaScript.
4
+
5
+ ## Quick start (npm)
6
+
7
+ Install:
8
+
9
+ ```bash
10
+ npm install @pleger/esa-js
11
+ ```
12
+
13
+ ### JavaScript example
14
+
15
+ ```js
16
+ const AJS = require("@pleger/esa-js");
17
+ const PCs = AJS.Pointcuts;
18
+
19
+ AJS.before(PCs.event("purchase"), function (jp) {
20
+ console.log("purchase observed:", jp.orderId, jp.total);
21
+ });
22
+
23
+ AJS.event("purchase", { orderId: "A-100", total: 42 }, function () {
24
+ console.log("inside purchase block");
25
+ });
26
+ ```
27
+
28
+ ### TypeScript example
29
+
30
+ ```ts
31
+ import AspectScript = require("@pleger/esa-js");
32
+
33
+ const AJS = AspectScript;
34
+ const PCs = AJS.Pointcuts;
35
+
36
+ AJS.around(PCs.event("audit"), (jp) => {
37
+ console.log("audit event:", jp.action);
38
+ return jp.proceed();
39
+ });
40
+
41
+ AJS.event("audit", { action: "DELETE_USER" }, () => {
42
+ console.log("business logic");
43
+ });
44
+ ```
45
+
46
+ ### Running instrumented scripts (`exec`, `call`, `get`, `set`, ...)
47
+
48
+ Use the CLI runner so your file is instrumented automatically:
49
+
50
+ ```bash
51
+ npx esa run your-script.js
52
+ ```
53
+
54
+ ### ESA stateful example
55
+
56
+ ```js
57
+ const AJS = require("@pleger/esa-js");
58
+ const ESA = AJS.ESA;
59
+ const PTs = ESA.Pointcuts;
60
+
61
+ function a(v) {}
62
+ function b() {}
63
+
64
+ const callA = PTs.call(a).and((jp, env) => env.bind("value", jp.args[0]));
65
+
66
+ const handler = ESA.deploy({
67
+ kind: ESA.BEFORE,
68
+ pattern: PTs.repeatUntil(callA, PTs.call(b)),
69
+ advice: (jp, env) => console.log("values:", env.value),
70
+ });
71
+ ```
72
+
73
+ ## What is included
74
+
75
+ - A runtime and source instrumenter for AspectScript and ESA-JS.
76
+ - A Node-based test runner that executes the original `tests/test*.js` suite while ignoring legacy `load(...)` lines.
77
+ - A CLI command (`aspectscript`) for running scripts and tests.
78
+ - TypeScript type definitions (`index.d.ts`).
79
+ - A static playground in `docs/` with ESA-focused examples and:
80
+ - editable examples
81
+ - execution output
82
+ - join point tracing
83
+
84
+ ## Local usage
85
+
86
+ Install dependencies:
87
+
88
+ ```bash
89
+ npm install
90
+ ```
91
+
92
+ Run the test suite:
93
+
94
+ ```bash
95
+ npm test
96
+ ```
97
+
98
+ Run with cache statistics:
99
+
100
+ ```bash
101
+ node run-tests.js --cache-stats
102
+ ```
103
+
104
+ Run only failed tests from the previous run:
105
+
106
+ ```bash
107
+ npm run test:failed
108
+ ```
109
+
110
+ Run any script/example file with AspectScript runtime + instrumentation:
111
+
112
+ ```bash
113
+ npm run run:script -- tests/test-ex.js
114
+ ```
115
+
116
+ Run and export execution trace as JSON:
117
+
118
+ ```bash
119
+ npm run run:script -- tests/test-ex.js --trace-json trace.json
120
+ ```
121
+
122
+ Disable transform cache for a run:
123
+
124
+ ```bash
125
+ npm run run:script -- tests/test-ex.js --no-cache
126
+ node run-tests.js --no-cache
127
+ ```
128
+
129
+ Run paper-aligned conformance examples:
130
+
131
+ ```bash
132
+ npm run test:conformance
133
+ ```
134
+
135
+ Use the CLI command:
136
+
137
+ ```bash
138
+ npx esa run tests/test-ex.js
139
+ npx esa test
140
+ npx esa test --failed
141
+ ```
142
+
143
+ Serve the playground locally from `docs/`:
144
+
145
+ ```bash
146
+ cd docs
147
+ python3 -m http.server 4173
148
+ ```
149
+
150
+ Then open `http://127.0.0.1:4173`.
151
+
152
+ ## GitHub guide
153
+
154
+ For a full command-line and GitHub usage guide, see [GITHUB_USAGE.md](./GITHUB_USAGE.md).
155
+ For practical examples/patterns, see [PATTERNS.md](./PATTERNS.md).
156
+ For package publishing readiness, see [NPM_PUBLISH.md](./NPM_PUBLISH.md).
157
+
158
+ ## Current test status
159
+
160
+ The current implementation passes the full legacy suite plus ESA split-case tests.
@@ -0,0 +1,14 @@
1
+ # AspectScript 10-Step Roadmap Status
2
+
3
+ Last updated: March 17, 2026
4
+
5
+ 1. Semantic parity with paper/tests: **Done** (`105/105` passing)
6
+ 2. Reentrancy and advice-order hardening: **Done**
7
+ 3. Scoping strategies explicit and testable: **Done** (runtime fixes + passing DS/DD tests)
8
+ 4. Source-focused diagnostics: **Done** (runtime errors include join point/level/receiver context)
9
+ 5. Machine-readable trace format: **Done** (`AJS.tracer.toJSON`, `AJS.tracer.saveToFile`, CLI `--trace-json`)
10
+ 6. TypeScript typings: **Done** (`index.d.ts`)
11
+ 7. npm package + CLI workflow: **In Progress** (`aspectscript` CLI ready; publish checklist added in `NPM_PUBLISH.md`)
12
+ 8. Instrumentation/performance caching: **Done** (`.aspectscript-cache`, `--no-cache`, `--cache-stats`)
13
+ 9. Conformance suite from paper examples: **Done** (`conformance/`, `run-conformance.js`, `npm run test:conformance`)
14
+ 10. Practical docs and patterns: **Done** (`PATTERNS.md` + README/GitHub guide updates)
@@ -0,0 +1,139 @@
1
+ # Step 1 Baseline and Triage (March 17, 2026)
2
+
3
+ This document captures the Step 1 deliverable of the plan:
4
+ - baseline run
5
+ - failure clustering by semantic root cause
6
+ - paper-backed expectations to guide fixes
7
+
8
+ Reference paper used for Step 1:
9
+ - `/Users/pleger/Downloads/1-s2.0-S0167642313002244-main.pdf`
10
+ - Tanter, Figueroa, Tabareau. *Execution levels for aspect-oriented programming* (Science of Computer Programming 80, 2014).
11
+
12
+ ## 1) Baseline
13
+
14
+ Command:
15
+
16
+ ```bash
17
+ node run-tests.js
18
+ ```
19
+
20
+ Result:
21
+ - Evaluated: `105`
22
+ - Failed: `11`
23
+
24
+ Current failing tests:
25
+ - `test-ctx.js`
26
+ - `test-dd-03.js`
27
+ - `test-ss.js`
28
+ - `test15.js`
29
+ - `test21-modifyingJPData.js`
30
+ - `testDS06.js`
31
+ - `testRee01.js`
32
+ - `testRee02.js`
33
+ - `testRee03.js`
34
+ - `testRee04.js`
35
+ - `testRee16-jpOrder.js`
36
+
37
+ `tests/lastFails.txt` now reflects this baseline.
38
+
39
+ ## 2) Paper-Guided Semantic Baseline
40
+
41
+ From the paper (used as guidance for expected behavior in this codebase):
42
+ - Execution levels are stratified; join points are emitted one level above current computation.
43
+ - Aspects affect join points one level below their deployment level.
44
+ - The default should defensively reduce regression risk.
45
+ - In AspectScript specifically, execution levels + reentrancy control should allow safe `down` usage without reintroducing loops.
46
+ - Control-flow reasoning should be level-sensitive (avoid conflating base computation and aspect/meta computation).
47
+
48
+ These principles map directly to current failing clusters around:
49
+ - reentrancy (`noBR`, `down`)
50
+ - cflow / parent-chain visibility
51
+ - scoping strategy and deployment propagation
52
+
53
+ ## 3) Failure Clusters
54
+
55
+ ### Cluster A: Deployment Scope and Join-Point Visibility
56
+
57
+ Tests:
58
+ - `test-ctx.js`
59
+ - `test-ss.js`
60
+ - `test15.js`
61
+ - `testDS06.js`
62
+ - `test-dd-03.js`
63
+
64
+ Observed symptoms:
65
+ - Missing expected `pr`/`call` join points in object-scoped scenarios.
66
+ - Unexpected extra matches in propagated/deployed contexts.
67
+ - Missing matches for interface-driven call pointcuts.
68
+ - Duplicate or leaked match in dynamic call/deployment path.
69
+
70
+ Primary hotspots in runtime:
71
+ - `currentVisibleAspects`, `aspectsFromLexicalContexts` in `aspectscript.js`
72
+ - frame construction in `wrap(...)` (lexical object/function visibility)
73
+ - propagation path (`computePropagation`, `pendingCalls`)
74
+ - strategy gates (`c`, `d`, `f`) in deployment evaluation
75
+
76
+ Working hypothesis:
77
+ - Aspect visibility and propagation across call/exec boundaries is inconsistent between lexical and dynamic paths, especially for `deployOn(...)` and strategy arrays.
78
+
79
+ ### Cluster B: Advice Order and JP Data Isolation
80
+
81
+ Tests:
82
+ - `test21-modifyingJPData.js`
83
+ - `testRee16-jpOrder.js`
84
+
85
+ Observed symptoms:
86
+ - Advice ordering mismatch (`before` behaves as LIFO where tests expect FIFO).
87
+ - cflow/order interaction includes join point `z` when it should be excluded.
88
+
89
+ Primary hotspots in runtime:
90
+ - chain construction loop in `weave(...)` for `before`/`around`
91
+ - cflow parent traversal and ordering assumptions
92
+
93
+ Working hypothesis:
94
+ - Advice chain composition order is inverted for `before`.
95
+ - Parent/cflow timing around `before` advice execution may conflate pre-body advice activity with function execution context.
96
+
97
+ ### Cluster C: Reentrancy and Execution Levels (`noBR` + `down`)
98
+
99
+ Tests:
100
+ - `testRee01.js`
101
+ - `testRee02.js`
102
+ - `testRee03.js`
103
+ - `testRee04.js`
104
+
105
+ Observed symptoms:
106
+ - Expected advice does not fire at all (over-suppression).
107
+ - `noBR(...)` and `down(...)` interplay blocks legitimate first matches.
108
+ - Context-aware `noBR(pc, ctx)` case suppresses all hits instead of one-per-context behavior.
109
+
110
+ Primary hotspots in runtime:
111
+ - `Pointcuts.noBR(...)`
112
+ - `down(...)` and `isSuppressed(...)`
113
+ - level bookkeeping (`state.currentLevel`, `state.downStack`)
114
+
115
+ Working hypothesis:
116
+ - Reentrancy suppression tokening and/or stack scanning is too aggressive.
117
+ - Level suppression may be applied before initial legitimate match is recorded.
118
+
119
+ ## 4) Step 2 Entry Strategy (from this triage)
120
+
121
+ Recommended fix order:
122
+ 1. Fix deterministic advice ordering in `weave(...)` (lowest-risk, unblocks JP-order tests).
123
+ 2. Fix `noBR`/`down` suppression semantics (Ree cluster).
124
+ 3. Fix deployment propagation and strategy visibility (`deployOn`, `c/d/f`, lexical vs dynamic environments).
125
+
126
+ Validation workflow:
127
+
128
+ ```bash
129
+ node run-tests.js --failed
130
+ node run-tests.js testRee
131
+ node run-tests.js test-ctx
132
+ node run-tests.js test-ss
133
+ node run-tests.js test-dd-03
134
+ node run-tests.js testDS06
135
+ ```
136
+
137
+ Exit criteria for Step 2 start:
138
+ - each cluster has one targeted failing test used as a guard during implementation
139
+ - full suite run after each cluster fix
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require("path");
4
+ const { spawnSync } = require("child_process");
5
+
6
+ function runNodeScript(scriptName, args) {
7
+ const scriptPath = path.join(__dirname, scriptName);
8
+ const result = spawnSync(process.execPath, [scriptPath].concat(args), {
9
+ stdio: "inherit",
10
+ });
11
+ if (typeof result.status === "number") {
12
+ process.exitCode = result.status;
13
+ } else {
14
+ process.exitCode = 1;
15
+ }
16
+ }
17
+
18
+ function usage() {
19
+ process.stdout.write(
20
+ "AspectScript CLI\n" +
21
+ "\n" +
22
+ "Usage:\n" +
23
+ " aspectscript run <file.js> [--trace-json trace.json] [--no-cache]\n" +
24
+ " aspectscript test [--failed|-f] [--cache-stats] [--no-cache] [testPrefix...]\n" +
25
+ "\n" +
26
+ "Examples:\n" +
27
+ " aspectscript run tests/test-ex.js\n" +
28
+ " aspectscript run tests/test-ex.js --trace-json trace.json\n" +
29
+ " aspectscript run tests/test-ex.js --no-cache\n" +
30
+ " aspectscript test\n" +
31
+ " aspectscript test --cache-stats\n" +
32
+ " aspectscript test --failed\n" +
33
+ " aspectscript test testRee\n"
34
+ );
35
+ }
36
+
37
+ function main() {
38
+ const args = process.argv.slice(2);
39
+ const command = args[0];
40
+
41
+ if (!command || command === "-h" || command === "--help") {
42
+ usage();
43
+ return;
44
+ }
45
+
46
+ if (command === "run") {
47
+ runNodeScript("run-script.js", args.slice(1));
48
+ return;
49
+ }
50
+
51
+ if (command === "test") {
52
+ runNodeScript("run-tests.js", args.slice(1));
53
+ return;
54
+ }
55
+
56
+ process.stderr.write("Unknown command: " + command + "\n\n");
57
+ usage();
58
+ process.exitCode = 1;
59
+ }
60
+
61
+ main();