@hashsome/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/app/entry.client.d.ts +1 -0
- package/dist/app/entry.client.js +7 -0
- package/dist/app/entry.client.js.map +1 -0
- package/dist/app/entry.server.d.ts +2 -0
- package/dist/app/entry.server.js +20 -0
- package/dist/app/entry.server.js.map +1 -0
- package/dist/app/index.d.ts +1 -0
- package/dist/app/index.js +1 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/root.d.ts +18 -0
- package/dist/app/root.js +36 -0
- package/dist/app/root.js.map +1 -0
- package/dist/bin/hashsome.d.ts +2 -0
- package/dist/bin/hashsome.js +17 -0
- package/dist/bin/hashsome.js.map +1 -0
- package/dist/src/cli/build.d.ts +4 -0
- package/dist/src/cli/build.js +18 -0
- package/dist/src/cli/build.js.map +1 -0
- package/dist/src/cli/dev.d.ts +1 -0
- package/dist/src/cli/dev.js +19 -0
- package/dist/src/cli/dev.js.map +1 -0
- package/dist/src/cli/package.d.ts +13 -0
- package/dist/src/cli/package.js +115 -0
- package/dist/src/cli/package.js.map +1 -0
- package/dist/src/cli/start.d.ts +1 -0
- package/dist/src/cli/start.js +12 -0
- package/dist/src/cli/start.js.map +1 -0
- package/dist/src/config.d.ts +36 -0
- package/dist/src/config.js +54 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/release/bundle.d.ts +10 -0
- package/dist/src/release/bundle.js +50 -0
- package/dist/src/release/bundle.js.map +1 -0
- package/dist/src/release/package.d.ts +26 -0
- package/dist/src/release/package.js +102 -0
- package/dist/src/release/package.js.map +1 -0
- package/dist/src/release/render.d.ts +32 -0
- package/dist/src/release/render.js +335 -0
- package/dist/src/release/render.js.map +1 -0
- package/dist/src/serve.d.ts +8 -0
- package/dist/src/serve.js +14 -0
- package/dist/src/serve.js.map +1 -0
- package/dist/src/server/assets.d.ts +10 -0
- package/dist/src/server/assets.js +46 -0
- package/dist/src/server/assets.js.map +1 -0
- package/dist/src/server/integrations.d.ts +12 -0
- package/dist/src/server/integrations.js +39 -0
- package/dist/src/server/integrations.js.map +1 -0
- package/dist/src/server/production.d.ts +7 -0
- package/dist/src/server/production.js +45 -0
- package/dist/src/server/production.js.map +1 -0
- package/dist/src/server/proxy.d.ts +34 -0
- package/dist/src/server/proxy.js +165 -0
- package/dist/src/server/proxy.js.map +1 -0
- package/dist/src/server/websocket.d.ts +5 -0
- package/dist/src/server/websocket.js +17 -0
- package/dist/src/server/websocket.js.map +1 -0
- package/dist/src/vite-config.d.ts +12 -0
- package/dist/src/vite-config.js +35 -0
- package/dist/src/vite-config.js.map +1 -0
- package/dist/src/vite.d.ts +5 -0
- package/dist/src/vite.js +42 -0
- package/dist/src/vite.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { encodeMessage, parseClientMessage, parseEntityRef, UnknownEntityError, } from '@hashsome/core';
|
|
2
|
+
const OPEN = 1;
|
|
3
|
+
const RANGES = ['1h', '1d', '1w', '1m'];
|
|
4
|
+
const BUCKETS = ['5m', '1h', '1d'];
|
|
5
|
+
/** A history query's fields, kept only when they are ones it has: the rest came from a browser. A
|
|
6
|
+
* missing or unknown range is a request that cannot be answered. */
|
|
7
|
+
function historyArgs(args) {
|
|
8
|
+
const { range, bucket } = args ?? {};
|
|
9
|
+
if (typeof range !== 'string' || !RANGES.includes(range)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
range: range,
|
|
14
|
+
...(typeof bucket === 'string' && BUCKETS.includes(bucket)
|
|
15
|
+
? { bucket: bucket }
|
|
16
|
+
: {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** The only fields a browse query has, kept only when they are strings: the rest came from a browser. */
|
|
20
|
+
function browseArgs(args) {
|
|
21
|
+
return {
|
|
22
|
+
...(typeof args?.path === 'string' ? { path: args.path } : {}),
|
|
23
|
+
...(typeof args?.search === 'string' ? { search: args.search } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Multiplexes browser connections onto server-held integrations. Tokens never
|
|
28
|
+
* reach the browser; current entities are replayed from the integrations' stores
|
|
29
|
+
* so a waking tablet gets a full picture immediately.
|
|
30
|
+
*
|
|
31
|
+
* The proxy does all the addressing: it parses `<integration>:<id>` and hands the integration only
|
|
32
|
+
* its local id. A subscription is resolved once the owning integration is connected — until then
|
|
33
|
+
* the browser has no answer (a component shows its loading state) — and an id the integration does
|
|
34
|
+
* not have is answered with `entity: null` and logged.
|
|
35
|
+
*/
|
|
36
|
+
export class Proxy {
|
|
37
|
+
#integrations = new Map();
|
|
38
|
+
#log;
|
|
39
|
+
constructor(integrations, options = {}) {
|
|
40
|
+
for (const integration of integrations) {
|
|
41
|
+
this.#integrations.set(integration.id, integration);
|
|
42
|
+
}
|
|
43
|
+
this.#log = options.log ?? (() => { });
|
|
44
|
+
}
|
|
45
|
+
handleConnection(socket) {
|
|
46
|
+
const send = (message) => {
|
|
47
|
+
if (socket.readyState === OPEN) {
|
|
48
|
+
socket.send(encodeMessage(message));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const subscriptions = new Map();
|
|
52
|
+
const cleanups = [];
|
|
53
|
+
for (const integration of this.#integrations.values()) {
|
|
54
|
+
const report = (status) => send({ type: 'status', integration: integration.id, status });
|
|
55
|
+
report(integration.status);
|
|
56
|
+
cleanups.push(integration.onStatusChange(report));
|
|
57
|
+
}
|
|
58
|
+
const answer = (id, outcome) => {
|
|
59
|
+
outcome.then((data) => send({ type: 'result', id, ok: true, ...(data !== undefined ? { data } : {}) }), (error) => send({
|
|
60
|
+
type: 'result',
|
|
61
|
+
id,
|
|
62
|
+
ok: false,
|
|
63
|
+
error: error instanceof Error ? error.message : String(error),
|
|
64
|
+
}));
|
|
65
|
+
};
|
|
66
|
+
const subscribe = (ref) => {
|
|
67
|
+
const { integration: integrationId, id } = parseEntityRef(ref);
|
|
68
|
+
const integration = this.#integrations.get(integrationId);
|
|
69
|
+
if (!integration) {
|
|
70
|
+
this.#log(`[proxy] no integration "${integrationId}" for ${ref}`);
|
|
71
|
+
send({ type: 'entity', ref, entity: null });
|
|
72
|
+
return () => { };
|
|
73
|
+
}
|
|
74
|
+
const start = () => {
|
|
75
|
+
try {
|
|
76
|
+
return integration.subscribe(id, (entity) => send({ type: 'entity', ref, entity: entity ?? null }));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (!(error instanceof UnknownEntityError)) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
this.#log(`[proxy] ${error.message}`);
|
|
83
|
+
send({ type: 'entity', ref, entity: null });
|
|
84
|
+
return () => { };
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
if (integration.status === 'connected') {
|
|
88
|
+
return start();
|
|
89
|
+
}
|
|
90
|
+
let stop;
|
|
91
|
+
const off = integration.onStatusChange((status) => {
|
|
92
|
+
if (status === 'connected' && !stop) {
|
|
93
|
+
stop = start();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return () => {
|
|
97
|
+
off();
|
|
98
|
+
stop?.();
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
socket.on('message', (data) => {
|
|
102
|
+
const message = parseClientMessage(data.toString());
|
|
103
|
+
if (!message) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
switch (message.type) {
|
|
107
|
+
case 'subscribe':
|
|
108
|
+
if (!subscriptions.has(message.ref)) {
|
|
109
|
+
subscriptions.set(message.ref, subscribe(message.ref));
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
case 'unsubscribe':
|
|
113
|
+
subscriptions.get(message.ref)?.();
|
|
114
|
+
subscriptions.delete(message.ref);
|
|
115
|
+
return;
|
|
116
|
+
case 'command': {
|
|
117
|
+
const { integration: integrationId, id } = parseEntityRef(message.ref);
|
|
118
|
+
const integration = this.#integrations.get(integrationId);
|
|
119
|
+
answer(message.id, integration
|
|
120
|
+
? integration.command(id, message.command, message.args)
|
|
121
|
+
: Promise.reject(new Error(`Unknown integration "${integrationId}"`)));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
case 'query': {
|
|
125
|
+
const { integration: integrationId, id } = parseEntityRef(message.ref);
|
|
126
|
+
const integration = this.#integrations.get(integrationId);
|
|
127
|
+
const history = message.query === 'history' ? historyArgs(message.args) : undefined;
|
|
128
|
+
answer(message.id, !integration
|
|
129
|
+
? Promise.reject(new Error(`Unknown integration "${integrationId}"`))
|
|
130
|
+
: message.query === 'history'
|
|
131
|
+
? integration.history
|
|
132
|
+
? history
|
|
133
|
+
? integration.history(id, history)
|
|
134
|
+
: Promise.reject(new Error('A history query needs a range'))
|
|
135
|
+
: Promise.reject(new Error(`"${integrationId}" keeps no history`))
|
|
136
|
+
: integration.browse
|
|
137
|
+
? integration.browse(id, browseArgs(message.args))
|
|
138
|
+
: Promise.reject(new Error(`"${integrationId}" has no media library`)));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
case 'raw': {
|
|
142
|
+
const integration = this.#integrations.get(message.integration);
|
|
143
|
+
answer(message.id, !integration
|
|
144
|
+
? Promise.reject(new Error(`Unknown integration "${message.integration}"`))
|
|
145
|
+
: integration.callRaw
|
|
146
|
+
? integration.callRaw(message.request)
|
|
147
|
+
: Promise.reject(new Error(`"${message.integration}" has no raw requests`)));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const dispose = () => {
|
|
153
|
+
for (const off of subscriptions.values()) {
|
|
154
|
+
off();
|
|
155
|
+
}
|
|
156
|
+
subscriptions.clear();
|
|
157
|
+
for (const off of cleanups) {
|
|
158
|
+
off();
|
|
159
|
+
}
|
|
160
|
+
cleanups.length = 0;
|
|
161
|
+
};
|
|
162
|
+
socket.on('close', dispose);
|
|
163
|
+
socket.on('error', dispose);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../../src/server/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Server as HttpServer } from 'node:http';
|
|
2
|
+
import type { Proxy } from './proxy.ts';
|
|
3
|
+
export declare const WS_PATH = "/ws";
|
|
4
|
+
/** Attaches the proxy to `/ws` on an http server, leaving other upgrades (Vite HMR) alone. */
|
|
5
|
+
export declare function attachWebSocket(server: HttpServer, proxy: Proxy): () => void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
export const WS_PATH = '/ws';
|
|
3
|
+
/** Attaches the proxy to `/ws` on an http server, leaving other upgrades (Vite HMR) alone. */
|
|
4
|
+
export function attachWebSocket(server, proxy) {
|
|
5
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
6
|
+
const onUpgrade = (request, socket, head) => {
|
|
7
|
+
if (new URL(request.url ?? '/', 'http://localhost').pathname !== WS_PATH) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
wss.handleUpgrade(request, socket, head, (ws) => proxy.handleConnection(ws));
|
|
11
|
+
};
|
|
12
|
+
server.on('upgrade', onUpgrade);
|
|
13
|
+
return () => {
|
|
14
|
+
server.off('upgrade', onUpgrade);
|
|
15
|
+
wss.close();
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../../src/server/websocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAGrC,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,CAAC;AAE7B,8FAA8F;AAC9F,MAAM,UAAU,eAAe,CAAC,MAAkB,EAAE,KAAY;IAC9D,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,CAChB,OAA4C,EAC5C,MAAoC,EACpC,IAAY,EACZ,EAAE;QACF,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzE,OAAO;QACT,CAAC;QAED,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/E,CAAC,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAChC,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACjC,GAAG,CAAC,KAAK,EAAE,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sensible default, re-exported by the project's own `vite.config.ts` (the React Router plugin
|
|
3
|
+
* requires a config file at the project root). Server settings and the dev proxy are supplied
|
|
4
|
+
* inline by the CLI.
|
|
5
|
+
*
|
|
6
|
+
* A function, so every load of the config gets its own React Router plugin. The SPA build starts a
|
|
7
|
+
* preview server that loads this config again in the same process; a plugin created once at import
|
|
8
|
+
* would be shared, and the preview's `serve` would overwrite the build's state in it
|
|
9
|
+
* ("Expected build manifest").
|
|
10
|
+
*/
|
|
11
|
+
declare const _default: import("vite").UserConfigFnObject;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { reactRouter } from '@react-router/dev/vite';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
/**
|
|
4
|
+
* Sensible default, re-exported by the project's own `vite.config.ts` (the React Router plugin
|
|
5
|
+
* requires a config file at the project root). Server settings and the dev proxy are supplied
|
|
6
|
+
* inline by the CLI.
|
|
7
|
+
*
|
|
8
|
+
* A function, so every load of the config gets its own React Router plugin. The SPA build starts a
|
|
9
|
+
* preview server that loads this config again in the same process; a plugin created once at import
|
|
10
|
+
* would be shared, and the preview's `serve` would overwrite the build's state in it
|
|
11
|
+
* ("Expected build manifest").
|
|
12
|
+
*/
|
|
13
|
+
export default defineConfig(() => ({
|
|
14
|
+
plugins: [reactRouter()],
|
|
15
|
+
// React Router's SPA prerender starts a private preview server; pin it to IPv4 so it is
|
|
16
|
+
// reachable in containers where `localhost` resolves to ::1.
|
|
17
|
+
preview: { host: '127.0.0.1' },
|
|
18
|
+
resolve: {
|
|
19
|
+
// One copy of these across the runtime app and the instance's dashboards.
|
|
20
|
+
dedupe: ['react', 'react-dom', 'react-router', '@hashsome/core', '@hashsome/ui'],
|
|
21
|
+
},
|
|
22
|
+
optimizeDeps: {
|
|
23
|
+
// Vite's dependency scanner only follows *static* imports reachable from the HTML entry —
|
|
24
|
+
// route modules are loaded dynamically by React Router's own router, not a plain `import`
|
|
25
|
+
// statement it can see, so a dashboard's dependencies (e.g. the energy chart's `recharts`)
|
|
26
|
+
// only surface once that route is actually visited. Without this, navigating to a dashboard
|
|
27
|
+
// not yet visited in the current dev session triggers a fresh dependency pre-bundle mid-visit
|
|
28
|
+
// — the page's own module graph update races a React Router navigation already in flight, and
|
|
29
|
+
// the view can appear stuck (URL changed, nothing rendered) until the resulting full reload,
|
|
30
|
+
// sometimes several seconds later. Pointing the scanner at every route module upfront (plus
|
|
31
|
+
// `app/root.tsx`, which pulls in `HashsomeProvider`) lets it discover all of this at
|
|
32
|
+
// server startup instead.
|
|
33
|
+
entries: ['app/root.tsx', 'dashboards/**/*.{ts,tsx}'],
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-config.js","sourceRoot":"","sources":["../../src/vite-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAEpC;;;;;;;;;GASG;AACH,eAAe,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC;IACxB,wFAAwF;IACxF,6DAA6D;IAC7D,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;IAC9B,OAAO,EAAE;QACP,0EAA0E;QAC1E,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,CAAC;KACjF;IACD,YAAY,EAAE;QACZ,0FAA0F;QAC1F,0FAA0F;QAC1F,2FAA2F;QAC3F,4FAA4F;QAC5F,8FAA8F;QAC9F,8FAA8F;QAC9F,6FAA6F;QAC7F,4FAA4F;QAC5F,qFAAqF;QACrF,0BAA0B;QAC1B,OAAO,EAAE,CAAC,cAAc,EAAE,0BAA0B,CAAC;KACtD;CACF,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type InlineConfig } from 'vite';
|
|
2
|
+
import type { ResolvedConfig } from './config.ts';
|
|
3
|
+
import type { Proxy } from './server/proxy.ts';
|
|
4
|
+
/** `proxy` is only passed for `dev`; `build` needs no server settings (its prerender starts a private preview server). */
|
|
5
|
+
export declare function createViteConfig(config: ResolvedConfig, proxy?: Proxy): InlineConfig;
|
package/dist/src/vite.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { searchForWorkspaceRoot } from 'vite';
|
|
4
|
+
import { serveAsset } from './server/assets.js';
|
|
5
|
+
import { attachWebSocket } from './server/websocket.js';
|
|
6
|
+
const runtimeRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
/** Dev only: `/healthz`, the `/ws` proxy and the asset route on Vite's own server. */
|
|
8
|
+
function hashsomeDevServer(proxy, config) {
|
|
9
|
+
return {
|
|
10
|
+
name: 'hashsome:dev-server',
|
|
11
|
+
configureServer(server) {
|
|
12
|
+
server.middlewares.use('/healthz', (_req, res) => {
|
|
13
|
+
res.setHeader('content-type', 'application/json');
|
|
14
|
+
res.end('{"status":"ok"}');
|
|
15
|
+
});
|
|
16
|
+
server.middlewares.use((req, res, next) => {
|
|
17
|
+
serveAsset(config.integrations, req, res).then((served) => (served ? undefined : next()), next);
|
|
18
|
+
});
|
|
19
|
+
if (server.httpServer) {
|
|
20
|
+
attachWebSocket(server.httpServer, proxy);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** `proxy` is only passed for `dev`; `build` needs no server settings (its prerender starts a private preview server). */
|
|
26
|
+
export function createViteConfig(config, proxy) {
|
|
27
|
+
return {
|
|
28
|
+
root: config.root,
|
|
29
|
+
configFile: join(config.root, 'vite.config.ts'),
|
|
30
|
+
...(proxy
|
|
31
|
+
? {
|
|
32
|
+
plugins: [hashsomeDevServer(proxy, config)],
|
|
33
|
+
server: {
|
|
34
|
+
host: config.host,
|
|
35
|
+
port: config.port,
|
|
36
|
+
strictPort: true,
|
|
37
|
+
fs: { allow: [searchForWorkspaceRoot(config.root), runtimeRoot] },
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
: {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../../src/vite.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAkC,MAAM,MAAM,CAAC;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAExE,sFAAsF;AACtF,SAAS,iBAAiB,CAAC,KAAY,EAAE,MAAsB;IAC7D,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,eAAe,CAAC,MAAM;YACpB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;gBAC/C,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;gBAClD,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,UAAU,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAC5C,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EACzC,IAAI,CACL,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,eAAe,CAAC,MAAM,CAAC,UAAwB,EAAE,KAAK,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,0HAA0H;AAC1H,MAAM,UAAU,gBAAgB,CAAC,MAAsB,EAAE,KAAa;IACpE,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC;QAC/C,GAAG,CAAC,KAAK;YACP,CAAC,CAAC;gBACE,OAAO,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBAC3C,MAAM,EAAE;oBACN,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,UAAU,EAAE,IAAI;oBAChB,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,EAAE;iBAClE;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hashsome/runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Server + CLI that serves a project's own dashboards",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/src/index.d.ts",
|
|
9
|
+
"default": "./dist/src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./app": {
|
|
12
|
+
"types": "./dist/app/index.d.ts",
|
|
13
|
+
"default": "./dist/app/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./entry-client": {
|
|
16
|
+
"types": "./dist/app/entry.client.d.ts",
|
|
17
|
+
"default": "./dist/app/entry.client.js"
|
|
18
|
+
},
|
|
19
|
+
"./entry-server": {
|
|
20
|
+
"types": "./dist/app/entry.server.d.ts",
|
|
21
|
+
"default": "./dist/app/entry.server.js"
|
|
22
|
+
},
|
|
23
|
+
"./vite-config": {
|
|
24
|
+
"types": "./dist/src/vite-config.d.ts",
|
|
25
|
+
"default": "./dist/src/vite-config.js"
|
|
26
|
+
},
|
|
27
|
+
"./serve": {
|
|
28
|
+
"types": "./dist/src/serve.d.ts",
|
|
29
|
+
"default": "./dist/src/serve.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@testing-library/react": "16.3.3",
|
|
34
|
+
"@types/express": "5.0.6",
|
|
35
|
+
"@types/react": "19.3.0",
|
|
36
|
+
"@types/react-dom": "19.3.0",
|
|
37
|
+
"@types/ws": "8.18.1",
|
|
38
|
+
"jsdom": "30.1.1"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@hashsome/core": "0.1.0",
|
|
42
|
+
"@hashsome/ui": "0.1.0",
|
|
43
|
+
"@react-router/dev": "8.4.0",
|
|
44
|
+
"express": "5.2.1",
|
|
45
|
+
"react": "19.3.0",
|
|
46
|
+
"react-dom": "19.3.0",
|
|
47
|
+
"react-router": "8.4.0",
|
|
48
|
+
"vite": "8.3.1",
|
|
49
|
+
"ws": "8.21.3"
|
|
50
|
+
},
|
|
51
|
+
"bin": {
|
|
52
|
+
"hashsome": "./dist/bin/hashsome.js"
|
|
53
|
+
},
|
|
54
|
+
"license": "GPL-3.0-only",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/dstoyanoff/hashsome.git",
|
|
58
|
+
"directory": "packages/runtime"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/dstoyanoff/hashsome#readme",
|
|
61
|
+
"bugs": "https://github.com/dstoyanoff/hashsome/issues",
|
|
62
|
+
"files": [
|
|
63
|
+
"dist"
|
|
64
|
+
],
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public",
|
|
67
|
+
"provenance": true
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"build": "tsc -b tsconfig.build.json"
|
|
72
|
+
}
|
|
73
|
+
}
|