motionspec 1.0.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/LICENSE +21 -0
- package/README.md +102 -0
- package/bin/catalog-lock.js +46 -0
- package/bin/license-check.js +32 -0
- package/bin/motion.js +136 -0
- package/catalog.lock.json +243 -0
- package/package.json +68 -0
- package/primitives/counterUp.json +54 -0
- package/primitives/cssTransition.json +38 -0
- package/primitives/marquee.json +44 -0
- package/primitives/parallaxLayer.json +35 -0
- package/primitives/pinnedSection.json +30 -0
- package/primitives/scaleOnScroll.json +55 -0
- package/primitives/scrollReveal.json +43 -0
- package/primitives/staggerReveal.json +43 -0
- package/schema/motionspec.schema.json +56 -0
- package/src/compiler/catalog-semver.js +133 -0
- package/src/compiler/catalog.js +94 -0
- package/src/compiler/compile.js +149 -0
- package/src/compiler/validate.js +264 -0
- package/src/demo/build-demo.js +150 -0
- package/src/discover/discover.js +92 -0
- package/src/mcp/register-tools.js +163 -0
- package/src/mcp/server.mjs +46 -0
- package/src/router/cache.js +81 -0
- package/src/router/clients.js +98 -0
- package/src/router/prompt.js +99 -0
- package/src/router/route.js +108 -0
- package/src/router/telemetry-sink.js +53 -0
- package/src/router/telemetry.js +68 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Telemetrie — jede geroutete Anfrage ist ein Messpunkt (Konzept §3.3).
|
|
4
|
+
* Haeuft sich 'escalate' bei einer Anfrageklasse, ist das das Signal, ein
|
|
5
|
+
* neues Primitiv in den Katalog aufzunehmen (Kapazitaet -1 <- +1).
|
|
6
|
+
*
|
|
7
|
+
* Phase C / C1: Speicherung laeuft ueber einen austauschbaren Sink (siehe
|
|
8
|
+
* telemetry-sink.js). Default ist der FileSink (JSONL, append-only,
|
|
9
|
+
* telemetry/events.jsonl, gitignored) — verhaltensgleich zu vorher. Auf dem
|
|
10
|
+
* Worker wird via setSink() ein AnalyticsEngineSink eingehaengt. Die
|
|
11
|
+
* Aggregation (byOutcome/escalations) bleibt hier; der Sink macht nur I/O.
|
|
12
|
+
*
|
|
13
|
+
* Audit-Befunde #7/#12 (2026-06-12): jedes Feld laengenbegrenzt, damit kein
|
|
14
|
+
* riesiges request/error-Feld die Ablage aufblaeht. #17: validate-Rauschen
|
|
15
|
+
* ist als outcome erkennbar und wird in der Eskalations-Sicht ausgeblendet.
|
|
16
|
+
*/
|
|
17
|
+
const path = require("path");
|
|
18
|
+
const { FileSink } = require("./telemetry-sink.js");
|
|
19
|
+
|
|
20
|
+
const MAX_FIELD = 500;
|
|
21
|
+
const NOISE_OUTCOMES = ["mcp-validate-ok", "mcp-validate-fail", "cache-hit", "cache-invalidated"];
|
|
22
|
+
|
|
23
|
+
/* Default-Telemetrie-Datei LAZY aufloesen: __dirname existiert nur in Node (CJS),
|
|
24
|
+
* nicht im ESM-Worker-Bundle. Der Worker setzt ohnehin via setSink() einen
|
|
25
|
+
* fs-freien Sink, BEVOR geloggt wird, daher wird die Datei dort nie berechnet. */
|
|
26
|
+
let _defaultFile = null;
|
|
27
|
+
function defaultTelemetryFile() {
|
|
28
|
+
if (_defaultFile === null) _defaultFile = path.join(__dirname, "..", "..", "telemetry", "events.jsonl");
|
|
29
|
+
return _defaultFile;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let sink = null;
|
|
33
|
+
function activeSink() {
|
|
34
|
+
if (sink === null) sink = new FileSink(defaultTelemetryFile());
|
|
35
|
+
return sink;
|
|
36
|
+
}
|
|
37
|
+
function setSink(s) { sink = s; }
|
|
38
|
+
function getSink() { return activeSink(); }
|
|
39
|
+
|
|
40
|
+
function clamp(v) {
|
|
41
|
+
if (typeof v === "string") return v.length > MAX_FIELD ? v.slice(0, MAX_FIELD) : v;
|
|
42
|
+
if (Array.isArray(v)) return v.slice(0, 20).map(clamp);
|
|
43
|
+
return v;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function log(event) {
|
|
47
|
+
const safe = {};
|
|
48
|
+
for (const k of Object.keys(event)) safe[k] = clamp(event[k]);
|
|
49
|
+
safe.ts = new Date().toISOString();
|
|
50
|
+
activeSink().append(safe);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function summary() {
|
|
54
|
+
const records = await activeSink().readAll();
|
|
55
|
+
const byOutcome = {};
|
|
56
|
+
const escalations = {};
|
|
57
|
+
for (const e of records) {
|
|
58
|
+
byOutcome[e.outcome] = (byOutcome[e.outcome] || 0) + 1;
|
|
59
|
+
/* #17: nur echte Eskalationen zaehlen fuer das Katalog-Wachstums-Signal */
|
|
60
|
+
if (typeof e.outcome === "string" && e.outcome.startsWith("escalate") && NOISE_OUTCOMES.indexOf(e.outcome) === -1)
|
|
61
|
+
escalations[e.outcome] = (escalations[e.outcome] || 0) + 1;
|
|
62
|
+
}
|
|
63
|
+
return { total: records.length, byOutcome, escalations };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { log, summary, setSink, getSink, NOISE_OUTCOMES };
|
|
67
|
+
/* TELEMETRY_FILE als lazy Getter erhalten (Tests lesen es; kein __dirname beim Laden). */
|
|
68
|
+
Object.defineProperty(module.exports, "TELEMETRY_FILE", { enumerable: true, get: defaultTelemetryFile });
|