@filamind-app/core 0.1.0 → 0.1.2
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 +8 -1
- package/dist/index.d.ts +33 -2
- package/dist/index.js +113 -0
- package/package.json +18 -6
package/README.md
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
The shared foundation of the FilaMind suite — **framework-agnostic TypeScript** consumed by FilaMind 3d
|
|
4
4
|
(web), FilaMind screen (touch), and FilaMind flow. Phase 0.
|
|
5
5
|
|
|
6
|
+
[](https://github.com/filamind-app/filamind-core/actions/workflows/ci.yml)
|
|
7
|
+
[](https://www.npmjs.com/package/@filamind-app/core)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://www.typescriptlang.org)
|
|
10
|
+
|
|
6
11
|
## What's here
|
|
7
12
|
| Module | Purpose |
|
|
8
13
|
| --- | --- |
|
|
@@ -11,6 +16,7 @@ The shared foundation of the FilaMind suite — **framework-agnostic TypeScript*
|
|
|
11
16
|
| `state/printer.ts` | `PrinterState` — merge-patch from `notify_status_update`, **1 s coalescing + `motion_report` fast-path** |
|
|
12
17
|
| `moonraker/connector.ts` | the backend-agnostic `Connector` seam (Moonraker first; remote/others later) |
|
|
13
18
|
| `moonraker/client.ts` | `MoonrakerClient` — reconnecting JSON-RPC WS (**injectable socket**, backoff **+ jitter + max-attempts**), id-correlated, notify fan-out, re-subscribe on reconnect, **REST file channel** (`upload`/`download`) |
|
|
19
|
+
| `moonraker/discovery.ts` | **zero-config endpoint discovery** — `resolveMoonrakerUrl()` races candidate endpoints (same-origin proxy / direct `:7125` / localhost) → first to open wins; runtime `override` short-circuits. Socket-injectable |
|
|
14
20
|
| `moonraker/subscriptions.ts` | the **versioned** canonical subscription contract — `NARROW_STATUS` / `FULL_CONTROL` tiers + `tier()` |
|
|
15
21
|
| `printer/klippy.ts` | the Klippy lifecycle (`ready`/`startup`/`shutdown`/`error`/`disconnected`) — tracked apart from the socket so a FIRMWARE_RESTART re-seeds instead of showing stale-as-live |
|
|
16
22
|
| `printer/prompt-parser.ts` | parses Klipper's `// action:prompt_*` protocol into structured modal dialogs (`PromptParser`) |
|
|
@@ -20,7 +26,8 @@ The shared foundation of the FilaMind suite — **framework-agnostic TypeScript*
|
|
|
20
26
|
| `backup/restore-points.ts` | reversible, retention-bounded **`RestorePoints`** (back-up-first substrate for config/flash/migrate) |
|
|
21
27
|
| `observability/logger.ts` | pluggable ring-buffer `Logger` (the diagnostics-bundle source; replaces silent `catch {}`) |
|
|
22
28
|
| `registry/widget-registry.ts` | the cross-surface widget/plugin registry + `aggregateSubscriptions` |
|
|
23
|
-
| `
|
|
29
|
+
| `remote/commands.ts` · `remote/command-sender.ts` | the cross-surface command bus — **UI-only** commands (navigate / message / locate) over a Moonraker **agent** connection. `CommandSender` identifies as an agent, re-identifies on reconnect, is single-flight, and sanitizes control/bidi text; it is never a mutation path (those still go through `WriteArbiter`) |
|
|
30
|
+
| `theme/tokens.ts` | the theme palettes as `--fm-*` design tokens — 3 signature Pharaonic themes (Tutankhamun · Horus · Anubis) + neutral **light** / **dark** |
|
|
24
31
|
| `i18n/locale-meta.ts` | the 19 shipped locales `{ code, name, rtl, dir }` + RTL list + CLDR `pluralCategory` (`Intl.PluralRules`) |
|
|
25
32
|
| `i18n/translator.ts` | framework-agnostic `translate` / `Translator` + the backend message-code contract (`resolveMessage`) |
|
|
26
33
|
| `i18n/locales/*.json` | drop-in catalogs (en + ar shipped as proof, incl. the full Arabic plural set) |
|
package/dist/index.d.ts
CHANGED
|
@@ -143,6 +143,37 @@ declare class MoonrakerClient implements Connector {
|
|
|
143
143
|
private onMessage;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** A minimal view of `location` — the DOM Location satisfies it; tests pass a plain object. */
|
|
147
|
+
interface LocationLike {
|
|
148
|
+
protocol?: string;
|
|
149
|
+
hostname?: string;
|
|
150
|
+
}
|
|
151
|
+
interface DiscoveryOptions {
|
|
152
|
+
/** Explicit runtime override (e.g. from settings). If set, it wins immediately — no probing. */
|
|
153
|
+
override?: string;
|
|
154
|
+
/** Candidate ws(s):// URLs to race. If omitted, they are derived from `location`. */
|
|
155
|
+
candidates?: string[];
|
|
156
|
+
/** Overall budget before discovery gives up (ms). Default 3000. */
|
|
157
|
+
timeoutMs?: number;
|
|
158
|
+
/** Injectable socket factory (tests / non-DOM). Default: `new WebSocket`. */
|
|
159
|
+
wsFactory?: (url: string) => WebSocketLike;
|
|
160
|
+
/** Source for deriving candidates. Default: `globalThis.location`. */
|
|
161
|
+
location?: LocationLike;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Ordered, de-duplicated candidate endpoints:
|
|
165
|
+
* 1. same-origin `/websocket` (the suite deploy reverse-proxies Moonraker here),
|
|
166
|
+
* 2. the page host on Moonraker's default `:7125` (direct, no proxy),
|
|
167
|
+
* 3. `localhost:7125` (on-printer / Tauri webview, whose origin is `tauri.localhost`).
|
|
168
|
+
*/
|
|
169
|
+
declare function deriveCandidates(location?: LocationLike): string[];
|
|
170
|
+
/**
|
|
171
|
+
* Resolve a reachable Moonraker WebSocket URL. `override` wins immediately; otherwise the
|
|
172
|
+
* candidates are raced and the first socket to open wins (the rest are closed). Rejects if
|
|
173
|
+
* none open within `timeoutMs`.
|
|
174
|
+
*/
|
|
175
|
+
declare function resolveMoonrakerUrl(opts?: DiscoveryOptions): Promise<string>;
|
|
176
|
+
|
|
146
177
|
declare const SUBSCRIPTION_CONTRACT_VERSION = 1;
|
|
147
178
|
/** Status-only tier (farm overview / ambient screen): the minimum to show state + progress. */
|
|
148
179
|
declare const NARROW_STATUS: SubscriptionMap;
|
|
@@ -361,7 +392,7 @@ interface ThemeTokens {
|
|
|
361
392
|
warning: string;
|
|
362
393
|
danger: string;
|
|
363
394
|
}
|
|
364
|
-
type ThemeName = 'tutankhamun' | 'horus' | 'anubis';
|
|
395
|
+
type ThemeName = 'tutankhamun' | 'horus' | 'anubis' | 'light' | 'dark';
|
|
365
396
|
declare const themes: Record<ThemeName, ThemeTokens>;
|
|
366
397
|
declare const DEFAULT_THEME: ThemeName;
|
|
367
398
|
/** `{ "--fm-bg": "#0E0F12", ... }` for a theme. */
|
|
@@ -512,4 +543,4 @@ declare function applySettings(s: UserSettings, el?: {
|
|
|
512
543
|
dir: 'ltr' | 'rtl';
|
|
513
544
|
};
|
|
514
545
|
|
|
515
|
-
export { type AgentAllow, type AgentEvent, type BackendMessage, type Catalog, CommandSender, type CommandSenderOptions, type ConnectionState, type Connector, type ConnectorCallbacks, DEFAULT_LOCALE, DEFAULT_SETTINGS, DEFAULT_THEME, FILAMIND_COMMAND_EVENT, FULL_CONTROL, FilaMindSession, type IdentifyInfo, type KlippyState, LOCALES, type Listener, type LocaleMeta, type LogEntry, type LogLevel, Logger, MoonrakerClient, type MoonrakerClientOptions, NARROW_STATUS, NULL_LOGGER, Observable, type Params, type PluralCategory, type PrinterObjects, PrinterState, type PromptButton, type PromptDialog, type PromptEvent, PromptParser, REMOTE_MESSAGE_LEVELS, REMOTE_VIEWS, RTL_LOCALES, type RemoteCommand, type RemoteMessageLevel, type RemoteView, type RestorePoint, RestorePoints, type RestoreStore, SETTINGS_VERSION, SUBSCRIPTION_CONTRACT_VERSION, type SessionOptions, type SettingsPersistence, SettingsStore, type Source, type Stamped, type SubscriptionMap, type SubscriptionTier, type SurfaceTarget, type ThemeName, type ThemeTokens, Translator, UNKNOWN, type UserSettings, type WebSocketLike, type WidgetDefinition, WriteArbiter, type WriteGuard, WriteRefused, _resetRegistry, aggregateSubscriptions, applySettings, applyTheme, deepMerge, deriveKlippyState, deriveMachineId, fnv1a, freshness, getWidget, getWidgets, handleAgentCommand, isKlippyLive, isRtl, isStale, localStoragePersistence, localeMeta, memoryPersistence, memoryRestoreStore, mergeSubscriptions, migrate, moonrakerDbPersistence, parseAgentEvent, parseCommand, pluralCategory, registerWidget, resolveMessage, roamSettings, stamp, themeToCssVars, themes, tier, translate };
|
|
546
|
+
export { type AgentAllow, type AgentEvent, type BackendMessage, type Catalog, CommandSender, type CommandSenderOptions, type ConnectionState, type Connector, type ConnectorCallbacks, DEFAULT_LOCALE, DEFAULT_SETTINGS, DEFAULT_THEME, type DiscoveryOptions, FILAMIND_COMMAND_EVENT, FULL_CONTROL, FilaMindSession, type IdentifyInfo, type KlippyState, LOCALES, type Listener, type LocaleMeta, type LocationLike, type LogEntry, type LogLevel, Logger, MoonrakerClient, type MoonrakerClientOptions, NARROW_STATUS, NULL_LOGGER, Observable, type Params, type PluralCategory, type PrinterObjects, PrinterState, type PromptButton, type PromptDialog, type PromptEvent, PromptParser, REMOTE_MESSAGE_LEVELS, REMOTE_VIEWS, RTL_LOCALES, type RemoteCommand, type RemoteMessageLevel, type RemoteView, type RestorePoint, RestorePoints, type RestoreStore, SETTINGS_VERSION, SUBSCRIPTION_CONTRACT_VERSION, type SessionOptions, type SettingsPersistence, SettingsStore, type Source, type Stamped, type SubscriptionMap, type SubscriptionTier, type SurfaceTarget, type ThemeName, type ThemeTokens, Translator, UNKNOWN, type UserSettings, type WebSocketLike, type WidgetDefinition, WriteArbiter, type WriteGuard, WriteRefused, _resetRegistry, aggregateSubscriptions, applySettings, applyTheme, deepMerge, deriveCandidates, deriveKlippyState, deriveMachineId, fnv1a, freshness, getWidget, getWidgets, handleAgentCommand, isKlippyLive, isRtl, isStale, localStoragePersistence, localeMeta, memoryPersistence, memoryRestoreStore, mergeSubscriptions, migrate, moonrakerDbPersistence, parseAgentEvent, parseCommand, pluralCategory, registerWidget, resolveMessage, resolveMoonrakerUrl, roamSettings, stamp, themeToCssVars, themes, tier, translate };
|
package/dist/index.js
CHANGED
|
@@ -270,6 +270,85 @@ function xhrUpload(url, form, onProgress) {
|
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
// src/moonraker/discovery.ts
|
|
274
|
+
var defaultWsFactory2 = (url) => new WebSocket(url);
|
|
275
|
+
function deriveCandidates(location) {
|
|
276
|
+
const loc = location ?? globalThis.location ?? {};
|
|
277
|
+
const wsProto = loc.protocol === "https:" ? "wss" : "ws";
|
|
278
|
+
const host = loc.hostname && loc.hostname.length > 0 ? loc.hostname : "localhost";
|
|
279
|
+
return [
|
|
280
|
+
.../* @__PURE__ */ new Set([
|
|
281
|
+
`${wsProto}://${host}/websocket`,
|
|
282
|
+
`${wsProto}://${host}:7125/websocket`,
|
|
283
|
+
`ws://localhost:7125/websocket`
|
|
284
|
+
])
|
|
285
|
+
];
|
|
286
|
+
}
|
|
287
|
+
function resolveMoonrakerUrl(opts = {}) {
|
|
288
|
+
const override = opts.override?.trim();
|
|
289
|
+
if (override) return Promise.resolve(override);
|
|
290
|
+
const candidates = opts.candidates ?? deriveCandidates(opts.location);
|
|
291
|
+
if (candidates.length === 0) {
|
|
292
|
+
return Promise.reject(new Error("moonraker discovery: no candidate endpoints"));
|
|
293
|
+
}
|
|
294
|
+
const makeWs = opts.wsFactory ?? defaultWsFactory2;
|
|
295
|
+
const timeoutMs = opts.timeoutMs ?? 3e3;
|
|
296
|
+
return new Promise((resolve, reject) => {
|
|
297
|
+
let settled = false;
|
|
298
|
+
let remaining = candidates.length;
|
|
299
|
+
const sockets = [];
|
|
300
|
+
const timer = setTimeout(finishFail, timeoutMs);
|
|
301
|
+
function cleanup() {
|
|
302
|
+
clearTimeout(timer);
|
|
303
|
+
for (const s of sockets) {
|
|
304
|
+
s.onopen = null;
|
|
305
|
+
s.onerror = null;
|
|
306
|
+
s.onclose = null;
|
|
307
|
+
try {
|
|
308
|
+
s.close();
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function finishOk(url) {
|
|
314
|
+
if (settled) return;
|
|
315
|
+
settled = true;
|
|
316
|
+
cleanup();
|
|
317
|
+
resolve(url);
|
|
318
|
+
}
|
|
319
|
+
function finishFail() {
|
|
320
|
+
if (settled) return;
|
|
321
|
+
settled = true;
|
|
322
|
+
cleanup();
|
|
323
|
+
reject(new Error(`moonraker discovery: no endpoint responded (${candidates.join(", ")})`));
|
|
324
|
+
}
|
|
325
|
+
function lose() {
|
|
326
|
+
remaining -= 1;
|
|
327
|
+
if (remaining <= 0) finishFail();
|
|
328
|
+
}
|
|
329
|
+
for (const url of candidates) {
|
|
330
|
+
let s;
|
|
331
|
+
try {
|
|
332
|
+
s = makeWs(url);
|
|
333
|
+
} catch {
|
|
334
|
+
lose();
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
sockets.push(s);
|
|
338
|
+
let done = false;
|
|
339
|
+
const fail = () => {
|
|
340
|
+
if (!done) {
|
|
341
|
+
done = true;
|
|
342
|
+
lose();
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
s.onopen = () => finishOk(url);
|
|
346
|
+
s.onerror = fail;
|
|
347
|
+
s.onclose = fail;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
273
352
|
// src/moonraker/subscriptions.ts
|
|
274
353
|
var SUBSCRIPTION_CONTRACT_VERSION = 1;
|
|
275
354
|
var NARROW_STATUS = {
|
|
@@ -759,6 +838,38 @@ var themes = {
|
|
|
759
838
|
success: "#6E7C3A",
|
|
760
839
|
warning: "#D98E2B",
|
|
761
840
|
danger: "#8C1F1A"
|
|
841
|
+
},
|
|
842
|
+
// neutral light — a conventional bright UI for users who prefer it
|
|
843
|
+
light: {
|
|
844
|
+
bg: "#F7F8FA",
|
|
845
|
+
surface: "#FFFFFF",
|
|
846
|
+
surface2: "#EEF1F5",
|
|
847
|
+
border: "#D6DBE2",
|
|
848
|
+
text: "#1A1D23",
|
|
849
|
+
textMuted: "#5B6573",
|
|
850
|
+
primary: "#2563EB",
|
|
851
|
+
primaryContrast: "#FFFFFF",
|
|
852
|
+
secondary: "#64748B",
|
|
853
|
+
accent: "#0D9488",
|
|
854
|
+
success: "#15803D",
|
|
855
|
+
warning: "#B45309",
|
|
856
|
+
danger: "#DC2626"
|
|
857
|
+
},
|
|
858
|
+
// neutral dark — a conventional dark UI, distinct from the warm Pharaonic palettes
|
|
859
|
+
dark: {
|
|
860
|
+
bg: "#0F1115",
|
|
861
|
+
surface: "#171A21",
|
|
862
|
+
surface2: "#1F232C",
|
|
863
|
+
border: "#2C313C",
|
|
864
|
+
text: "#E6E8EC",
|
|
865
|
+
textMuted: "#9AA2AE",
|
|
866
|
+
primary: "#4F8EF7",
|
|
867
|
+
primaryContrast: "#0B0D11",
|
|
868
|
+
secondary: "#6B7280",
|
|
869
|
+
accent: "#14B8A6",
|
|
870
|
+
success: "#22A06B",
|
|
871
|
+
warning: "#E0A92E",
|
|
872
|
+
danger: "#E5484D"
|
|
762
873
|
}
|
|
763
874
|
};
|
|
764
875
|
var DEFAULT_THEME = "tutankhamun";
|
|
@@ -1140,6 +1251,7 @@ export {
|
|
|
1140
1251
|
applySettings,
|
|
1141
1252
|
applyTheme,
|
|
1142
1253
|
deepMerge,
|
|
1254
|
+
deriveCandidates,
|
|
1143
1255
|
deriveKlippyState,
|
|
1144
1256
|
deriveMachineId,
|
|
1145
1257
|
fnv1a,
|
|
@@ -1162,6 +1274,7 @@ export {
|
|
|
1162
1274
|
pluralCategory,
|
|
1163
1275
|
registerWidget,
|
|
1164
1276
|
resolveMessage,
|
|
1277
|
+
resolveMoonrakerUrl,
|
|
1165
1278
|
roamSettings,
|
|
1166
1279
|
stamp,
|
|
1167
1280
|
themeToCssVars,
|
package/package.json
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@filamind-app/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared foundation for the FilaMind suite — Moonraker client, reactive printer state, provenance-stamped values, design tokens, and the widget/plugin registry. Framework-agnostic TypeScript.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
|
-
"repository": {
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/filamind-app/filamind-core.git"
|
|
9
|
+
},
|
|
7
10
|
"homepage": "https://github.com/filamind-app/filamind-core#readme",
|
|
8
|
-
"bugs": {
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/filamind-app/filamind-core/issues"
|
|
13
|
+
},
|
|
9
14
|
"type": "module",
|
|
10
15
|
"main": "./dist/index.js",
|
|
11
16
|
"module": "./dist/index.js",
|
|
12
17
|
"types": "./dist/index.d.ts",
|
|
13
18
|
"exports": {
|
|
14
|
-
".": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
15
23
|
},
|
|
16
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
17
27
|
"sideEffects": false,
|
|
18
|
-
"publishConfig": {
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
19
31
|
"scripts": {
|
|
20
32
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
21
33
|
"dev": "tsup src/index.ts --format esm --dts --watch",
|