@hydranium/protocol 1.0.0-next.10
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 +81 -0
- package/lib/abstract-logger.d.ts +66 -0
- package/lib/abstract-logger.d.ts.map +1 -0
- package/lib/abstract-logger.js +110 -0
- package/lib/abstract-logger.js.map +1 -0
- package/lib/browser-runtime.d.ts +124 -0
- package/lib/browser-runtime.d.ts.map +1 -0
- package/lib/browser-runtime.js +11 -0
- package/lib/browser-runtime.js.map +1 -0
- package/lib/client/data-events.d.ts +49 -0
- package/lib/client/data-events.d.ts.map +1 -0
- package/lib/client/data-events.js +60 -0
- package/lib/client/data-events.js.map +1 -0
- package/lib/client/data-port.d.ts +95 -0
- package/lib/client/data-port.d.ts.map +1 -0
- package/lib/client/data-port.js +11 -0
- package/lib/client/data-port.js.map +1 -0
- package/lib/client/data-session.d.ts +129 -0
- package/lib/client/data-session.d.ts.map +1 -0
- package/lib/client/data-session.js +179 -0
- package/lib/client/data-session.js.map +1 -0
- package/lib/client/index.d.ts +39 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/index.js +55 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client/message-relay.d.ts +111 -0
- package/lib/client/message-relay.d.ts.map +1 -0
- package/lib/client/message-relay.js +139 -0
- package/lib/client/message-relay.js.map +1 -0
- package/lib/client/post-message-transport.d.ts +77 -0
- package/lib/client/post-message-transport.d.ts.map +1 -0
- package/lib/client/post-message-transport.js +95 -0
- package/lib/client/post-message-transport.js.map +1 -0
- package/lib/clock.d.ts +111 -0
- package/lib/clock.d.ts.map +1 -0
- package/lib/clock.js +74 -0
- package/lib/clock.js.map +1 -0
- package/lib/data/data-protocol-methods.d.ts +39 -0
- package/lib/data/data-protocol-methods.d.ts.map +1 -0
- package/lib/data/data-protocol-methods.js +61 -0
- package/lib/data/data-protocol-methods.js.map +1 -0
- package/lib/data/data-server-protocol.d.ts +264 -0
- package/lib/data/data-server-protocol.d.ts.map +1 -0
- package/lib/data/data-server-protocol.js +11 -0
- package/lib/data/data-server-protocol.js.map +1 -0
- package/lib/data/diagnostics.d.ts +97 -0
- package/lib/data/diagnostics.d.ts.map +1 -0
- package/lib/data/diagnostics.js +25 -0
- package/lib/data/diagnostics.js.map +1 -0
- package/lib/data/events.d.ts +107 -0
- package/lib/data/events.d.ts.map +1 -0
- package/lib/data/events.js +11 -0
- package/lib/data/events.js.map +1 -0
- package/lib/data/index.d.ts +15 -0
- package/lib/data/index.d.ts.map +1 -0
- package/lib/data/index.js +38 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/methods.d.ts +57 -0
- package/lib/data/methods.d.ts.map +1 -0
- package/lib/data/methods.js +60 -0
- package/lib/data/methods.js.map +1 -0
- package/lib/data/requests.d.ts +65 -0
- package/lib/data/requests.d.ts.map +1 -0
- package/lib/data/requests.js +11 -0
- package/lib/data/requests.js.map +1 -0
- package/lib/debouncer.d.ts +75 -0
- package/lib/debouncer.d.ts.map +1 -0
- package/lib/debouncer.js +91 -0
- package/lib/debouncer.js.map +1 -0
- package/lib/errors.d.ts +84 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +113 -0
- package/lib/errors.js.map +1 -0
- package/lib/host-diagnostics.d.ts +58 -0
- package/lib/host-diagnostics.d.ts.map +1 -0
- package/lib/host-diagnostics.js +18 -0
- package/lib/host-diagnostics.js.map +1 -0
- package/lib/index.d.ts +34 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/latency-collector.d.ts +126 -0
- package/lib/latency-collector.d.ts.map +1 -0
- package/lib/latency-collector.js +137 -0
- package/lib/latency-collector.js.map +1 -0
- package/lib/logger.d.ts +119 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +126 -0
- package/lib/logger.js.map +1 -0
- package/lib/model-server.d.ts +352 -0
- package/lib/model-server.d.ts.map +1 -0
- package/lib/model-server.js +160 -0
- package/lib/model-server.js.map +1 -0
- package/lib/model-service/args.d.ts +64 -0
- package/lib/model-service/args.d.ts.map +1 -0
- package/lib/model-service/args.js +11 -0
- package/lib/model-service/args.js.map +1 -0
- package/lib/model-service/index.d.ts +11 -0
- package/lib/model-service/index.d.ts.map +1 -0
- package/lib/model-service/index.js +31 -0
- package/lib/model-service/index.js.map +1 -0
- package/lib/model-service/reference-candidate.d.ts +56 -0
- package/lib/model-service/reference-candidate.d.ts.map +1 -0
- package/lib/model-service/reference-candidate.js +11 -0
- package/lib/model-service/reference-candidate.js.map +1 -0
- package/lib/noop-logger.d.ts +25 -0
- package/lib/noop-logger.d.ts.map +1 -0
- package/lib/noop-logger.js +36 -0
- package/lib/noop-logger.js.map +1 -0
- package/lib/observable-value.d.ts +72 -0
- package/lib/observable-value.d.ts.map +1 -0
- package/lib/observable-value.js +52 -0
- package/lib/observable-value.js.map +1 -0
- package/lib/patch-merge.d.ts +123 -0
- package/lib/patch-merge.d.ts.map +1 -0
- package/lib/patch-merge.js +104 -0
- package/lib/patch-merge.js.map +1 -0
- package/lib/profile-session.d.ts +87 -0
- package/lib/profile-session.d.ts.map +1 -0
- package/lib/profile-session.js +98 -0
- package/lib/profile-session.js.map +1 -0
- package/lib/profiling.d.ts +83 -0
- package/lib/profiling.d.ts.map +1 -0
- package/lib/profiling.js +20 -0
- package/lib/profiling.js.map +1 -0
- package/lib/project.d.ts +121 -0
- package/lib/project.d.ts.map +1 -0
- package/lib/project.js +34 -0
- package/lib/project.js.map +1 -0
- package/lib/rpc/bind-rpc-methods.d.ts +100 -0
- package/lib/rpc/bind-rpc-methods.d.ts.map +1 -0
- package/lib/rpc/bind-rpc-methods.js +110 -0
- package/lib/rpc/bind-rpc-methods.js.map +1 -0
- package/lib/rpc/create-rpc-proxy.d.ts +188 -0
- package/lib/rpc/create-rpc-proxy.d.ts.map +1 -0
- package/lib/rpc/create-rpc-proxy.js +158 -0
- package/lib/rpc/create-rpc-proxy.js.map +1 -0
- package/lib/rpc/index.d.ts +12 -0
- package/lib/rpc/index.d.ts.map +1 -0
- package/lib/rpc/index.js +31 -0
- package/lib/rpc/index.js.map +1 -0
- package/lib/rpc/wire-prefix.d.ts +37 -0
- package/lib/rpc/wire-prefix.d.ts.map +1 -0
- package/lib/rpc/wire-prefix.js +49 -0
- package/lib/rpc/wire-prefix.js.map +1 -0
- package/lib/testing/data-doubles.d.ts +121 -0
- package/lib/testing/data-doubles.d.ts.map +1 -0
- package/lib/testing/data-doubles.js +102 -0
- package/lib/testing/data-doubles.js.map +1 -0
- package/lib/testing/fake-clock.d.ts +35 -0
- package/lib/testing/fake-clock.d.ts.map +1 -0
- package/lib/testing/fake-clock.js +86 -0
- package/lib/testing/fake-clock.js.map +1 -0
- package/lib/testing/harness.d.ts +24 -0
- package/lib/testing/harness.d.ts.map +1 -0
- package/lib/testing/harness.js +11 -0
- package/lib/testing/harness.js.map +1 -0
- package/lib/testing/index.d.ts +13 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +41 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/node/duplex-connection.d.ts +25 -0
- package/lib/testing/node/duplex-connection.d.ts.map +1 -0
- package/lib/testing/node/duplex-connection.js +34 -0
- package/lib/testing/node/duplex-connection.js.map +1 -0
- package/lib/testing/node/duplex-stream.d.ts +32 -0
- package/lib/testing/node/duplex-stream.d.ts.map +1 -0
- package/lib/testing/node/duplex-stream.js +35 -0
- package/lib/testing/node/duplex-stream.js.map +1 -0
- package/lib/testing/node/index.d.ts +11 -0
- package/lib/testing/node/index.d.ts.map +1 -0
- package/lib/testing/node/index.js +37 -0
- package/lib/testing/node/index.js.map +1 -0
- package/lib/testing/wait-for.d.ts +69 -0
- package/lib/testing/wait-for.d.ts.map +1 -0
- package/lib/testing/wait-for.js +67 -0
- package/lib/testing/wait-for.js.map +1 -0
- package/lib/tracer.d.ts +115 -0
- package/lib/tracer.d.ts.map +1 -0
- package/lib/tracer.js +205 -0
- package/lib/tracer.js.map +1 -0
- package/lib/transfer-diagnostic.d.ts +73 -0
- package/lib/transfer-diagnostic.d.ts.map +1 -0
- package/lib/transfer-diagnostic.js +45 -0
- package/lib/transfer-diagnostic.js.map +1 -0
- package/lib/transfer-document.d.ts +73 -0
- package/lib/transfer-document.d.ts.map +1 -0
- package/lib/transfer-document.js +46 -0
- package/lib/transfer-document.js.map +1 -0
- package/lib/transfer-element.d.ts +39 -0
- package/lib/transfer-element.d.ts.map +1 -0
- package/lib/transfer-element.js +11 -0
- package/lib/transfer-element.js.map +1 -0
- package/lib/uri.d.ts +62 -0
- package/lib/uri.d.ts.map +1 -0
- package/lib/uri.js +28 -0
- package/lib/uri.js.map +1 -0
- package/lib/util.d.ts +147 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +211 -0
- package/lib/util.js.map +1 -0
- package/package.json +100 -0
- package/src/abstract-logger.ts +120 -0
- package/src/browser-runtime.ts +135 -0
- package/src/client/data-events.ts +71 -0
- package/src/client/data-port.ts +99 -0
- package/src/client/data-session.ts +209 -0
- package/src/client/index.ts +40 -0
- package/src/client/message-relay.ts +216 -0
- package/src/client/post-message-transport.ts +167 -0
- package/src/clock.ts +173 -0
- package/src/data/data-protocol-methods.ts +79 -0
- package/src/data/data-server-protocol.ts +296 -0
- package/src/data/diagnostics.ts +116 -0
- package/src/data/events.ts +127 -0
- package/src/data/index.ts +23 -0
- package/src/data/methods.ts +59 -0
- package/src/data/requests.ts +70 -0
- package/src/debouncer.ts +118 -0
- package/src/errors.ts +127 -0
- package/src/host-diagnostics.ts +63 -0
- package/src/index.ts +41 -0
- package/src/latency-collector.ts +231 -0
- package/src/logger.ts +172 -0
- package/src/model-server.ts +479 -0
- package/src/model-service/args.ts +67 -0
- package/src/model-service/index.ts +16 -0
- package/src/model-service/reference-candidate.ts +58 -0
- package/src/noop-logger.ts +36 -0
- package/src/observable-value.ts +92 -0
- package/src/patch-merge.ts +165 -0
- package/src/profile-session.ts +164 -0
- package/src/profiling.ts +113 -0
- package/src/project.ts +125 -0
- package/src/rpc/README.md +163 -0
- package/src/rpc/bind-rpc-methods.ts +180 -0
- package/src/rpc/create-rpc-proxy.ts +292 -0
- package/src/rpc/index.ts +16 -0
- package/src/rpc/wire-prefix.ts +48 -0
- package/src/testing/data-doubles.ts +178 -0
- package/src/testing/fake-clock.ts +106 -0
- package/src/testing/harness.ts +24 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/node/duplex-connection.ts +50 -0
- package/src/testing/node/duplex-stream.ts +51 -0
- package/src/testing/node/index.ts +22 -0
- package/src/testing/wait-for.ts +94 -0
- package/src/tracer.ts +277 -0
- package/src/transfer-diagnostic.ts +92 -0
- package/src/transfer-document.ts +99 -0
- package/src/transfer-element.ts +43 -0
- package/src/uri.ts +61 -0
- package/src/util.ts +250 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
22
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
// Subpath barrel for the in-process `ModelService` facade contract. The
|
|
26
|
+
// implementation lives in `@hydranium/core`; this package owns the
|
|
27
|
+
// argument types so both the framework facade and the data-server wire
|
|
28
|
+
// protocol can structurally agree on the lifecycle shape.
|
|
29
|
+
__exportStar(require("./args"), exports);
|
|
30
|
+
__exportStar(require("./reference-candidate"), exports);
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model-service/index.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;;;;;;;;;;;;;;AAElF,wEAAwE;AACxE,mEAAmE;AACnE,uEAAuE;AACvE,0DAA0D;AAE1D,yCAAuB;AACvB,wDAAsC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import type { TransferElement } from '../transfer-element';
|
|
10
|
+
/**
|
|
11
|
+
* UI-facing descriptor of an AST element that may be assigned as the value
|
|
12
|
+
* of a reference. Carries the URI of the owning document, the AST type, the
|
|
13
|
+
* display label, and the persisted reference value.
|
|
14
|
+
*
|
|
15
|
+
* Produced by the reference-candidate pipeline in `@hydranium/core`
|
|
16
|
+
* (`ReferenceCandidateProvider.find`). Consumed by UI surfaces —
|
|
17
|
+
* command-palettes, drop-target action providers, completion popups — that
|
|
18
|
+
* present ranked, deduped reference candidates to the user.
|
|
19
|
+
*
|
|
20
|
+
* **Why two label fields**: `label` is what the user sees in the dropdown;
|
|
21
|
+
* `value` is what gets persisted as the reference's `$refText` when the
|
|
22
|
+
* user selects this element. Separating them lets the server present a
|
|
23
|
+
* human-readable display name (e.g. `node.name`) while storing the
|
|
24
|
+
* canonical reference identifier (e.g. `node.id`). Both fields are
|
|
25
|
+
* required — producers without a display/value distinction set the same
|
|
26
|
+
* string in both, so consumers never need to remember a fallback rule.
|
|
27
|
+
*/
|
|
28
|
+
export interface ReferenceCandidate {
|
|
29
|
+
/** URI of the document declaring the referenced element. */
|
|
30
|
+
uri: string;
|
|
31
|
+
/** AST `$type` of the referenced element — useful for icon dispatch. */
|
|
32
|
+
type: string;
|
|
33
|
+
/** Display label shown to the user in dropdowns and chips. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** String stored as `$refText` when the user selects this element. */
|
|
36
|
+
value: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Result of resolving a `ReferenceRequest` to its target — a
|
|
40
|
+
* {@link ReferenceCandidate} plus the resolved node's transfer subtree.
|
|
41
|
+
*
|
|
42
|
+
* A candidate and a target are the same kind of thing (an addressable
|
|
43
|
+
* reference target): `find` lists candidates, `resolveReference` returns the
|
|
44
|
+
* one a value matched. The target additionally carries `element` because it
|
|
45
|
+
* is a single result that can afford the payload, where candidates come as a
|
|
46
|
+
* list and stay lightweight. The encoded `element` is just the resolved
|
|
47
|
+
* node's subtree, NOT the whole document root — callers that want the full
|
|
48
|
+
* document fetch it via `getModelDocument(uri)`.
|
|
49
|
+
*
|
|
50
|
+
* Generic over the transfer root type so adopters get their typed overlay.
|
|
51
|
+
*/
|
|
52
|
+
export interface ReferenceTarget<TTransfer extends TransferElement = TransferElement> extends ReferenceCandidate {
|
|
53
|
+
/** The resolved target node, encoded as a transfer subtree. */
|
|
54
|
+
element: TTransfer;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=reference-candidate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-candidate.d.ts","sourceRoot":"","sources":["../../src/model-service/reference-candidate.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,kBAAkB;IAChC,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,CAAE,SAAQ,kBAAkB;IAC7G,+DAA+D;IAC/D,OAAO,EAAE,SAAS,CAAC;CACrB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
//# sourceMappingURL=reference-candidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-candidate.js","sourceRoot":"","sources":["../../src/model-service/reference-candidate.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import { AbstractLogger } from './abstract-logger';
|
|
10
|
+
import { type LogLevel, type Logger } from './logger';
|
|
11
|
+
/**
|
|
12
|
+
* {@link Logger} implementation that discards every message. Intended for
|
|
13
|
+
* programmatic / in-process consumers (test harnesses, non-LSP hosts) that
|
|
14
|
+
* use the framework services but don't have a vscode-jsonrpc connection to
|
|
15
|
+
* forward log output through.
|
|
16
|
+
*
|
|
17
|
+
* Chaining methods (`for`, `sub`, `with`) return a noop instance of the same
|
|
18
|
+
* class; `withUri` short-circuits to the same instance.
|
|
19
|
+
*/
|
|
20
|
+
export declare class NoopLogger extends AbstractLogger implements Logger {
|
|
21
|
+
protected emit(_level: LogLevel, _label: string, _message: string, _args: readonly unknown[]): void;
|
|
22
|
+
withUri(_uri: string): this;
|
|
23
|
+
protected derive(component: string): this;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=noop-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-logger.d.ts","sourceRoot":"","sources":["../src/noop-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;;;;GAQG;AACH,qBAAa,UAAW,SAAQ,cAAe,YAAW,MAAM;IAC7D,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAI1F,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpC,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAI3C"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.NoopLogger = void 0;
|
|
12
|
+
const abstract_logger_1 = require("./abstract-logger");
|
|
13
|
+
/**
|
|
14
|
+
* {@link Logger} implementation that discards every message. Intended for
|
|
15
|
+
* programmatic / in-process consumers (test harnesses, non-LSP hosts) that
|
|
16
|
+
* use the framework services but don't have a vscode-jsonrpc connection to
|
|
17
|
+
* forward log output through.
|
|
18
|
+
*
|
|
19
|
+
* Chaining methods (`for`, `sub`, `with`) return a noop instance of the same
|
|
20
|
+
* class; `withUri` short-circuits to the same instance.
|
|
21
|
+
*/
|
|
22
|
+
class NoopLogger extends abstract_logger_1.AbstractLogger {
|
|
23
|
+
emit(_level, _label, _message, _args) {
|
|
24
|
+
// noop — every message is discarded.
|
|
25
|
+
}
|
|
26
|
+
withUri(_uri) {
|
|
27
|
+
// Composing a no-op label is wasted work — return the same instance.
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
derive(component) {
|
|
31
|
+
const Subclass = this.constructor;
|
|
32
|
+
return new Subclass(component);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.NoopLogger = NoopLogger;
|
|
36
|
+
//# sourceMappingURL=noop-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-logger.js","sourceRoot":"","sources":["../src/noop-logger.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAElF,uDAAmD;AAGnD;;;;;;;;GAQG;AACH,MAAa,UAAW,SAAQ,gCAAc;IACjC,IAAI,CAAC,MAAgB,EAAE,MAAc,EAAE,QAAgB,EAAE,KAAyB;QACzF,qCAAqC;IACxC,CAAC;IAEQ,OAAO,CAAC,IAAY;QAC1B,qEAAqE;QACrE,OAAO,IAAI,CAAC;IACf,CAAC;IAES,MAAM,CAAC,SAAiB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAA+C,CAAC;QACtE,OAAO,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;CACH;AAdD,gCAcC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import { type Event } from 'vscode-jsonrpc';
|
|
10
|
+
/**
|
|
11
|
+
* A value that may change over the connection lifetime. `value` is the
|
|
12
|
+
* current snapshot; `onChange` fires when the snapshot is replaced.
|
|
13
|
+
*
|
|
14
|
+
* The dynamic source in practice is always a user-configurable setting (see
|
|
15
|
+
* the `Settings` producers, which bind one to a Langium configuration
|
|
16
|
+
* section), but the type is source-agnostic — a constant wrapped via
|
|
17
|
+
* {@link ObservableValue.of} is still an `ObservableValue`; its `onChange`
|
|
18
|
+
* simply never fires. Recognised structurally by {@link isObservableValue}.
|
|
19
|
+
*/
|
|
20
|
+
export interface ObservableValue<T> {
|
|
21
|
+
/**
|
|
22
|
+
* `readonly` means the holder cannot assign it, NOT that it is stable —
|
|
23
|
+
* a live cell backs this with a getter, so it must be read at the point of
|
|
24
|
+
* use. Copying it into a field at construction pins the snapshot and
|
|
25
|
+
* silently defeats the whole type. A setting-bound cell answers its declared
|
|
26
|
+
* default until the client's configuration fetch resolves, so an early read
|
|
27
|
+
* is a legitimate value rather than an error.
|
|
28
|
+
*/
|
|
29
|
+
readonly value: T;
|
|
30
|
+
/**
|
|
31
|
+
* Fires when {@link value} is replaced, deduplicated by `Object.is`, so an
|
|
32
|
+
* unchanged push is not re-announced. Subscribing is OPTIONAL and usually
|
|
33
|
+
* unnecessary — reading `.value` per use already sees every change; the
|
|
34
|
+
* event is for consumers that must re-arm something a later read cannot fix
|
|
35
|
+
* (a timer already scheduled, a listener already registered).
|
|
36
|
+
*
|
|
37
|
+
* It never fires at all for a constant cell, so logic that lives only in an
|
|
38
|
+
* `onChange` handler does nothing when an adopter passes a plain value.
|
|
39
|
+
* Dispose what subscribing returns.
|
|
40
|
+
*/
|
|
41
|
+
readonly onChange: Event<T>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What a service option accepts: either a plain constant (one-shot, never
|
|
45
|
+
* changes) or an {@link ObservableValue}. Consumers normalise to a cell once
|
|
46
|
+
* via {@link ObservableValue.from} and read `.value` at the point of use.
|
|
47
|
+
*/
|
|
48
|
+
export type MaybeObservableValue<T> = T | ObservableValue<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Type-guard discriminating a {@link MaybeObservableValue} into its {@link ObservableValue}
|
|
51
|
+
* arm. Returns `false` for primitives, `null`, and any non-object payload; for
|
|
52
|
+
* objects, requires both a `value` property and a callable `onChange` accessor
|
|
53
|
+
* (vscode-jsonrpc events are exposed as callable subscribers).
|
|
54
|
+
*/
|
|
55
|
+
export declare function isObservableValue<T>(input: MaybeObservableValue<T>): input is ObservableValue<T>;
|
|
56
|
+
export declare namespace ObservableValue {
|
|
57
|
+
/**
|
|
58
|
+
* Wrap a constant as an {@link ObservableValue} whose `onChange` never
|
|
59
|
+
* fires. Adopters rarely need this — the constant arm of {@link MaybeObservableValue}
|
|
60
|
+
* already accepts a bare value; it exists for {@link from} and for tests
|
|
61
|
+
* that want an explicit cell.
|
|
62
|
+
*/
|
|
63
|
+
function of<T>(value: T): ObservableValue<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Normalise a {@link MaybeObservableValue} to an {@link ObservableValue}. Identity for a
|
|
66
|
+
* cell (preserving its live `value` getter + `onChange`); wraps a constant
|
|
67
|
+
* via {@link of}. The combinator a consumer calls once in its constructor
|
|
68
|
+
* so the rest of the class reads `.value` uniformly.
|
|
69
|
+
*/
|
|
70
|
+
function from<T>(input: MaybeObservableValue<T>): ObservableValue<T>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=observable-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observable-value.d.ts","sourceRoot":"","sources":["../src/observable-value.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAmB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;AAO7D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,eAAe,CAAC,CAAC,CAAC,CAMhG;AAED,yBAAiB,eAAe,CAAC;IAC9B;;;;;OAKG;IACH,SAAgB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAElD;IAED;;;;;OAKG;IACH,SAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAE1E;CACH"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ObservableValue = void 0;
|
|
12
|
+
exports.isObservableValue = isObservableValue;
|
|
13
|
+
const NOOP_DISPOSABLE = Object.freeze({ dispose: () => undefined });
|
|
14
|
+
/** An {@link Event} that never fires — the `onChange` of a constant cell. */
|
|
15
|
+
const NEVER_EVENT = () => NOOP_DISPOSABLE;
|
|
16
|
+
/**
|
|
17
|
+
* Type-guard discriminating a {@link MaybeObservableValue} into its {@link ObservableValue}
|
|
18
|
+
* arm. Returns `false` for primitives, `null`, and any non-object payload; for
|
|
19
|
+
* objects, requires both a `value` property and a callable `onChange` accessor
|
|
20
|
+
* (vscode-jsonrpc events are exposed as callable subscribers).
|
|
21
|
+
*/
|
|
22
|
+
function isObservableValue(input) {
|
|
23
|
+
if (input === null || typeof input !== 'object') {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const candidate = input;
|
|
27
|
+
return 'value' in candidate && typeof candidate.onChange === 'function';
|
|
28
|
+
}
|
|
29
|
+
var ObservableValue;
|
|
30
|
+
(function (ObservableValue) {
|
|
31
|
+
/**
|
|
32
|
+
* Wrap a constant as an {@link ObservableValue} whose `onChange` never
|
|
33
|
+
* fires. Adopters rarely need this — the constant arm of {@link MaybeObservableValue}
|
|
34
|
+
* already accepts a bare value; it exists for {@link from} and for tests
|
|
35
|
+
* that want an explicit cell.
|
|
36
|
+
*/
|
|
37
|
+
function of(value) {
|
|
38
|
+
return { value, onChange: NEVER_EVENT };
|
|
39
|
+
}
|
|
40
|
+
ObservableValue.of = of;
|
|
41
|
+
/**
|
|
42
|
+
* Normalise a {@link MaybeObservableValue} to an {@link ObservableValue}. Identity for a
|
|
43
|
+
* cell (preserving its live `value` getter + `onChange`); wraps a constant
|
|
44
|
+
* via {@link of}. The combinator a consumer calls once in its constructor
|
|
45
|
+
* so the rest of the class reads `.value` uniformly.
|
|
46
|
+
*/
|
|
47
|
+
function from(input) {
|
|
48
|
+
return isObservableValue(input) ? input : of(input);
|
|
49
|
+
}
|
|
50
|
+
ObservableValue.from = from;
|
|
51
|
+
})(ObservableValue || (exports.ObservableValue = ObservableValue = {}));
|
|
52
|
+
//# sourceMappingURL=observable-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observable-value.js","sourceRoot":"","sources":["../src/observable-value.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAwDlF,8CAMC;AAjBD,MAAM,eAAe,GAAe,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;AAEhF,6EAA6E;AAC7E,MAAM,WAAW,GAAiB,GAAG,EAAE,CAAC,eAAe,CAAC;AAExD;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAI,KAA8B;IAChE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC;IAChB,CAAC;IACD,MAAM,SAAS,GAAG,KAAoC,CAAC;IACvD,OAAO,OAAO,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,QAAQ,KAAK,UAAU,CAAC;AAC3E,CAAC;AAED,IAAiB,eAAe,CAoB/B;AApBD,WAAiB,eAAe;IAC7B;;;;;OAKG;IACH,SAAgB,EAAE,CAAI,KAAQ;QAC3B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAuB,EAAE,CAAC;IACvD,CAAC;IAFe,kBAAE,KAEjB,CAAA;IAED;;;;;OAKG;IACH,SAAgB,IAAI,CAAI,KAA8B;QACnD,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IAFe,oBAAI,OAEnB,CAAA;AACJ,CAAC,EApBgB,eAAe,+BAAf,eAAe,QAoB/B"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import { type Operation as JsonPatchOperation } from 'fast-json-patch';
|
|
10
|
+
/**
|
|
11
|
+
* Augment a user-intent JSON patch (computed `baseline → attempted`) with
|
|
12
|
+
* `test` ops so a strict `applyPatch` against freshly-fetched server state
|
|
13
|
+
* fails loudly when a foreign writer changed a path the user also changed.
|
|
14
|
+
*
|
|
15
|
+
* `fast-json-patch`'s `validateOperation: true` validates op structure and
|
|
16
|
+
* path resolvability but NOT the pre-existing value — a plain `replace` /
|
|
17
|
+
* `remove` silently overwrites whatever the foreign writer put there. For each
|
|
18
|
+
* `replace` / `remove` op we prepend a `test` op carrying the baseline value at
|
|
19
|
+
* that path, so a same-path divergence becomes a `TEST_OPERATION_FAILED` throw.
|
|
20
|
+
* The caller treats that as a real field-level conflict (drop + refetch) rather
|
|
21
|
+
* than silently clobbering the foreign edit.
|
|
22
|
+
*
|
|
23
|
+
* `add` ops are left unguarded: the path is new, so there is no baseline value
|
|
24
|
+
* to test against, and add-vs-add overlaps are out of scope for this floor.
|
|
25
|
+
*
|
|
26
|
+
* Used behind the framework's `ConflictError` contract by every reconcile
|
|
27
|
+
* path — the GLSP recording command's undo/redo and forward-write, via
|
|
28
|
+
* {@link ReconcilingConflictResolver}, and an adopter's form-widget save — so
|
|
29
|
+
* they share one collision-detection rule.
|
|
30
|
+
*/
|
|
31
|
+
export declare function augmentWithTestOps(baseline: object, userPatch: ReadonlyArray<JsonPatchOperation>): JsonPatchOperation[];
|
|
32
|
+
/**
|
|
33
|
+
* Outcome of {@link reconcileByPatchReplay}. The caller persists / re-baselines
|
|
34
|
+
* on `merged`, drops + surfaces the `fresh` root on `conflict`, and decides its
|
|
35
|
+
* own fallback (e.g. force-retry) on `no-op` / `unavailable`.
|
|
36
|
+
*/
|
|
37
|
+
export type ReconcileOutcome<T> = {
|
|
38
|
+
/** The replay succeeded: the foreign writer touched no path the user did. */
|
|
39
|
+
status: 'merged';
|
|
40
|
+
/**
|
|
41
|
+
* A fresh root built on the refetched server state, carrying both
|
|
42
|
+
* intents. It is NOT the caller's `attempted` root — re-baseline on
|
|
43
|
+
* this value, or the next write diffs against state the server never
|
|
44
|
+
* had.
|
|
45
|
+
*/
|
|
46
|
+
merged: T;
|
|
47
|
+
} | {
|
|
48
|
+
/**
|
|
49
|
+
* The user's root already equalled the baseline, so the version gate
|
|
50
|
+
* fired on drift that changed nothing. No refetch was performed and
|
|
51
|
+
* there is nothing to persist — retrying the same write reproduces it.
|
|
52
|
+
*/
|
|
53
|
+
status: 'no-op';
|
|
54
|
+
} | {
|
|
55
|
+
/** A guarded `test` op tripped: user and foreign writer touched one path. */
|
|
56
|
+
status: 'conflict';
|
|
57
|
+
/**
|
|
58
|
+
* The server's current root, refetched and unmodified — the user's
|
|
59
|
+
* intent was NOT applied to it. Surface it and drop the write; treating
|
|
60
|
+
* it as a merge result silently discards what the user typed.
|
|
61
|
+
*/
|
|
62
|
+
fresh: T;
|
|
63
|
+
} | {
|
|
64
|
+
/**
|
|
65
|
+
* The refetch produced nothing, so reconciliation could not be
|
|
66
|
+
* attempted at all. This says nothing about whether a conflict exists;
|
|
67
|
+
* the caller picks its own fallback (force-write, retry, surface).
|
|
68
|
+
*/
|
|
69
|
+
status: 'unavailable';
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Shared three-way reconcile for a `ConflictError`: diff the user's intent
|
|
73
|
+
* (`baseline → attempted`), refetch the server's current root, and replay the
|
|
74
|
+
* intent on top under strict, {@link augmentWithTestOps}-guarded `applyPatch`.
|
|
75
|
+
*
|
|
76
|
+
* - `no-op` — the user's root equals the baseline, so the gate fired on a
|
|
77
|
+
* benign version drift; nothing to replay (refetch is skipped).
|
|
78
|
+
* - `unavailable` — the refetch produced nothing; caller falls back.
|
|
79
|
+
* - `merged` — the foreign writer touched only paths the user did not; the
|
|
80
|
+
* merged root carries both intents.
|
|
81
|
+
* - `conflict` — a same-path divergence tripped a `test` op; caller drops the
|
|
82
|
+
* write and surfaces `fresh`.
|
|
83
|
+
*
|
|
84
|
+
* I/O is the caller's: `refetch` supplies the current root, and applying the
|
|
85
|
+
* `merged` result (update vs save, re-baseline, UI refresh) stays at the call
|
|
86
|
+
* site so form and GLSP paths keep their own persistence semantics.
|
|
87
|
+
*/
|
|
88
|
+
export declare function reconcileByPatchReplay<T extends object>(baseline: T, attempted: T, refetch: () => Promise<T | undefined>): Promise<ReconcileOutcome<T>>;
|
|
89
|
+
/**
|
|
90
|
+
* Strategy for resolving a write that raced a foreign edit. One declarative
|
|
91
|
+
* choice covers every conflict site — `execute` forward-write `(before,
|
|
92
|
+
* after)`, `undo` `(after, before)`, `redo` `(before, after)`, and form / GLSP
|
|
93
|
+
* save `(lastSynced, newRoot)` — so adopters pick reconcile-vs-force once
|
|
94
|
+
* (by binding a resolver) rather than per call site.
|
|
95
|
+
*/
|
|
96
|
+
export interface ConflictResolver {
|
|
97
|
+
/**
|
|
98
|
+
* Reconcile the user's `baseline → attempted` intent against the current
|
|
99
|
+
* server state (`refetch`), returning a {@link ReconcileOutcome} the caller
|
|
100
|
+
* acts on (persist `merged`, drop on `conflict`, fall back on `no-op` /
|
|
101
|
+
* `unavailable`).
|
|
102
|
+
*/
|
|
103
|
+
resolve<T extends object>(baseline: T, attempted: T, refetch: () => Promise<T | undefined>): Promise<ReconcileOutcome<T>>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Default {@link ConflictResolver}: field-level three-way merge via
|
|
107
|
+
* {@link reconcileByPatchReplay}. A foreign edit to a different field is
|
|
108
|
+
* merged; a same-field collision is reported as a conflict rather than
|
|
109
|
+
* clobbered.
|
|
110
|
+
*/
|
|
111
|
+
export declare class ReconcilingConflictResolver implements ConflictResolver {
|
|
112
|
+
resolve<T extends object>(baseline: T, attempted: T, refetch: () => Promise<T | undefined>): Promise<ReconcileOutcome<T>>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Last-writer-wins {@link ConflictResolver}: always reports the `attempted`
|
|
116
|
+
* state as merged, without refetching or guarding. Suitable for single-client
|
|
117
|
+
* tools or always-regenerated artifacts where a concurrent foreign edit may be
|
|
118
|
+
* overwritten. A foreign edit to any field is clobbered.
|
|
119
|
+
*/
|
|
120
|
+
export declare class ForceConflictResolver implements ConflictResolver {
|
|
121
|
+
resolve<T extends object>(_baseline: T, attempted: T, _refetch: () => Promise<T | undefined>): Promise<ReconcileOutcome<T>>;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=patch-merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-merge.d.ts","sourceRoot":"","sources":["../src/patch-merge.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAqD,KAAK,SAAS,IAAI,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1H;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,EAAE,CASvH;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IACzB;IACG,6EAA6E;IAC7E,MAAM,EAAE,QAAQ,CAAC;IACjB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,CAAC;CACZ,GACD;IACG;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;CAClB,GACD;IACG,6EAA6E;IAC7E,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,KAAK,EAAE,CAAC,CAAC;CACX,GACD;IACG;;;;OAIG;IACH,MAAM,EAAE,aAAa,CAAC;CACxB,CAAC;AAEP;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EAC1D,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GACrC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAe9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC9B;;;;;OAKG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5H;AAED;;;;;GAKG;AACH,qBAAa,2BAA4B,YAAW,gBAAgB;IACjE,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;CAG3H;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,gBAAgB;IACrD,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;CAGnI"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/********************************************************************************
|
|
3
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ForceConflictResolver = exports.ReconcilingConflictResolver = void 0;
|
|
12
|
+
exports.augmentWithTestOps = augmentWithTestOps;
|
|
13
|
+
exports.reconcileByPatchReplay = reconcileByPatchReplay;
|
|
14
|
+
const fast_json_patch_1 = require("fast-json-patch");
|
|
15
|
+
/**
|
|
16
|
+
* Augment a user-intent JSON patch (computed `baseline → attempted`) with
|
|
17
|
+
* `test` ops so a strict `applyPatch` against freshly-fetched server state
|
|
18
|
+
* fails loudly when a foreign writer changed a path the user also changed.
|
|
19
|
+
*
|
|
20
|
+
* `fast-json-patch`'s `validateOperation: true` validates op structure and
|
|
21
|
+
* path resolvability but NOT the pre-existing value — a plain `replace` /
|
|
22
|
+
* `remove` silently overwrites whatever the foreign writer put there. For each
|
|
23
|
+
* `replace` / `remove` op we prepend a `test` op carrying the baseline value at
|
|
24
|
+
* that path, so a same-path divergence becomes a `TEST_OPERATION_FAILED` throw.
|
|
25
|
+
* The caller treats that as a real field-level conflict (drop + refetch) rather
|
|
26
|
+
* than silently clobbering the foreign edit.
|
|
27
|
+
*
|
|
28
|
+
* `add` ops are left unguarded: the path is new, so there is no baseline value
|
|
29
|
+
* to test against, and add-vs-add overlaps are out of scope for this floor.
|
|
30
|
+
*
|
|
31
|
+
* Used behind the framework's `ConflictError` contract by every reconcile
|
|
32
|
+
* path — the GLSP recording command's undo/redo and forward-write, via
|
|
33
|
+
* {@link ReconcilingConflictResolver}, and an adopter's form-widget save — so
|
|
34
|
+
* they share one collision-detection rule.
|
|
35
|
+
*/
|
|
36
|
+
function augmentWithTestOps(baseline, userPatch) {
|
|
37
|
+
const augmented = [];
|
|
38
|
+
for (const op of userPatch) {
|
|
39
|
+
if (op.op === 'replace' || op.op === 'remove') {
|
|
40
|
+
augmented.push({ op: 'test', path: op.path, value: (0, fast_json_patch_1.getValueByPointer)(baseline, op.path) });
|
|
41
|
+
}
|
|
42
|
+
augmented.push(op);
|
|
43
|
+
}
|
|
44
|
+
return augmented;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shared three-way reconcile for a `ConflictError`: diff the user's intent
|
|
48
|
+
* (`baseline → attempted`), refetch the server's current root, and replay the
|
|
49
|
+
* intent on top under strict, {@link augmentWithTestOps}-guarded `applyPatch`.
|
|
50
|
+
*
|
|
51
|
+
* - `no-op` — the user's root equals the baseline, so the gate fired on a
|
|
52
|
+
* benign version drift; nothing to replay (refetch is skipped).
|
|
53
|
+
* - `unavailable` — the refetch produced nothing; caller falls back.
|
|
54
|
+
* - `merged` — the foreign writer touched only paths the user did not; the
|
|
55
|
+
* merged root carries both intents.
|
|
56
|
+
* - `conflict` — a same-path divergence tripped a `test` op; caller drops the
|
|
57
|
+
* write and surfaces `fresh`.
|
|
58
|
+
*
|
|
59
|
+
* I/O is the caller's: `refetch` supplies the current root, and applying the
|
|
60
|
+
* `merged` result (update vs save, re-baseline, UI refresh) stays at the call
|
|
61
|
+
* site so form and GLSP paths keep their own persistence semantics.
|
|
62
|
+
*/
|
|
63
|
+
async function reconcileByPatchReplay(baseline, attempted, refetch) {
|
|
64
|
+
const userPatch = (0, fast_json_patch_1.compare)(baseline, attempted);
|
|
65
|
+
if (userPatch.length === 0) {
|
|
66
|
+
return { status: 'no-op' };
|
|
67
|
+
}
|
|
68
|
+
const fresh = await refetch();
|
|
69
|
+
if (!fresh) {
|
|
70
|
+
return { status: 'unavailable' };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const merged = (0, fast_json_patch_1.applyPatch)((0, fast_json_patch_1.deepClone)(fresh), augmentWithTestOps(baseline, userPatch), true).newDocument;
|
|
74
|
+
return { status: 'merged', merged };
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { status: 'conflict', fresh };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Default {@link ConflictResolver}: field-level three-way merge via
|
|
82
|
+
* {@link reconcileByPatchReplay}. A foreign edit to a different field is
|
|
83
|
+
* merged; a same-field collision is reported as a conflict rather than
|
|
84
|
+
* clobbered.
|
|
85
|
+
*/
|
|
86
|
+
class ReconcilingConflictResolver {
|
|
87
|
+
resolve(baseline, attempted, refetch) {
|
|
88
|
+
return reconcileByPatchReplay(baseline, attempted, refetch);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.ReconcilingConflictResolver = ReconcilingConflictResolver;
|
|
92
|
+
/**
|
|
93
|
+
* Last-writer-wins {@link ConflictResolver}: always reports the `attempted`
|
|
94
|
+
* state as merged, without refetching or guarding. Suitable for single-client
|
|
95
|
+
* tools or always-regenerated artifacts where a concurrent foreign edit may be
|
|
96
|
+
* overwritten. A foreign edit to any field is clobbered.
|
|
97
|
+
*/
|
|
98
|
+
class ForceConflictResolver {
|
|
99
|
+
async resolve(_baseline, attempted, _refetch) {
|
|
100
|
+
return { status: 'merged', merged: attempted };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.ForceConflictResolver = ForceConflictResolver;
|
|
104
|
+
//# sourceMappingURL=patch-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-merge.js","sourceRoot":"","sources":["../src/patch-merge.ts"],"names":[],"mappings":";AAAA;;;;;;;kFAOkF;;;AAyBlF,gDASC;AA+DD,wDAmBC;AAlHD,qDAA0H;AAE1H;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,kBAAkB,CAAC,QAAgB,EAAE,SAA4C;IAC9F,MAAM,SAAS,GAAyB,EAAE,CAAC;IAC3C,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,EAAE,CAAC,EAAE,KAAK,SAAS,IAAI,EAAE,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC7C,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAA,mCAAiB,EAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,SAAS,CAAC;AACpB,CAAC;AA8CD;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,UAAU,sBAAsB,CACzC,QAAW,EACX,SAAY,EACZ,OAAqC;IAErC,MAAM,SAAS,GAAG,IAAA,yBAAO,EAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC/C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,CAAC;QACF,MAAM,MAAM,GAAG,IAAA,4BAAU,EAAC,IAAA,2BAAS,EAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,WAAW,CAAC;QACvG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACN,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC;AACJ,CAAC;AAmBD;;;;;GAKG;AACH,MAAa,2BAA2B;IACrC,OAAO,CAAmB,QAAW,EAAE,SAAY,EAAE,OAAqC;QACvF,OAAO,sBAAsB,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;CACH;AAJD,kEAIC;AAED;;;;;GAKG;AACH,MAAa,qBAAqB;IAC/B,KAAK,CAAC,OAAO,CAAmB,SAAY,EAAE,SAAY,EAAE,QAAsC;QAC/F,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;CACH;AAJD,sDAIC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import { type Clock, type Stopwatch } from './clock';
|
|
10
|
+
import { Logger, type LogThreshold } from './logger';
|
|
11
|
+
/** One aggregated row of a {@link ProfileSession}: a scope id and its totals. */
|
|
12
|
+
export interface ProfileRecord {
|
|
13
|
+
/** The scope id passed to {@link ProfileSession.scope}. */
|
|
14
|
+
id: string;
|
|
15
|
+
/** How many times a scope with this id ran. */
|
|
16
|
+
count: number;
|
|
17
|
+
/** Summed self-time across those runs (wall-clock minus nested scopes). */
|
|
18
|
+
selfMs: number;
|
|
19
|
+
/** Self-time as a percentage of the session's total wall-clock. */
|
|
20
|
+
selfPct: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* An aggregating, parent-exclusive self-time profiler scoped to one operation.
|
|
24
|
+
*
|
|
25
|
+
* Unlike `Tracer.time`, which emits one log line per
|
|
26
|
+
* call and keeps the timeline, a {@link ProfileSession} collapses many
|
|
27
|
+
* {@link scope} calls into a compact per-id aggregate (count + self-time +
|
|
28
|
+
* self-%), then dumps it as line-based output through a {@link Logger}. It is
|
|
29
|
+
* the in-house analogue of Langium's `ProfilingTask` (per-rule / per-node-type
|
|
30
|
+
* self-time) but for the framework's own passes — integrity rules, GModel
|
|
31
|
+
* construction, the save/reconcile chain — that Langium's profiler does not
|
|
32
|
+
* cover.
|
|
33
|
+
*
|
|
34
|
+
* Spawned per operation via `tracer.profile(id)`, used, reported, discarded —
|
|
35
|
+
* it is stateful and op-scoped, which is why it is not a method on the
|
|
36
|
+
* long-lived {@link Logger}/`Tracer`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ProfileSession {
|
|
39
|
+
/**
|
|
40
|
+
* Run `fn` as a measured task `id`, accumulating its self-time = wall-clock
|
|
41
|
+
* minus any nested {@link scope} calls. Re-entrant: an inner scope nests and
|
|
42
|
+
* its full duration is excluded from the enclosing id's self-time
|
|
43
|
+
* (parent-exclusive). Async `fn` is awaited so timing covers the full
|
|
44
|
+
* settle. Returns `fn`'s result unchanged; rethrows after recording the
|
|
45
|
+
* elapsed time of a throwing scope.
|
|
46
|
+
*/
|
|
47
|
+
scope<T>(id: string, fn: () => Promise<T>): Promise<T>;
|
|
48
|
+
scope<T>(id: string, fn: () => T): T;
|
|
49
|
+
/**
|
|
50
|
+
* Emit the aggregate through the bound {@link Logger}: one line per id,
|
|
51
|
+
* sorted by self-time descending, plus an "unaccounted" remainder (the
|
|
52
|
+
* session wall-clock not attributed to any top-level scope). No-op when
|
|
53
|
+
* `logLevel` is suppressed by the global threshold (incl. `'off'`).
|
|
54
|
+
*/
|
|
55
|
+
report(logLevel?: LogThreshold): void;
|
|
56
|
+
/** The same aggregate as structured records, for tests / programmatic use. */
|
|
57
|
+
records(): readonly ProfileRecord[];
|
|
58
|
+
}
|
|
59
|
+
interface ScopeFrame {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
/** Session-stopwatch reading when this scope began. */
|
|
62
|
+
readonly start: number;
|
|
63
|
+
/** Wall-time consumed by nested scopes, excluded from this scope's self-time. */
|
|
64
|
+
childMs: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Default {@link ProfileSession}. Times against an injected {@link Clock} (so
|
|
68
|
+
* it is fake-clock deterministic in tests, unlike a raw `performance.now`) and
|
|
69
|
+
* dumps through an injected {@link Logger}.
|
|
70
|
+
*/
|
|
71
|
+
export declare class DefaultProfileSession implements ProfileSession {
|
|
72
|
+
protected readonly logger: Logger;
|
|
73
|
+
protected readonly identifier: string;
|
|
74
|
+
/** Single monotonic timeline for the whole session; per-scope readings are deltas off it. */
|
|
75
|
+
protected readonly sessionStopwatch: Stopwatch;
|
|
76
|
+
protected readonly stack: ScopeFrame[];
|
|
77
|
+
protected readonly entries: Map<string, number[]>;
|
|
78
|
+
constructor(logger: Logger, clock: Clock, identifier: string);
|
|
79
|
+
scope<T>(id: string, fn: () => Promise<T>): Promise<T>;
|
|
80
|
+
scope<T>(id: string, fn: () => T): T;
|
|
81
|
+
/** Pop `frame`, charge its full duration to the parent, and record its self-time. */
|
|
82
|
+
protected closeFrame(frame: ScopeFrame): void;
|
|
83
|
+
records(): readonly ProfileRecord[];
|
|
84
|
+
report(logLevel?: LogThreshold): void;
|
|
85
|
+
}
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=profile-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-session.d.ts","sourceRoot":"","sources":["../src/profile-session.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAU,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC3B,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,cAAc;IAC5B;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAErC;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAEtC,8EAA8E;IAC9E,OAAO,IAAI,SAAS,aAAa,EAAE,CAAC;CACtC;AAED,UAAU,UAAU;IACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAOtD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAEjC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM;IARxC,6FAA6F;IAC7F,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,CAAC;IAC/C,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,CAAM;IAC5C,SAAS,CAAC,QAAQ,CAAC,OAAO,wBAA+B;gBAGnC,MAAM,EAAE,MAAM,EACjC,KAAK,EAAE,KAAK,EACO,UAAU,EAAE,MAAM;IAKxC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IACtD,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IA2BpC,qFAAqF;IACrF,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAgB7C,OAAO,IAAI,SAAS,aAAa,EAAE;IAUnC,MAAM,CAAC,QAAQ,GAAE,YAAsB,GAAG,IAAI;CAiBhD"}
|