@objectstack/metadata 4.1.1 → 4.2.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/dist/index.cjs +194 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +195 -0
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +194 -0
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +195 -0
- package/dist/node.js.map +1 -1
- package/package.json +5 -5
package/dist/node.js
CHANGED
|
@@ -1,9 +1,162 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
2
6
|
var __export = (target, all) => {
|
|
3
7
|
for (var name in all)
|
|
4
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
9
|
};
|
|
6
10
|
|
|
11
|
+
// src/routes/hmr-routes.ts
|
|
12
|
+
var hmr_routes_exports = {};
|
|
13
|
+
__export(hmr_routes_exports, {
|
|
14
|
+
registerMetadataHmrRoutes: () => registerMetadataHmrRoutes
|
|
15
|
+
});
|
|
16
|
+
function registerMetadataHmrRoutes(app, manager, options = {}) {
|
|
17
|
+
const routePath = options.path ?? "/api/v1/dev/metadata-events";
|
|
18
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
19
|
+
const broadcast = (evt) => {
|
|
20
|
+
for (const l of listeners) {
|
|
21
|
+
try {
|
|
22
|
+
l(evt);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
let fsHookInstalled = false;
|
|
28
|
+
const installFsHooks = async () => {
|
|
29
|
+
if (fsHookInstalled) return;
|
|
30
|
+
const mgr = manager;
|
|
31
|
+
if (typeof mgr.subscribe !== "function") {
|
|
32
|
+
fsHookInstalled = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const types = await manager.getRegisteredTypes();
|
|
36
|
+
for (const type of types) {
|
|
37
|
+
mgr.subscribe(type, (evt) => {
|
|
38
|
+
const ts = typeof evt.timestamp === "string" ? Date.parse(evt.timestamp) : evt.timestamp ?? Date.now();
|
|
39
|
+
broadcast({
|
|
40
|
+
kind: "metadata-change",
|
|
41
|
+
type: evt.type ?? "changed",
|
|
42
|
+
metadataType: evt.metadataType ?? type,
|
|
43
|
+
name: evt.name ?? "",
|
|
44
|
+
path: evt.path,
|
|
45
|
+
timestamp: Number.isFinite(ts) ? ts : Date.now()
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
fsHookInstalled = true;
|
|
50
|
+
};
|
|
51
|
+
installFsHooks().catch(() => {
|
|
52
|
+
});
|
|
53
|
+
let onPostReload = null;
|
|
54
|
+
app.get(routePath, async (c) => {
|
|
55
|
+
await installFsHooks().catch(() => {
|
|
56
|
+
});
|
|
57
|
+
const types = await manager.getRegisteredTypes().catch(() => []);
|
|
58
|
+
const stream = new ReadableStream({
|
|
59
|
+
async start(controller) {
|
|
60
|
+
const enc = new TextEncoder();
|
|
61
|
+
let closed = false;
|
|
62
|
+
const safeEnqueue = (chunk) => {
|
|
63
|
+
if (closed) return;
|
|
64
|
+
try {
|
|
65
|
+
controller.enqueue(enc.encode(chunk));
|
|
66
|
+
} catch {
|
|
67
|
+
closed = true;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const listener = (evt) => {
|
|
71
|
+
if (closed) return;
|
|
72
|
+
const eventName = evt.kind === "reload" ? "reload" : "metadata-change";
|
|
73
|
+
safeEnqueue(`event: ${eventName}
|
|
74
|
+
data: ${JSON.stringify(evt)}
|
|
75
|
+
|
|
76
|
+
`);
|
|
77
|
+
};
|
|
78
|
+
listeners.add(listener);
|
|
79
|
+
safeEnqueue(`event: ready
|
|
80
|
+
data: ${JSON.stringify({ types, timestamp: Date.now() })}
|
|
81
|
+
|
|
82
|
+
`);
|
|
83
|
+
const heartbeat = setInterval(() => {
|
|
84
|
+
safeEnqueue(`: ping ${Date.now()}
|
|
85
|
+
|
|
86
|
+
`);
|
|
87
|
+
}, 15e3);
|
|
88
|
+
const cleanup = () => {
|
|
89
|
+
if (closed) return;
|
|
90
|
+
closed = true;
|
|
91
|
+
clearInterval(heartbeat);
|
|
92
|
+
listeners.delete(listener);
|
|
93
|
+
try {
|
|
94
|
+
controller.close();
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const signal = c.req?.raw?.signal;
|
|
99
|
+
if (signal) {
|
|
100
|
+
if (signal.aborted) cleanup();
|
|
101
|
+
else signal.addEventListener("abort", cleanup, { once: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return new Response(stream, {
|
|
106
|
+
status: 200,
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
109
|
+
"Cache-Control": "no-cache, no-transform",
|
|
110
|
+
"Connection": "keep-alive",
|
|
111
|
+
"X-Accel-Buffering": "no"
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
app.post(routePath, async (c) => {
|
|
116
|
+
let body = {};
|
|
117
|
+
try {
|
|
118
|
+
const ct = c.req?.header?.("content-type") ?? "";
|
|
119
|
+
if (typeof c.req?.json === "function" && ct.includes("json")) {
|
|
120
|
+
body = await c.req.json();
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
if (onPostReload) await onPostReload(body);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return new Response(
|
|
128
|
+
JSON.stringify({ ok: false, error: e?.message ?? "reload handler failed" }),
|
|
129
|
+
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const reason = body.reason ?? "manual-trigger";
|
|
133
|
+
broadcast({
|
|
134
|
+
kind: "reload",
|
|
135
|
+
reason,
|
|
136
|
+
changed: body.changed,
|
|
137
|
+
timestamp: Date.now()
|
|
138
|
+
});
|
|
139
|
+
return new Response(
|
|
140
|
+
JSON.stringify({ ok: true, listeners: listeners.size, reason }),
|
|
141
|
+
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
broadcastReload(reason, changed) {
|
|
146
|
+
broadcast({ kind: "reload", reason, changed, timestamp: Date.now() });
|
|
147
|
+
},
|
|
148
|
+
setOnPostReload(fn) {
|
|
149
|
+
onPostReload = fn;
|
|
150
|
+
},
|
|
151
|
+
listenerCount: () => listeners.size
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
var init_hmr_routes = __esm({
|
|
155
|
+
"src/routes/hmr-routes.ts"() {
|
|
156
|
+
"use strict";
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
7
160
|
// src/metadata-manager.ts
|
|
8
161
|
import { createLogger } from "@objectstack/core";
|
|
9
162
|
|
|
@@ -1773,6 +1926,21 @@ var _MetadataManager = class _MetadataManager {
|
|
|
1773
1926
|
unsubscribe: () => this.removeWatchCallback(type, wrappedCallback)
|
|
1774
1927
|
};
|
|
1775
1928
|
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Subscribe to raw metadata watch events for a given type.
|
|
1931
|
+
*
|
|
1932
|
+
* Unlike `watchService` (which maps to the IMetadataService contract and
|
|
1933
|
+
* drops fields like `path`/`timestamp`), this returns the raw
|
|
1934
|
+
* `MetadataWatchEvent` produced by the underlying watcher — useful for
|
|
1935
|
+
* developer-facing tooling such as the HMR SSE endpoint that wants the
|
|
1936
|
+
* source file path and original timestamp.
|
|
1937
|
+
*
|
|
1938
|
+
* @returns An unsubscribe function.
|
|
1939
|
+
*/
|
|
1940
|
+
subscribe(type, callback) {
|
|
1941
|
+
this.addWatchCallback(type, callback);
|
|
1942
|
+
return () => this.removeWatchCallback(type, callback);
|
|
1943
|
+
}
|
|
1776
1944
|
// ==========================================
|
|
1777
1945
|
// Import / Export
|
|
1778
1946
|
// ==========================================
|
|
@@ -2791,6 +2959,33 @@ var MetadataPlugin = class {
|
|
|
2791
2959
|
error: e.message
|
|
2792
2960
|
});
|
|
2793
2961
|
}
|
|
2962
|
+
try {
|
|
2963
|
+
const httpServer = ctx.getService("http-server") ?? ctx.getService("http.server");
|
|
2964
|
+
if (httpServer && typeof httpServer.getRawApp === "function") {
|
|
2965
|
+
const { registerMetadataHmrRoutes: registerMetadataHmrRoutes2 } = await Promise.resolve().then(() => (init_hmr_routes(), hmr_routes_exports));
|
|
2966
|
+
const hub = registerMetadataHmrRoutes2(httpServer.getRawApp(), this.manager);
|
|
2967
|
+
hub.setOnPostReload(async (body = {}) => {
|
|
2968
|
+
const src2 = this.options.artifactSource;
|
|
2969
|
+
if (src2?.mode === "local-file") {
|
|
2970
|
+
try {
|
|
2971
|
+
await this._loadFromLocalFile(ctx, src2.path, src2.fetchTimeoutMs);
|
|
2972
|
+
ctx.logger.info("[MetadataPlugin] artifact reloaded via HMR POST", {
|
|
2973
|
+
path: src2.path,
|
|
2974
|
+
reason: body?.reason
|
|
2975
|
+
});
|
|
2976
|
+
} catch (e) {
|
|
2977
|
+
ctx.logger.warn("[MetadataPlugin] artifact reload failed", { error: e?.message });
|
|
2978
|
+
throw e;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
});
|
|
2982
|
+
console.log("[MetadataPlugin] HMR endpoint registered at /api/v1/dev/metadata-events");
|
|
2983
|
+
} else {
|
|
2984
|
+
console.log("[MetadataPlugin] HTTP server with getRawApp() not available \u2014 skipping HMR endpoint");
|
|
2985
|
+
}
|
|
2986
|
+
} catch (e) {
|
|
2987
|
+
console.warn("[MetadataPlugin] Failed to register HMR endpoint", e?.message);
|
|
2988
|
+
}
|
|
2794
2989
|
};
|
|
2795
2990
|
this.options = {
|
|
2796
2991
|
watch: true,
|