@coldsmirk/inkstone-elasticsearch 0.22.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,40 @@
1
+ import { a as ElasticsearchAssist, t as ElasticsearchMarker } from "./markers-BctX905j.js";
2
+ import * as monacoEditor from "monaco-editor";
3
+ import { IDisposable, editor } from "monaco-editor";
4
+ //#region src/monaco.d.ts
5
+ /**
6
+ * The monaco module itself, as `onMount` hands it over.
7
+ */
8
+ type MonacoModule = typeof monacoEditor;
9
+ /**
10
+ * The language id the editor registers a request under.
11
+ */
12
+ declare const ELASTICSEARCH_LANGUAGE_ID = "elasticsearch";
13
+ /**
14
+ * Register the language — the id, its Monarch grammar and its bracket configuration — once per
15
+ * monaco module. Call it before the first model of the language is created (`beforeMount`).
16
+ */
17
+ declare function defineElasticsearchLanguage(monaco: MonacoModule, languageId?: string): void;
18
+ interface ElasticsearchLanguageHooks {
19
+ /**
20
+ * The assist as it stands right now — read per request rather than captured, so the host
21
+ * can swap it without touching Monaco's registry; null answers nothing.
22
+ */
23
+ assist: () => ElasticsearchAssist | null;
24
+ }
25
+ /**
26
+ * Register completion and hover for one model. The disposables are the caller's to hold:
27
+ * Monaco's registry outlives any editor.
28
+ */
29
+ declare function registerElasticsearchLanguage(monaco: MonacoModule, modelPath: string, hooks: ElasticsearchLanguageHooks, languageId?: string): IDisposable[];
30
+ /**
31
+ * The owner Monaco files this package's markers under.
32
+ */
33
+ declare const ELASTICSEARCH_MARKER_OWNER = "inkstone-elasticsearch";
34
+ /**
35
+ * Put `markers` on the model as Monaco's own, replacing this package's last set; the words
36
+ * beside each code are the host's, handed in per code.
37
+ */
38
+ declare function setElasticsearchMarkers(monaco: MonacoModule, model: editor.ITextModel, markers: readonly ElasticsearchMarker[], wording: (marker: ElasticsearchMarker) => string): void;
39
+ //#endregion
40
+ export { ELASTICSEARCH_LANGUAGE_ID, ELASTICSEARCH_MARKER_OWNER, ElasticsearchLanguageHooks, MonacoModule, defineElasticsearchLanguage, registerElasticsearchLanguage, setElasticsearchMarkers };
package/dist/monaco.js ADDED
@@ -0,0 +1,145 @@
1
+ //#region src/monaco.ts
2
+ const ELASTICSEARCH_LANGUAGE_ID = "elasticsearch";
3
+ const LANGUAGES_KEY = Symbol.for("coldsmirk.inkstone.elasticsearch.languages");
4
+ function definedLanguages(monaco) {
5
+ const holder = globalThis;
6
+ const registry = holder[LANGUAGES_KEY] ?? /* @__PURE__ */ new WeakMap();
7
+ holder[LANGUAGES_KEY] = registry;
8
+ const existing = registry.get(monaco);
9
+ if (existing) return existing;
10
+ const created = /* @__PURE__ */ new Set();
11
+ registry.set(monaco, created);
12
+ return created;
13
+ }
14
+ function grammar() {
15
+ return {
16
+ ignoreCase: true,
17
+ unicode: true,
18
+ tokenizer: { root: [
19
+ [/^(?<blank>\s*)(?<method>get|post|put|delete|head|patch)(?<gap>\s+)(?<path>\/[^\s?]*)(?<query>\S*)/u, [
20
+ "white",
21
+ "keyword",
22
+ "white",
23
+ "entity.name.function",
24
+ "variable"
25
+ ]],
26
+ [/"(?:[^"\\]|\\.)*"(?=\s*:)/u, "support.type.property-name.json"],
27
+ [/"(?:[^"\\]|\\.)*"/u, "string"],
28
+ [/-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/u, "constant.numeric"],
29
+ [/\b(?:true|false|null)\b/u, "constant.language"],
30
+ [/[{}[\]]/u, "delimiter.bracket"],
31
+ [/[,:]/u, "delimiter"],
32
+ [/\s+/u, "white"],
33
+ [/[^\s"{}[\],:]+/u, "variable"]
34
+ ] }
35
+ };
36
+ }
37
+ function defineElasticsearchLanguage(monaco, languageId = ELASTICSEARCH_LANGUAGE_ID) {
38
+ const defined = definedLanguages(monaco);
39
+ if (defined.has(languageId)) return;
40
+ defined.add(languageId);
41
+ monaco.languages.register({ id: languageId });
42
+ monaco.languages.setLanguageConfiguration(languageId, {
43
+ brackets: [["{", "}"], ["[", "]"]],
44
+ autoClosingPairs: [
45
+ {
46
+ open: "{",
47
+ close: "}"
48
+ },
49
+ {
50
+ open: "[",
51
+ close: "]"
52
+ },
53
+ {
54
+ open: "\"",
55
+ close: "\"",
56
+ notIn: ["string"]
57
+ }
58
+ ],
59
+ surroundingPairs: [
60
+ {
61
+ open: "{",
62
+ close: "}"
63
+ },
64
+ {
65
+ open: "[",
66
+ close: "]"
67
+ },
68
+ {
69
+ open: "\"",
70
+ close: "\""
71
+ }
72
+ ],
73
+ wordPattern: /[^\s"{}[\],:]+/u
74
+ });
75
+ monaco.languages.setMonarchTokensProvider(languageId, grammar());
76
+ }
77
+ function itemKinds(monaco) {
78
+ const kinds = monaco.languages.CompletionItemKind;
79
+ return {
80
+ method: kinds.Keyword,
81
+ endpoint: kinds.Function,
82
+ index: kinds.Struct,
83
+ alias: kinds.Interface,
84
+ key: kinds.Field
85
+ };
86
+ }
87
+ function registerElasticsearchLanguage(monaco, modelPath, hooks, languageId = ELASTICSEARCH_LANGUAGE_ID) {
88
+ const kinds = itemKinds(monaco);
89
+ const path = monaco.Uri.parse(modelPath).toString();
90
+ const answering = (model) => model.uri.toString() === path ? hooks.assist() : null;
91
+ return [monaco.languages.registerCompletionItemProvider(languageId, {
92
+ triggerCharacters: [
93
+ "/",
94
+ "\"",
95
+ "_"
96
+ ],
97
+ provideCompletionItems: (model, position) => {
98
+ const assist = answering(model);
99
+ if (assist === null) return { suggestions: [] };
100
+ return { suggestions: assist.complete(model.getValue(), model.getOffsetAt(position)).map((candidate, index) => {
101
+ return {
102
+ label: candidate.label,
103
+ kind: kinds[candidate.kind],
104
+ insertText: candidate.insertText,
105
+ insertTextRules: candidate.snippet ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet : void 0,
106
+ detail: candidate.detail,
107
+ documentation: candidate.documentation,
108
+ range: new monaco.Range(position.lineNumber, Math.max(1, position.column - candidate.replace), position.lineNumber, position.column),
109
+ sortText: `${String(candidate.sortGroup)}:${String(index).padStart(4, "0")}`
110
+ };
111
+ }) };
112
+ }
113
+ }), monaco.languages.registerHoverProvider(languageId, { provideHover: (model, position) => {
114
+ const assist = answering(model);
115
+ if (assist === null) return null;
116
+ const card = assist.hover(model.getValue(), model.getOffsetAt(position));
117
+ if (card === null) return null;
118
+ const from = model.getPositionAt(card.from);
119
+ const to = model.getPositionAt(card.to);
120
+ return {
121
+ range: new monaco.Range(from.lineNumber, from.column, to.lineNumber, to.column),
122
+ contents: card.markdown.map((value) => {
123
+ return { value };
124
+ })
125
+ };
126
+ } })];
127
+ }
128
+ const ELASTICSEARCH_MARKER_OWNER = "inkstone-elasticsearch";
129
+ function setElasticsearchMarkers(monaco, model, markers, wording) {
130
+ const severities = monaco.MarkerSeverity;
131
+ monaco.editor.setModelMarkers(model, ELASTICSEARCH_MARKER_OWNER, markers.map((marker) => {
132
+ const from = model.getPositionAt(marker.from);
133
+ const to = model.getPositionAt(marker.to);
134
+ return {
135
+ severity: marker.severity === "error" ? severities.Error : marker.severity === "warning" ? severities.Warning : severities.Hint,
136
+ message: wording(marker),
137
+ startLineNumber: from.lineNumber,
138
+ startColumn: from.column,
139
+ endLineNumber: to.lineNumber,
140
+ endColumn: to.column
141
+ };
142
+ }));
143
+ }
144
+ //#endregion
145
+ export { ELASTICSEARCH_LANGUAGE_ID, ELASTICSEARCH_MARKER_OWNER, defineElasticsearchLanguage, registerElasticsearchLanguage, setElasticsearchMarkers };
@@ -0,0 +1,93 @@
1
+ import { a as ElasticsearchAssist, t as ElasticsearchMarker } from "./markers-BctX905j.js";
2
+ import { MonacoModule } from "./monaco.js";
3
+ import { ReactNode } from "react";
4
+ import { editor } from "monaco-editor";
5
+ //#region src/react.d.ts
6
+ /**
7
+ * What a host reaches into the editor for: inserting at the caret — an index tree is an input
8
+ * device — and the focus.
9
+ */
10
+ interface ElasticsearchEditorHandle {
11
+ insert: (text: string) => void;
12
+ focus: () => void;
13
+ }
14
+ /**
15
+ * Where the operator is in the document, in offsets — what `runnableRequest` reads to decide
16
+ * the request a run sends. `from` and `to` are the selection's bounds and equal where nothing
17
+ * is selected; the text rides with them because the two must belong to the same instant.
18
+ */
19
+ interface ElasticsearchCaret {
20
+ text: string;
21
+ from: number;
22
+ to: number;
23
+ }
24
+ interface ElasticsearchEditorProps {
25
+ value: string;
26
+ onChange: (value: string) => void;
27
+ /**
28
+ * Render with the dark theme; the component never reads the OS `prefers-color-scheme`.
29
+ *
30
+ * @default false
31
+ */
32
+ dark?: boolean;
33
+ /**
34
+ * Editor height in px, or `"fill"` to consume the parent's resolved height (the parent must be
35
+ * a `min-height: 0` flex item).
36
+ */
37
+ height?: number | "fill";
38
+ fontSize?: number;
39
+ /**
40
+ * UI locale for Monaco's chrome — page-global and locked by the first editor to mount.
41
+ */
42
+ locale?: "en" | "zh-cn";
43
+ placeholder?: string;
44
+ /**
45
+ * The editor's accessible name, set on Monaco's own input surface.
46
+ */
47
+ ariaLabel: string;
48
+ /**
49
+ * The Monaco language id the model registers under; the grammar is defined under it.
50
+ *
51
+ * @default "elasticsearch"
52
+ */
53
+ languageId?: string;
54
+ loading?: ReactNode;
55
+ failure?: ReactNode;
56
+ /**
57
+ * Ctrl/Cmd-Enter — the run key, handed the editor's live caret; the host reads the request
58
+ * to send off it with `runnableRequest`.
59
+ */
60
+ onRun?: (caret: ElasticsearchCaret) => void;
61
+ /**
62
+ * The label the run action shows in Monaco's own chrome.
63
+ *
64
+ * @default "Run request"
65
+ */
66
+ runActionLabel?: string;
67
+ /**
68
+ * The caret moved, or the text under it changed.
69
+ */
70
+ onCaret?: (caret: ElasticsearchCaret) => void;
71
+ onReady?: (handle: ElasticsearchEditorHandle) => void;
72
+ /**
73
+ * Runs after this component's own mount wiring, with the live editor and the monaco module.
74
+ */
75
+ onMount?: (instance: editor.IStandaloneCodeEditor, monaco: MonacoModule) => void;
76
+ /**
77
+ * What completion and hover answer from; neither without it.
78
+ */
79
+ assist?: ElasticsearchAssist;
80
+ /**
81
+ * The words each marker shows, per code (`requestMarkers`) — the host's language. No
82
+ * markers are drawn without it.
83
+ */
84
+ markerWording?: (marker: ElasticsearchMarker) => string;
85
+ }
86
+ /**
87
+ * The request editing surface: Monaco under the `elasticsearch` grammar, one or more requests
88
+ * in the console's spelling, the run key handing the caret over, completion and hover wired
89
+ * through `registerElasticsearchLanguage`, and the markers refreshed on every change.
90
+ */
91
+ declare function ElasticsearchEditor({ value, onChange, dark, height, fontSize, locale, placeholder, ariaLabel, languageId, loading, failure, onRun, runActionLabel, onCaret, onReady, onMount, assist, markerWording }: ElasticsearchEditorProps): import("react").JSX.Element;
92
+ //#endregion
93
+ export { ElasticsearchCaret, ElasticsearchEditor, ElasticsearchEditorHandle, ElasticsearchEditorProps };
package/dist/react.js ADDED
@@ -0,0 +1,104 @@
1
+ import { t as requestMarkers } from "./markers-DBJshw-Y.js";
2
+ import { ELASTICSEARCH_LANGUAGE_ID, defineElasticsearchLanguage, registerElasticsearchLanguage, setElasticsearchMarkers } from "./monaco.js";
3
+ import { MonacoEditor } from "@coldsmirk/inkstone-react/monaco";
4
+ import { useEffect, useInsertionEffect, useRef, useState } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/react.tsx
7
+ const EDITOR_ID_KEY = Symbol.for("coldsmirk.inkstone.elasticsearch.editorId");
8
+ function nextEditorId() {
9
+ const holder = globalThis;
10
+ const id = holder[EDITOR_ID_KEY] ?? 0;
11
+ holder[EDITOR_ID_KEY] = id + 1;
12
+ return id;
13
+ }
14
+ function useLatest(value) {
15
+ const ref = useRef(value);
16
+ useInsertionEffect(() => {
17
+ ref.current = value;
18
+ });
19
+ return ref;
20
+ }
21
+ function caretOf(instance) {
22
+ const model = instance.getModel();
23
+ const selection = instance.getSelection();
24
+ if (model === null || selection === null) return {
25
+ text: "",
26
+ from: 0,
27
+ to: 0
28
+ };
29
+ return {
30
+ text: model.getValue(),
31
+ from: model.getOffsetAt(selection.getStartPosition()),
32
+ to: model.getOffsetAt(selection.getEndPosition())
33
+ };
34
+ }
35
+ function ElasticsearchEditor({ value, onChange, dark = false, height = 200, fontSize, locale, placeholder, ariaLabel, languageId = ELASTICSEARCH_LANGUAGE_ID, loading, failure, onRun, runActionLabel = "Run request", onCaret, onReady, onMount, assist, markerWording }) {
36
+ const [modelPath] = useState(() => `file:///inkstone-elasticsearch/${String(nextEditorId())}.es`);
37
+ const bindings = useRef([]);
38
+ const assistRef = useLatest(assist ?? null);
39
+ const runRef = useLatest(onRun);
40
+ const caretChangedRef = useLatest(onCaret);
41
+ const readyRef = useLatest(onReady);
42
+ const mountRef = useLatest(onMount);
43
+ const wordingRef = useLatest(markerWording);
44
+ useEffect(() => () => {
45
+ for (const binding of bindings.current) binding.dispose();
46
+ bindings.current = [];
47
+ }, []);
48
+ const fill = height === "fill";
49
+ return /* @__PURE__ */ jsx("div", {
50
+ style: fill ? {
51
+ height: "100%",
52
+ minHeight: 0
53
+ } : { height },
54
+ children: /* @__PURE__ */ jsx(MonacoEditor, {
55
+ showLineNumbers: true,
56
+ dark,
57
+ failure,
58
+ fontSize,
59
+ height: fill ? "100%" : height,
60
+ language: languageId,
61
+ loading,
62
+ locale,
63
+ options: { ariaLabel },
64
+ path: modelPath,
65
+ placeholder,
66
+ value,
67
+ beforeMount: (monaco) => {
68
+ defineElasticsearchLanguage(monaco, languageId);
69
+ },
70
+ onChange,
71
+ onMount: (live, monaco) => {
72
+ const mark = () => {
73
+ const model = live.getModel();
74
+ const wording = wordingRef.current;
75
+ if (model !== null && wording !== void 0) setElasticsearchMarkers(monaco, model, requestMarkers(model.getValue()), wording);
76
+ };
77
+ bindings.current.push(live.onDidChangeCursorSelection(() => caretChangedRef.current?.(caretOf(live))), live.onDidChangeModelContent(mark), live.addAction({
78
+ id: "inkstone-elasticsearch.run",
79
+ label: runActionLabel,
80
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
81
+ run: (current) => runRef.current?.(caretOf(current))
82
+ }), ...registerElasticsearchLanguage(monaco, modelPath, { assist: () => assistRef.current }, languageId));
83
+ mark();
84
+ readyRef.current?.({
85
+ insert: (text) => {
86
+ const selection = live.getSelection();
87
+ if (selection) live.executeEdits("inkstone-elasticsearch-insert", [{
88
+ range: selection,
89
+ text,
90
+ forceMoveMarkers: true
91
+ }]);
92
+ live.focus();
93
+ },
94
+ focus: () => {
95
+ live.focus();
96
+ }
97
+ });
98
+ mountRef.current?.(live, monaco);
99
+ }
100
+ })
101
+ });
102
+ }
103
+ //#endregion
104
+ export { ElasticsearchEditor };
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@coldsmirk/inkstone-elasticsearch",
3
+ "version": "0.22.0",
4
+ "description": "Elasticsearch request editing intelligence for Monaco: the console spelling (a method and a path, the body after), endpoint and index completion, body keys by context, request markers, and a drop-in React request editor.",
5
+ "keywords": [
6
+ "elasticsearch",
7
+ "monaco",
8
+ "completion",
9
+ "editor",
10
+ "autocomplete",
11
+ "kibana"
12
+ ],
13
+ "homepage": "https://github.com/coldsmirk/inkstone/tree/main/packages/elasticsearch#readme",
14
+ "bugs": "https://github.com/coldsmirk/inkstone/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/coldsmirk/inkstone.git",
18
+ "directory": "packages/elasticsearch"
19
+ },
20
+ "license": "UNLICENSED",
21
+ "author": {
22
+ "name": "Venus"
23
+ },
24
+ "sideEffects": false,
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ },
31
+ "./monaco": {
32
+ "types": "./dist/monaco.d.ts",
33
+ "default": "./dist/monaco.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react.d.ts",
37
+ "default": "./dist/react.js"
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "types": "./dist/index.d.ts",
42
+ "files": [
43
+ "dist"
44
+ ],
45
+ "devDependencies": {
46
+ "monaco-editor": "^0.56.0",
47
+ "react": "^19.2.8",
48
+ "react-dom": "^19.2.8",
49
+ "@coldsmirk/inkstone-react": "^0.22.0"
50
+ },
51
+ "peerDependencies": {
52
+ "monaco-editor": "^0.56.0",
53
+ "react": ">=19",
54
+ "@coldsmirk/inkstone-react": "^0.22.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@coldsmirk/inkstone-react": {
58
+ "optional": true
59
+ },
60
+ "monaco-editor": {
61
+ "optional": true
62
+ },
63
+ "react": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "engines": {
68
+ "node": ">=24"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "scripts": {
74
+ "build": "tsdown",
75
+ "clean": "rimraf dist",
76
+ "typecheck": "tsc --noEmit"
77
+ }
78
+ }