@holo-js/adapter-shared 0.3.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,26 @@
1
+ import { NormalizedHoloHttpError } from '@holo-js/kernel/http-errors';
2
+ export { HoloHttpErrorStatus, NormalizedHoloHttpError, isHoloHttpErrorStatus, normalizeHoloHttpError } from '@holo-js/kernel/http-errors';
3
+
4
+ type ClientErrorPageOptions = {
5
+ readonly rootId: string;
6
+ readonly statusClassName: string;
7
+ };
8
+ declare function renderClientHttpErrorPage(error: NormalizedHoloHttpError, options: ClientErrorPageOptions): void;
9
+
10
+ type RealtimeDefinitionSourceMap = {
11
+ readonly version: 3;
12
+ readonly file: string;
13
+ readonly sources: readonly string[];
14
+ readonly sourcesContent: readonly string[];
15
+ readonly names: readonly string[];
16
+ readonly mappings: string;
17
+ };
18
+ type RealtimeDefinitionTransformResult = {
19
+ readonly code: string;
20
+ readonly map: RealtimeDefinitionSourceMap;
21
+ };
22
+ declare function stripRealtimeServerHandlers(source: string): string;
23
+ declare function createRealtimeClientDefinitionModule(source: string, importTarget: string): string;
24
+ declare function createRealtimeClientDefinitionTransform(source: string, importTarget: string): RealtimeDefinitionTransformResult;
25
+
26
+ export { type ClientErrorPageOptions, type RealtimeDefinitionSourceMap, type RealtimeDefinitionTransformResult, createRealtimeClientDefinitionModule, createRealtimeClientDefinitionTransform, renderClientHttpErrorPage, stripRealtimeServerHandlers };
package/dist/index.mjs ADDED
@@ -0,0 +1,188 @@
1
+ // src/index.ts
2
+ import ts from "typescript";
3
+ import {
4
+ isHoloHttpErrorStatus,
5
+ normalizeHoloHttpError
6
+ } from "@holo-js/kernel/http-errors";
7
+
8
+ // src/clientErrorPage.ts
9
+ function escapePage(error, options) {
10
+ const browserDocument = globalThis.document;
11
+ if (!browserDocument) return;
12
+ browserDocument.title = `${error.status}: ${error.message}`;
13
+ const existingRoot = browserDocument.getElementById(options.rootId);
14
+ const root = existingRoot ?? browserDocument.createElement("main");
15
+ root.id = options.rootId;
16
+ root.setAttribute("role", "alert");
17
+ root.style.cssText = "position:fixed;inset:0;z-index:2147483647;display:grid;place-items:center;margin:0;background:#fff;color:#000;font-family:Arial,Helvetica,sans-serif";
18
+ const wrapper = browserDocument.createElement("div");
19
+ wrapper.style.cssText = "display:flex;align-items:center;gap:1.5rem;padding:1.5rem";
20
+ const status = browserDocument.createElement("h1");
21
+ status.className = options.statusClassName;
22
+ status.textContent = String(error.status);
23
+ status.style.cssText = "display:inline-block;margin:0 1.25rem 0 0;padding:0 1.5rem 0 0;font-size:24px;font-weight:500;line-height:49px;vertical-align:top;border-right:1px solid rgba(0,0,0,.3)";
24
+ const description = browserDocument.createElement("div");
25
+ description.style.cssText = "display:inline-block;text-align:left;line-height:49px;height:49px;vertical-align:middle";
26
+ const message = browserDocument.createElement("h2");
27
+ message.textContent = error.message;
28
+ message.style.cssText = "font-size:14px;font-weight:400;line-height:49px;margin:0";
29
+ description.append(message);
30
+ wrapper.append(status, description);
31
+ root.replaceChildren(wrapper);
32
+ for (const child of Array.from(browserDocument.body.children)) {
33
+ if (child.id !== root.id) child.style.display = "none";
34
+ }
35
+ if (!existingRoot) browserDocument.body.append(root);
36
+ }
37
+ function renderClientHttpErrorPage(error, options) {
38
+ escapePage(error, options);
39
+ }
40
+
41
+ // src/index.ts
42
+ function propertyName(property) {
43
+ if (!("name" in property) || !property.name) return void 0;
44
+ return ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) ? property.name.text : void 0;
45
+ }
46
+ function collectRealtimeFactories(sourceFile) {
47
+ const factories = /* @__PURE__ */ new Map([
48
+ ["query", "query"],
49
+ ["mutation", "mutation"]
50
+ ]);
51
+ for (const statement of sourceFile.statements) {
52
+ if (!ts.isImportDeclaration(statement) || !statement.importClause?.namedBindings) continue;
53
+ if (!ts.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "@holo-js/realtime") continue;
54
+ if (!ts.isNamedImports(statement.importClause.namedBindings)) continue;
55
+ for (const element of statement.importClause.namedBindings.elements) {
56
+ const importedName = element.propertyName?.text ?? element.name.text;
57
+ if (importedName === "query" || importedName === "mutation") {
58
+ factories.set(element.name.text, importedName);
59
+ }
60
+ }
61
+ }
62
+ return factories;
63
+ }
64
+ function collectDefinitions(sourceFile) {
65
+ const definitions = [];
66
+ const factories = collectRealtimeFactories(sourceFile);
67
+ for (const statement of sourceFile.statements) {
68
+ if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue;
69
+ for (const declaration of statement.declarationList.declarations) {
70
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer || !ts.isCallExpression(declaration.initializer)) continue;
71
+ const factoryNode = declaration.initializer.expression;
72
+ if (!ts.isIdentifier(factoryNode)) continue;
73
+ const factory = factories.get(factoryNode.text);
74
+ if (!factory) continue;
75
+ const definitionNode = declaration.initializer.arguments[0];
76
+ if (!definitionNode || !ts.isObjectLiteralExpression(definitionNode)) continue;
77
+ const nameProperty = definitionNode.properties.find((property) => propertyName(property) === "name");
78
+ const name = nameProperty && ts.isPropertyAssignment(nameProperty) ? nameProperty.initializer.getText(sourceFile) : void 0;
79
+ const sourceLine = sourceFile.getLineAndCharacterOfPosition(declaration.getStart(sourceFile)).line;
80
+ definitions.push({ exportedName: declaration.name.text, factory, sourceLine, ...name ? { name } : {} });
81
+ }
82
+ }
83
+ return Object.freeze(definitions);
84
+ }
85
+ var base64VlqCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
86
+ function encodeVlq(value) {
87
+ let encoded = "";
88
+ let remaining = value < 0 ? (-value << 1) + 1 : value << 1;
89
+ do {
90
+ let digit = remaining & 31;
91
+ remaining >>>= 5;
92
+ if (remaining > 0) digit |= 32;
93
+ encoded += base64VlqCharacters[digit];
94
+ } while (remaining > 0);
95
+ return encoded;
96
+ }
97
+ function createSourceMap(source, sourceLines) {
98
+ let previousSourceLine = 0;
99
+ const mappings = sourceLines.map((sourceLine) => {
100
+ if (typeof sourceLine === "undefined") return "";
101
+ const segment = `${encodeVlq(0)}${encodeVlq(0)}${encodeVlq(sourceLine - previousSourceLine)}${encodeVlq(0)}`;
102
+ previousSourceLine = sourceLine;
103
+ return segment;
104
+ }).join(";");
105
+ return {
106
+ version: 3,
107
+ file: "realtime.client.ts",
108
+ sources: ["realtime.ts"],
109
+ sourcesContent: [source],
110
+ names: [],
111
+ mappings
112
+ };
113
+ }
114
+ function parseRealtimeSource(source) {
115
+ const result = ts.transpileModule(source, {
116
+ compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ESNext },
117
+ reportDiagnostics: true
118
+ });
119
+ const diagnostic = result.diagnostics?.find((entry) => entry.category === ts.DiagnosticCategory.Error);
120
+ if (diagnostic) {
121
+ throw new SyntaxError(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
122
+ }
123
+ return ts.createSourceFile("realtime.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
124
+ }
125
+ function stripRealtimeServerHandlers(source) {
126
+ const sourceFile = parseRealtimeSource(source);
127
+ const transformer = (context) => (root) => {
128
+ const visit = (node) => {
129
+ if (ts.isPropertyAssignment(node) && (propertyName(node) === "handler" || propertyName(node) === "authorize")) {
130
+ return ts.factory.updatePropertyAssignment(node, node.name, ts.factory.createIdentifier("undefined"));
131
+ }
132
+ if (ts.isMethodDeclaration(node) && (propertyName(node) === "handler" || propertyName(node) === "authorize")) {
133
+ return ts.factory.createPropertyAssignment(node.name, ts.factory.createIdentifier("undefined"));
134
+ }
135
+ return ts.visitEachChild(node, visit, context);
136
+ };
137
+ return ts.visitNode(root, visit);
138
+ };
139
+ const result = ts.transform(sourceFile, [transformer]);
140
+ try {
141
+ return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printFile(result.transformed[0]);
142
+ } finally {
143
+ result.dispose();
144
+ }
145
+ }
146
+ function createRealtimeClientDefinitionModule(source, importTarget) {
147
+ return createRealtimeClientDefinitionTransform(source, importTarget).code;
148
+ }
149
+ function createRealtimeClientDefinitionTransform(source, importTarget) {
150
+ const sourceFile = parseRealtimeSource(source);
151
+ const definitions = collectDefinitions(sourceFile);
152
+ if (definitions.length === 0) {
153
+ const code = stripRealtimeServerHandlers(source);
154
+ const sourceLineCount = source.split("\n").length;
155
+ return Object.freeze({
156
+ code,
157
+ map: createSourceMap(source, code.split("\n").map((_, index) => Math.min(index, sourceLineCount - 1)))
158
+ });
159
+ }
160
+ const factories = [...new Set(definitions.map((definition) => definition.factory))];
161
+ const outputLines = [
162
+ `import { ${factories.join(", ")} } from '${importTarget}'`,
163
+ ""
164
+ ];
165
+ const sourceLines = [void 0, void 0];
166
+ for (const definition of definitions) {
167
+ outputLines.push(`export const ${definition.exportedName} = ${definition.factory}({`);
168
+ sourceLines.push(definition.sourceLine);
169
+ if (definition.name) {
170
+ outputLines.push(` name: ${definition.name},`);
171
+ sourceLines.push(definition.sourceLine);
172
+ }
173
+ outputLines.push(" handler: undefined,", "})", "");
174
+ sourceLines.push(definition.sourceLine, definition.sourceLine, void 0);
175
+ }
176
+ return Object.freeze({
177
+ code: outputLines.join("\n"),
178
+ map: createSourceMap(source, sourceLines)
179
+ });
180
+ }
181
+ export {
182
+ createRealtimeClientDefinitionModule,
183
+ createRealtimeClientDefinitionTransform,
184
+ isHoloHttpErrorStatus,
185
+ normalizeHoloHttpError,
186
+ renderClientHttpErrorPage,
187
+ stripRealtimeServerHandlers
188
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@holo-js/adapter-shared",
3
+ "version": "0.3.0",
4
+ "description": "Holo-JS Framework - shared framework adapter infrastructure",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "default": "./dist/index.mjs"
12
+ }
13
+ },
14
+ "main": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "stub": "tsup",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
23
+ "test": "vitest --run"
24
+ },
25
+ "dependencies": {
26
+ "@holo-js/kernel": "^0.3.0",
27
+ "typescript": "^5.7.2"
28
+ },
29
+ "devDependencies": {
30
+ "tsup": "^8.3.5",
31
+ "vitest": "^4.1.5"
32
+ }
33
+ }