@intersystems-community/ascot 0.0.0-stage → 0.1.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/README.md +89 -2
- package/dist/index.d.ts +22 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/generated/ascot.d.ts +43 -0
- package/generated/ascot.js +10205 -0
- package/generated/interfaces/iris-ascot-common.d.ts +210 -0
- package/generated/interfaces/iris-ascot-exported.d.ts +26 -0
- package/generated/interfaces/iris-ascot-imported.d.ts +13 -0
- package/lib/ascot.core.wasm +0 -0
- package/lib/ascot.core2.wasm +0 -0
- package/lib/ascot.core3.wasm +0 -0
- package/package.json +36 -4
package/README.md
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @intersystems-community/ascot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[ascot](https://github.com/intersystems-community/ascot) is a type checker and code
|
|
4
|
+
intelligence engine for InterSystems ObjectScript. This package is its WebAssembly build
|
|
5
|
+
for Node and other JavaScript hosts, with a small TypeScript wrapper. It is the engine
|
|
6
|
+
behind the InterSystems Language Server's ascot features; it does not include the `ascot`
|
|
7
|
+
command-line tool.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @intersystems-community/ascot
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createWorkspace, type Imported } from "@intersystems-community/ascot";
|
|
17
|
+
|
|
18
|
+
// What the engine asks you for when a name isn't defined in the workspace, typically
|
|
19
|
+
// answered from an IRIS instance over its REST API. Return `undefined` for "not found",
|
|
20
|
+
// or for everything if you have no instance: the workspace is then checked on its own.
|
|
21
|
+
class NoIris implements Imported {
|
|
22
|
+
async getCls(cls: string) { return undefined; } // a class's members and metadata
|
|
23
|
+
async getInc(name: string) { return undefined; } // the text of `name.inc`
|
|
24
|
+
async getIntOrMac(name: string) { return undefined; } // the text of routine `name`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ws = await createWorkspace(new NoIris());
|
|
28
|
+
|
|
29
|
+
// `open` is both open and edit: it stores the raw text (the kind comes from the uri's
|
|
30
|
+
// extension: .cls, .mac, .int, .inc) and returns the uris whose cached analysis is now
|
|
31
|
+
// stale, this one and anything that includes or references it.
|
|
32
|
+
await ws.open("file:///T.cls", `Class T Extends %RegisteredObject
|
|
33
|
+
{
|
|
34
|
+
ClassMethod M() As %Status
|
|
35
|
+
{
|
|
36
|
+
Set x = 1
|
|
37
|
+
Write x
|
|
38
|
+
Quit
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`);
|
|
42
|
+
|
|
43
|
+
await ws.diagnostics("file:///T.cls");
|
|
44
|
+
// [{ severity: "error", message: "Expected return value of type '%Status' but got nothing",
|
|
45
|
+
// range: { start: { line: 6, character: 5 }, ... }, relatedInformation: [...] }]
|
|
46
|
+
|
|
47
|
+
await ws.hover("file:///T.cls", { line: 5, character: 7 }); // the `x` being written
|
|
48
|
+
// "```objectscript\n%DataType = 1\n```"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Load every file of a project into one `Workspace` so cross-file references resolve, and
|
|
52
|
+
call `open` again on each edit. `close(uri)` removes a document; a closed editor tab is
|
|
53
|
+
usually not a removal, so keep the on-disk text open instead.
|
|
54
|
+
|
|
55
|
+
## API
|
|
56
|
+
|
|
57
|
+
`createWorkspace(imported)` returns a `Workspace` whose methods mirror the engine's
|
|
58
|
+
exports, all returning promises. Positions and ranges are zero-based line/character pairs,
|
|
59
|
+
as in the Language Server Protocol.
|
|
60
|
+
|
|
61
|
+
| Method | Returns |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `open(uri, text)` | uris whose analysis is now stale |
|
|
64
|
+
| `close(uri)` | uris whose analysis is now stale |
|
|
65
|
+
| `diagnostics(uri)` | `Diagnostic[]` |
|
|
66
|
+
| `inlayHint(uri, range)` | `InlayHint[]` |
|
|
67
|
+
| `definition(uri, position)` | `Location \| undefined` |
|
|
68
|
+
| `references(uri, position, includeDeclaration)` | `Location[]` |
|
|
69
|
+
| `hover(uri, position)` | markdown `string \| undefined` |
|
|
70
|
+
| `documentSymbol(uri)` | the class or routine and its members or labels |
|
|
71
|
+
| `workspaceSymbol(query)` | classes and members across the workspace by prefix |
|
|
72
|
+
| `queryCls(query)` | `[uri, ClassInfo][]` for classes by prefix |
|
|
73
|
+
| `queryMem(cls, query)` | `[uri, MemberInfo][]` for a class's members by prefix |
|
|
74
|
+
|
|
75
|
+
All result types (`Diagnostic`, `Location`, `ClassInfo`, `MemberInfo`, ...) are exported
|
|
76
|
+
from the package.
|
|
77
|
+
|
|
78
|
+
Calls are serialized: one runs at a time, and the rest queue behind it. Every `Workspace`
|
|
79
|
+
created by this package shares one wasm instance and one `Imported` class, so construct
|
|
80
|
+
them all from the same implementation.
|
|
81
|
+
|
|
82
|
+
## Bundling
|
|
83
|
+
|
|
84
|
+
The core wasm modules are read from this package's `lib/` directory at runtime, relative
|
|
85
|
+
to the wrapper's own location. If you bundle the JavaScript, copy `lib/*.core*.wasm` to a
|
|
86
|
+
`lib/` directory next to your bundle's output directory.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT or Apache-2.0, at your option.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Root } from "../generated/ascot.js" with { "resolution-mode": "import" };
|
|
2
|
+
import type { ClassInfo, RoutineSource } from "../generated/interfaces/iris-ascot-common.js" with { "resolution-mode": "import"
|
|
3
|
+
};
|
|
4
|
+
export type * from "../generated/interfaces/iris-ascot-common.js";
|
|
5
|
+
type RawWorkspace = InstanceType<Root["exported"]["Workspace"]>;
|
|
6
|
+
/** Every method of the wasm `Workspace`, as `createWorkspace` hands it out: all async, since
|
|
7
|
+
* `serialize` below queues each call behind the previous one. */
|
|
8
|
+
export type Workspace = {
|
|
9
|
+
[K in keyof RawWorkspace]: RawWorkspace[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<Awaited<R>> : RawWorkspace[K];
|
|
10
|
+
};
|
|
11
|
+
/** The shape your `imported.iris-connection` implementation must satisfy. */
|
|
12
|
+
export interface Imported {
|
|
13
|
+
getCls(cls: string): Promise<ClassInfo | undefined>;
|
|
14
|
+
getInc(name: string): Promise<RoutineSource | undefined>;
|
|
15
|
+
getIntOrMac(name: string): Promise<RoutineSource | undefined>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A `Workspace` backed by `imported`, with every method pre-serialized so overlapping
|
|
19
|
+
* calls (e.g. from two documents' diagnostics resolving concurrently) can't trip ascot's
|
|
20
|
+
* internal `RefCell` borrows or jco's single-in-flight-call constraint.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createWorkspace(imported: Imported): Promise<Workspace>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createWorkspace = createWorkspace;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
// jco needs the actual `IrisConnection` class (not just an instance) up front, to bind
|
|
40
|
+
// resource-identity checks (`instanceof`) at instantiation time -- so the wasm module is
|
|
41
|
+
// loaded lazily on the first `createWorkspace` call, using that call's own argument's
|
|
42
|
+
// class. Every later call must be constructed from that same class; in practice callers
|
|
43
|
+
// only ever have one `Imported` implementation, so this holds.
|
|
44
|
+
let wasm;
|
|
45
|
+
async function loadWasm(IrisConnection) {
|
|
46
|
+
return (wasm ??= import("../generated/ascot.js").then(({ instantiate }) => instantiate((name) => new WebAssembly.Module(fs.readFileSync(path.resolve(__dirname, "../lib", name))), {
|
|
47
|
+
"iris:ascot/common": {},
|
|
48
|
+
"iris:ascot/imported": { IrisConnection },
|
|
49
|
+
})));
|
|
50
|
+
}
|
|
51
|
+
// jco's jspi glue allows only one in-flight suspended call per component instance --
|
|
52
|
+
// overlapping calls (e.g. diagnostics() and hover() both awaiting a REST round trip)
|
|
53
|
+
// trip its exclusive-lock check. Every `Workspace` this package creates shares the same
|
|
54
|
+
// wasm instance, so this queue is module-wide, not per-Workspace.
|
|
55
|
+
let tail = Promise.resolve();
|
|
56
|
+
function serialize(instance) {
|
|
57
|
+
return new Proxy(instance, {
|
|
58
|
+
get(target, prop, receiver) {
|
|
59
|
+
const value = Reflect.get(target, prop, receiver);
|
|
60
|
+
if (typeof value !== "function")
|
|
61
|
+
return value;
|
|
62
|
+
return async (...args) => {
|
|
63
|
+
const prev = tail;
|
|
64
|
+
let release;
|
|
65
|
+
tail = new Promise((resolve) => (release = resolve));
|
|
66
|
+
await prev;
|
|
67
|
+
try {
|
|
68
|
+
return await value.apply(target, args);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
release();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A `Workspace` backed by `imported`, with every method pre-serialized so overlapping
|
|
79
|
+
* calls (e.g. from two documents' diagnostics resolving concurrently) can't trip ascot's
|
|
80
|
+
* internal `RefCell` borrows or jco's single-in-flight-call constraint.
|
|
81
|
+
*/
|
|
82
|
+
async function createWorkspace(imported) {
|
|
83
|
+
const IrisConnection = Object.getPrototypeOf(imported).constructor;
|
|
84
|
+
const root = await loadWasm(IrisConnection);
|
|
85
|
+
return serialize(new root.exported.Workspace(imported));
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,MAAY,EAAE,oCAAgB;AAC9B,MAAY,IAAI,sCAAkB;AA+BlC,uFAAuF;AACvF,yFAAyF;AACzF,sFAAsF;AACtF,wFAAwF;AACxF,+DAA+D;AAC/D,IAAI,IAA+B,CAAC;AAEpC,KAAK,UAAU,QAAQ,CAAC,cAA2D;IAClF,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,uBAAuB,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CACzE,WAAW,CACV,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAClG;QACC,mBAAmB,EAAE,EAAE;QACvB,qBAAqB,EAAE,EAAE,cAAc,EAAE;KAChC,CACV,CACD,CAAC,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,qFAAqF;AACrF,wFAAwF;AACxF,kEAAkE;AAClE,IAAI,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;AAE5C,SAAS,SAAS,CAAC,QAAsB;IACxC,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE;QAC1B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAClD,IAAI,OAAO,KAAK,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YAC9C,OAAO,KAAK,EAAE,GAAG,IAAe,EAAE,EAAE;gBACnC,MAAM,IAAI,GAAG,IAAI,CAAC;gBAClB,IAAI,OAAoB,CAAC;gBACzB,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;gBAC3D,MAAM,IAAI,CAAC;gBACX,IAAI,CAAC;oBACJ,OAAO,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBACxC,CAAC;wBAAS,CAAC;oBACV,OAAO,EAAE,CAAC;gBACX,CAAC;YACF,CAAC,CAAC;QACH,CAAC;KACD,CAAyB,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACI,KAAK,0BAA0B,QAAkB;IACvD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC;IACnE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC5C,OAAO,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// world root:component/root
|
|
2
|
+
import type * as IrisAscotCommon from './interfaces/iris-ascot-common.js'; // iris:ascot/common
|
|
3
|
+
import type * as IrisAscotImported from './interfaces/iris-ascot-imported.js'; // iris:ascot/imported
|
|
4
|
+
import type * as IrisAscotExported from './interfaces/iris-ascot-exported.js'; // iris:ascot/exported
|
|
5
|
+
export interface ImportObject {
|
|
6
|
+
'iris:ascot/common': typeof IrisAscotCommon,
|
|
7
|
+
'iris:ascot/imported': typeof IrisAscotImported,
|
|
8
|
+
}
|
|
9
|
+
export interface Root {
|
|
10
|
+
'iris:ascot/exported': typeof IrisAscotExported,
|
|
11
|
+
exported: typeof IrisAscotExported,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Instantiates this component with the provided imports and
|
|
16
|
+
* returns a map of all the exports of the component.
|
|
17
|
+
*
|
|
18
|
+
* This function is intended to be similar to the
|
|
19
|
+
* `WebAssembly.Instantiate` constructor. The second `imports`
|
|
20
|
+
* argument is the "import object" for wasm, except here it
|
|
21
|
+
* uses component-model-layer types instead of core wasm
|
|
22
|
+
* integers/numbers/etc.
|
|
23
|
+
*
|
|
24
|
+
* The first argument to this function, `getCoreModule`, is
|
|
25
|
+
* used to compile core wasm modules within the component.
|
|
26
|
+
* Components are composed of core wasm modules and this callback
|
|
27
|
+
* will be invoked per core wasm module. The caller of this
|
|
28
|
+
* function is responsible for reading the core wasm module
|
|
29
|
+
* identified by `path` and returning its compiled
|
|
30
|
+
* `WebAssembly.Module` object. This would use the
|
|
31
|
+
* `WebAssembly.Module` constructor on the web, for example.
|
|
32
|
+
*/
|
|
33
|
+
export function instantiate(
|
|
34
|
+
getCoreModule: (path: string) => WebAssembly.Module,
|
|
35
|
+
imports: ImportObject,
|
|
36
|
+
instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance
|
|
37
|
+
): Root;
|
|
38
|
+
export function instantiate(
|
|
39
|
+
getCoreModule: (path: string) => WebAssembly.Module | Promise<WebAssembly.Module>,
|
|
40
|
+
imports: ImportObject,
|
|
41
|
+
instantiateCore?: (module: WebAssembly.Module, imports: Record<string, any>) => WebAssembly.Instance | Promise<WebAssembly.Instance>
|
|
42
|
+
): Root | Promise<Root>;
|
|
43
|
+
|