@holo-js/adapter-shared 0.3.28 → 0.4.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Holo-JS contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ 717ccaf9e57a434a91def4c1373ad81bf3ddf49e548dc31bfa883e0241e05136
package/dist/build.d.ts CHANGED
@@ -6,7 +6,7 @@ type RealtimeDefinitionSourceMap = {
6
6
  readonly version: 3;
7
7
  readonly file: string;
8
8
  readonly sources: readonly string[];
9
- readonly sourcesContent: readonly string[];
9
+ readonly sourcesContent?: readonly string[];
10
10
  readonly names: readonly string[];
11
11
  readonly mappings: string;
12
12
  };
@@ -17,8 +17,14 @@ type RealtimeDefinitionTransformResult = {
17
17
  type RealtimeDefinitionTransformOptions = {
18
18
  readonly preserveServerHandlers?: boolean;
19
19
  };
20
+ type RealtimeDefinitionTransformInput = RealtimeDefinitionTransformOptions & {
21
+ readonly source: string;
22
+ readonly importTarget: string;
23
+ readonly filePath: string;
24
+ };
20
25
  declare function stripRealtimeServerHandlers(source: string): string;
21
- declare function createRealtimeClientDefinitionModule(source: string, importTarget: string): string;
22
- declare function createRealtimeClientDefinitionTransform(source: string, importTarget: string, options?: RealtimeDefinitionTransformOptions): RealtimeDefinitionTransformResult;
26
+ declare function createRealtimeClientDefinitionModule(input: Omit<RealtimeDefinitionTransformInput, 'preserveServerHandlers'>): string;
27
+ declare function createRealtimeClientDefinitionTransform(input: RealtimeDefinitionTransformInput): RealtimeDefinitionTransformResult;
23
28
 
24
- export { type RealtimeDefinitionSourceMap, type RealtimeDefinitionTransformOptions, type RealtimeDefinitionTransformResult, compileBrowserValidation, compileNextRoute, createRealtimeClientDefinitionModule, createRealtimeClientDefinitionTransform, stripRealtimeServerHandlers };
29
+ export { compileBrowserValidation, compileNextRoute, createRealtimeClientDefinitionModule, createRealtimeClientDefinitionTransform, stripRealtimeServerHandlers };
30
+ export type { RealtimeDefinitionSourceMap, RealtimeDefinitionTransformInput, RealtimeDefinitionTransformOptions, RealtimeDefinitionTransformResult };
package/dist/build.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createRealtimeClientDefinitionModule,
5
5
  createRealtimeClientDefinitionTransform,
6
6
  stripRealtimeServerHandlers
7
- } from "./chunk-6FHRFQPC.mjs";
7
+ } from "./chunk-YDL2S5BT.mjs";
8
8
  export {
9
9
  compileBrowserValidation,
10
10
  compileNextRoute,
@@ -245,41 +245,200 @@ function propertyName(property) {
245
245
  if (!("name" in property) || !property.name) return void 0;
246
246
  return ts3.isIdentifier(property.name) || ts3.isStringLiteral(property.name) ? property.name.text : void 0;
247
247
  }
248
- function collectRealtimeFactories(sourceFile) {
248
+ var realtimeFactoryExports = /* @__PURE__ */ new Map([
249
+ ["query", "query"],
250
+ ["mutation", "mutation"],
251
+ ["defineRealtimeQuery", "query"],
252
+ ["defineRealtimeMutation", "mutation"]
253
+ ]);
254
+ function referenceRoot(expression) {
255
+ let current = unwrapExpression(expression);
256
+ while (ts3.isPropertyAccessExpression(current) || ts3.isElementAccessExpression(current)) {
257
+ current = unwrapExpression(current.expression);
258
+ }
259
+ return ts3.isIdentifier(current) ? current : void 0;
260
+ }
261
+ function boundNames(name) {
262
+ if (ts3.isIdentifier(name)) return [name.text];
263
+ return name.elements.flatMap((element) => ts3.isBindingElement(element) ? boundNames(element.name) : []);
264
+ }
265
+ function collectRealtimeBindings(sourceFile, importTarget) {
249
266
  const factories = /* @__PURE__ */ new Map([
250
267
  ["query", "query"],
251
268
  ["mutation", "mutation"]
252
269
  ]);
270
+ const namespaces = /* @__PURE__ */ new Set();
271
+ const aliases = /* @__PURE__ */ new Set();
253
272
  for (const statement of sourceFile.statements) {
254
- if (!ts3.isImportDeclaration(statement) || !statement.importClause?.namedBindings) continue;
255
- if (!ts3.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "@holo-js/realtime") continue;
256
- if (!ts3.isNamedImports(statement.importClause.namedBindings)) continue;
257
- for (const element of statement.importClause.namedBindings.elements) {
273
+ if (!ts3.isImportDeclaration(statement) || !statement.importClause || statement.importClause.isTypeOnly) continue;
274
+ if (!ts3.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "@holo-js/realtime" && statement.moduleSpecifier.text !== importTarget) continue;
275
+ const { name, namedBindings } = statement.importClause;
276
+ if (name) namespaces.add(name.text);
277
+ if (namedBindings && ts3.isNamespaceImport(namedBindings)) namespaces.add(namedBindings.name.text);
278
+ if (!namedBindings || !ts3.isNamedImports(namedBindings)) continue;
279
+ for (const element of namedBindings.elements) {
280
+ if (element.isTypeOnly) continue;
258
281
  const importedName = element.propertyName?.text ?? element.name.text;
259
- if (importedName === "query" || importedName === "mutation") {
260
- factories.set(element.name.text, importedName);
282
+ if (importedName === "default") {
283
+ namespaces.add(element.name.text);
284
+ continue;
261
285
  }
286
+ const factory = realtimeFactoryExports.get(importedName);
287
+ if (factory) factories.set(element.name.text, factory);
262
288
  }
263
289
  }
264
- return factories;
290
+ for (const statement of sourceFile.statements) {
291
+ if (!ts3.isVariableStatement(statement)) continue;
292
+ for (const declaration of statement.declarationList.declarations) {
293
+ const root = declaration.initializer && referenceRoot(declaration.initializer);
294
+ if (root && (factories.has(root.text) || namespaces.has(root.text) || aliases.has(root.text))) {
295
+ for (const name of boundNames(declaration.name)) aliases.add(name);
296
+ }
297
+ }
298
+ }
299
+ return { factories, namespaces, aliases };
265
300
  }
266
- function collectDefinitions(sourceFile) {
267
- const definitions = [];
268
- const factories = collectRealtimeFactories(sourceFile);
301
+ function namespaceMember(callee, bindings) {
302
+ if (!ts3.isPropertyAccessExpression(callee) && !ts3.isElementAccessExpression(callee)) return void 0;
303
+ const namespace = unwrapExpression(callee.expression);
304
+ if (!ts3.isIdentifier(namespace) || !bindings.namespaces.has(namespace.text)) return void 0;
305
+ if (ts3.isPropertyAccessExpression(callee)) return callee.name.text;
306
+ return ts3.isStringLiteralLike(callee.argumentExpression) ? callee.argumentExpression.text : void 0;
307
+ }
308
+ function resolveFactory(wrappedCallee, bindings) {
309
+ const callee = unwrapExpression(wrappedCallee);
310
+ if (ts3.isIdentifier(callee)) return bindings.factories.get(callee.text);
311
+ const member = namespaceMember(callee, bindings);
312
+ return member === void 0 ? void 0 : realtimeFactoryExports.get(member);
313
+ }
314
+ function unwrapExpression(expression) {
315
+ let current = expression;
316
+ while (ts3.isParenthesizedExpression(current) || ts3.isAsExpression(current) || ts3.isSatisfiesExpression(current) || ts3.isTypeAssertionExpression(current) || ts3.isNonNullExpression(current)) {
317
+ current = current.expression;
318
+ }
319
+ return current;
320
+ }
321
+ function containsFactoryCall(node, bindings) {
322
+ if (ts3.isCallExpression(node) && resolveFactory(node.expression, bindings)) return true;
323
+ return ts3.forEachChild(node, (child) => containsFactoryCall(child, bindings) || void 0) ?? false;
324
+ }
325
+ function containsRealtimeReference(node, bindings) {
326
+ return containsFactoryCall(node, bindings) || referencesName(node, /* @__PURE__ */ new Set([...bindings.namespaces, ...bindings.aliases]));
327
+ }
328
+ function collectStringConstants(sourceFile) {
329
+ const constants = /* @__PURE__ */ new Map();
269
330
  for (const statement of sourceFile.statements) {
270
- if (!ts3.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword)) continue;
331
+ if (!ts3.isVariableStatement(statement) || !(statement.declarationList.flags & ts3.NodeFlags.Const) || statement.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.DeclareKeyword)) continue;
271
332
  for (const declaration of statement.declarationList.declarations) {
272
- if (!ts3.isIdentifier(declaration.name) || !declaration.initializer || !ts3.isCallExpression(declaration.initializer)) continue;
273
- const factoryNode = declaration.initializer.expression;
274
- if (!ts3.isIdentifier(factoryNode)) continue;
275
- const factory = factories.get(factoryNode.text);
276
- if (!factory) continue;
277
- const definitionNode = declaration.initializer.arguments[0];
278
- if (!definitionNode || !ts3.isObjectLiteralExpression(definitionNode)) continue;
279
- const nameProperty = definitionNode.properties.find((property) => propertyName(property) === "name");
280
- const name = nameProperty && ts3.isPropertyAssignment(nameProperty) ? nameProperty.initializer.getText(sourceFile) : void 0;
281
- const sourceLine = sourceFile.getLineAndCharacterOfPosition(declaration.getStart(sourceFile)).line;
282
- definitions.push({ exportedName: declaration.name.text, factory, sourceLine, ...name ? { name } : {} });
333
+ if (ts3.isIdentifier(declaration.name) && declaration.initializer && ts3.isStringLiteralLike(declaration.initializer)) {
334
+ constants.set(declaration.name.text, declaration.initializer);
335
+ }
336
+ }
337
+ }
338
+ return constants;
339
+ }
340
+ function definitionKey(property) {
341
+ if (!property.name) return void 0;
342
+ if (ts3.isComputedPropertyName(property.name)) {
343
+ const expression = unwrapExpression(property.name.expression);
344
+ return ts3.isStringLiteralLike(expression) ? expression.text : void 0;
345
+ }
346
+ return ts3.isIdentifier(property.name) || ts3.isStringLiteral(property.name) || ts3.isNumericLiteral(property.name) ? property.name.text : void 0;
347
+ }
348
+ function definitionNameValue(property, constants) {
349
+ const value = ts3.isShorthandPropertyAssignment(property) ? property.name : ts3.isPropertyAssignment(property) ? unwrapExpression(property.initializer) : void 0;
350
+ const literal2 = value && (ts3.isStringLiteralLike(value) ? value : ts3.isIdentifier(value) ? constants.get(value.text) : void 0);
351
+ return literal2 ? { known: true, literal: literal2 } : { known: false };
352
+ }
353
+ function resolveDefinitionName(definitionNode, constants) {
354
+ let name = { known: true };
355
+ for (const property of definitionNode.properties) {
356
+ if (ts3.isSpreadAssignment(property)) {
357
+ name = { known: false };
358
+ continue;
359
+ }
360
+ const key = definitionKey(property);
361
+ if (key === "name") {
362
+ name = definitionNameValue(property, constants);
363
+ } else if (key === void 0 && property.name && ts3.isComputedPropertyName(property.name)) {
364
+ name = { known: false };
365
+ }
366
+ }
367
+ return name;
368
+ }
369
+ function referencesName(node, names) {
370
+ if (ts3.isIdentifier(node) && names.has(node.text)) {
371
+ const parent = node.parent;
372
+ const isPropertyName = ts3.isPropertyAccessExpression(parent) && parent.name === node || (ts3.isPropertyAssignment(parent) || ts3.isMethodDeclaration(parent) || ts3.isPropertyDeclaration(parent) || ts3.isGetAccessorDeclaration(parent) || ts3.isSetAccessorDeclaration(parent)) && parent.name === node;
373
+ if (!isPropertyName) return true;
374
+ }
375
+ return ts3.forEachChild(node, (child) => referencesName(child, names) || void 0) ?? false;
376
+ }
377
+ function collectDefinitionLocals(sourceFile, bindings) {
378
+ const locals = /* @__PURE__ */ new Set();
379
+ const declarations = sourceFile.statements.filter(ts3.isVariableStatement).flatMap((statement) => [...statement.declarationList.declarations]);
380
+ let changed = true;
381
+ while (changed) {
382
+ changed = false;
383
+ for (const declaration of declarations) {
384
+ if (!declaration.initializer) continue;
385
+ if (!containsRealtimeReference(declaration.initializer, bindings) && !referencesName(declaration.initializer, locals)) continue;
386
+ for (const name of boundNames(declaration.name)) {
387
+ if (!locals.has(name)) {
388
+ locals.add(name);
389
+ changed = true;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ return locals;
395
+ }
396
+ function unreducibleDefinitionError(filePath, exportedName) {
397
+ return new Error(`${filePath} exports ${exportedName} through a query or mutation call that cannot be reduced to a browser definition. Export each definition directly, as export const name = query({ name: 'posts.list', ... }) or export default query({ ... }).`);
398
+ }
399
+ function collectDefinitions(sourceFile, filePath, importTarget) {
400
+ const definitions = [];
401
+ const bindings = collectRealtimeBindings(sourceFile, importTarget);
402
+ const constants = collectStringConstants(sourceFile);
403
+ const definitionLocals = collectDefinitionLocals(sourceFile, bindings);
404
+ const recordDefinition = (exportedName, expression, node) => {
405
+ const call = unwrapExpression(expression);
406
+ const factory = ts3.isCallExpression(call) ? resolveFactory(call.expression, bindings) : void 0;
407
+ if (!ts3.isCallExpression(call) || !factory) {
408
+ if (containsRealtimeReference(expression, bindings) || referencesName(expression, definitionLocals)) {
409
+ throw unreducibleDefinitionError(filePath, exportedName);
410
+ }
411
+ return;
412
+ }
413
+ const definitionNode = call.arguments[0];
414
+ if (!definitionNode || !ts3.isObjectLiteralExpression(definitionNode)) throw unreducibleDefinitionError(filePath, exportedName);
415
+ const name = resolveDefinitionName(definitionNode, constants);
416
+ if (!name.known) {
417
+ throw new Error(`${filePath} defines ${exportedName} with a name that is not a string literal, so it cannot be sent to the browser.`);
418
+ }
419
+ const sourceLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line;
420
+ definitions.push({ exportedName, factory, sourceLine, ...name.literal ? { name: name.literal.getText(sourceFile) } : {} });
421
+ };
422
+ for (const statement of sourceFile.statements) {
423
+ if (ts3.isVariableStatement(statement)) {
424
+ if (!statement.modifiers?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword)) continue;
425
+ for (const declaration of statement.declarationList.declarations) {
426
+ if (!declaration.initializer) continue;
427
+ if (!ts3.isIdentifier(declaration.name)) {
428
+ if (boundNames(declaration.name).some((name) => definitionLocals.has(name))) {
429
+ throw unreducibleDefinitionError(filePath, declaration.name.getText(sourceFile));
430
+ }
431
+ continue;
432
+ }
433
+ recordDefinition(declaration.name.text, declaration.initializer, declaration);
434
+ }
435
+ } else if (ts3.isExportAssignment(statement) && !statement.isExportEquals) {
436
+ recordDefinition("default", statement.expression, statement);
437
+ } else if (ts3.isExportDeclaration(statement) && !statement.moduleSpecifier && !statement.isTypeOnly && statement.exportClause && ts3.isNamedExports(statement.exportClause)) {
438
+ for (const element of statement.exportClause.elements) {
439
+ const localName = element.propertyName?.text ?? element.name.text;
440
+ if (!element.isTypeOnly && definitionLocals.has(localName)) throw unreducibleDefinitionError(filePath, element.name.text);
441
+ }
283
442
  }
284
443
  }
285
444
  return Object.freeze(definitions);
@@ -296,7 +455,7 @@ function encodeVlq(value) {
296
455
  } while (remaining > 0);
297
456
  return encoded;
298
457
  }
299
- function createSourceMap(source, sourceLines) {
458
+ function createSourceMap(sourceLines, sourcesContent) {
300
459
  let previousSourceLine = 0;
301
460
  const mappings = sourceLines.map((sourceLine) => {
302
461
  if (typeof sourceLine === "undefined") return "";
@@ -308,7 +467,7 @@ function createSourceMap(source, sourceLines) {
308
467
  version: 3,
309
468
  file: "realtime.client.ts",
310
469
  sources: ["realtime.ts"],
311
- sourcesContent: [source],
470
+ ...sourcesContent ? { sourcesContent } : {},
312
471
  names: [],
313
472
  mappings
314
473
  };
@@ -369,36 +528,44 @@ function stripRealtimeServerHandlers(source) {
369
528
  result.dispose();
370
529
  }
371
530
  }
372
- function createRealtimeClientDefinitionModule(source, importTarget) {
373
- return createRealtimeClientDefinitionTransform(source, importTarget).code;
531
+ function createRealtimeClientDefinitionModule(input) {
532
+ return createRealtimeClientDefinitionTransform(input).code;
374
533
  }
375
- function createRealtimeClientDefinitionTransform(source, importTarget, options = {}) {
376
- if (options.preserveServerHandlers) {
534
+ function createRealtimeClientDefinitionTransform(input) {
535
+ if (typeof input !== "object" || input === null || typeof input.source !== "string" || typeof input.importTarget !== "string" || typeof input.filePath !== "string") {
536
+ throw new TypeError("createRealtimeClientDefinitionTransform takes one object with source, importTarget, and filePath strings, and an optional preserveServerHandlers flag.");
537
+ }
538
+ const { source, importTarget, filePath } = input;
539
+ if (input.preserveServerHandlers) {
377
540
  const code = retargetRealtimeImports(source, importTarget);
378
541
  const sourceLineCount = source.split("\n").length;
379
542
  return Object.freeze({
380
543
  code,
381
- map: createSourceMap(source, code.split("\n").map((_, index) => Math.min(index, sourceLineCount - 1)))
544
+ map: createSourceMap(code.split("\n").map((_, index) => Math.min(index, sourceLineCount - 1)), [source])
382
545
  });
383
546
  }
384
547
  const sourceFile = parseRealtimeSource(source);
385
- const definitions = collectDefinitions(sourceFile);
548
+ const definitions = collectDefinitions(sourceFile, filePath, importTarget);
386
549
  if (definitions.length === 0) {
387
- const code = stripRealtimeServerHandlers(source);
388
- const sourceLineCount = source.split("\n").length;
389
- return Object.freeze({
390
- code,
391
- map: createSourceMap(source, code.split("\n").map((_, index) => Math.min(index, sourceLineCount - 1)))
392
- });
550
+ throw new Error(`${filePath} has no query or mutation definitions, so it cannot be sent to the browser. Keep server-only code out of server/realtime, or move it out of that folder.`);
551
+ }
552
+ const exportedNames = new Set(definitions.map((definition) => definition.exportedName));
553
+ const factoryLocals = /* @__PURE__ */ new Map();
554
+ for (const definition of definitions) {
555
+ if (factoryLocals.has(definition.factory)) continue;
556
+ let local = definition.factory;
557
+ while (exportedNames.has(local)) local = `_${local}`;
558
+ factoryLocals.set(definition.factory, local);
393
559
  }
394
- const factories = [...new Set(definitions.map((definition) => definition.factory))];
560
+ const importSpecifiers = [...factoryLocals].map(([factory, local]) => factory === local ? factory : `${factory} as ${local}`);
395
561
  const outputLines = [
396
- `import { ${factories.join(", ")} } from '${importTarget}'`,
562
+ `import { ${importSpecifiers.join(", ")} } from '${importTarget}'`,
397
563
  ""
398
564
  ];
399
565
  const sourceLines = [void 0, void 0];
400
566
  for (const definition of definitions) {
401
- outputLines.push(`export const ${definition.exportedName} = ${definition.factory}({`);
567
+ const factory = factoryLocals.get(definition.factory);
568
+ outputLines.push(definition.exportedName === "default" ? `export default ${factory}({` : `export const ${definition.exportedName} = ${factory}({`);
402
569
  sourceLines.push(definition.sourceLine);
403
570
  if (definition.name) {
404
571
  outputLines.push(` name: ${definition.name},`);
@@ -409,7 +576,7 @@ function createRealtimeClientDefinitionTransform(source, importTarget, options =
409
576
  }
410
577
  return Object.freeze({
411
578
  code: outputLines.join("\n"),
412
- map: createSourceMap(source, sourceLines)
579
+ map: createSourceMap(sourceLines)
413
580
  });
414
581
  }
415
582
 
package/dist/client.d.ts CHANGED
@@ -7,4 +7,5 @@ type ClientErrorPageOptions = {
7
7
  };
8
8
  declare function renderClientHttpErrorPage(error: NormalizedHoloHttpError, options: ClientErrorPageOptions): void;
9
9
 
10
- export { type ClientErrorPageOptions, renderClientHttpErrorPage };
10
+ export { renderClientHttpErrorPage };
11
+ export type { ClientErrorPageOptions };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { HoloHttpErrorStatus, NormalizedHoloHttpError, isHoloHttpErrorStatus, normalizeHoloHttpError } from '@holo-js/kernel/http-errors';
2
2
  export { ClientErrorPageOptions, renderClientHttpErrorPage } from './client.js';
3
- export { RealtimeDefinitionSourceMap, RealtimeDefinitionTransformOptions, RealtimeDefinitionTransformResult, compileBrowserValidation, compileNextRoute, createRealtimeClientDefinitionModule, createRealtimeClientDefinitionTransform, stripRealtimeServerHandlers } from './build.js';
3
+ export { RealtimeDefinitionSourceMap, RealtimeDefinitionTransformInput, RealtimeDefinitionTransformOptions, RealtimeDefinitionTransformResult, compileBrowserValidation, compileNextRoute, createRealtimeClientDefinitionModule, createRealtimeClientDefinitionTransform, stripRealtimeServerHandlers } from './build.js';
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  createRealtimeClientDefinitionModule,
10
10
  createRealtimeClientDefinitionTransform,
11
11
  stripRealtimeServerHandlers
12
- } from "./chunk-6FHRFQPC.mjs";
12
+ } from "./chunk-YDL2S5BT.mjs";
13
13
  export {
14
14
  compileBrowserValidation,
15
15
  compileNextRoute,
@@ -11,4 +11,5 @@ type NextRequestLike = {
11
11
  declare function getCurrentNextRequest(): NextRequestLike | undefined;
12
12
  declare function runWithNextRequest<TValue>(request: NextRequestLike, callback: () => TValue): TValue;
13
13
 
14
- export { type NextRequestLike, getCurrentNextRequest, runWithNextRequest };
14
+ export { getCurrentNextRequest, runWithNextRequest };
15
+ export type { NextRequestLike };
@@ -0,0 +1,5 @@
1
+ declare function assertRealtimeDefinitionTree(rootDir: string): void;
2
+ declare function resolveRealtimeDefinitionDirectories(rootDir: string): readonly string[];
3
+ declare function isInRealtimeDefinitionDirectory(directories: readonly string[], id: string): boolean;
4
+
5
+ export { assertRealtimeDefinitionTree, isInRealtimeDefinitionDirectory, resolveRealtimeDefinitionDirectories };
@@ -0,0 +1,57 @@
1
+ // src/realtime-directories.ts
2
+ import { existsSync, readdirSync, realpathSync, statSync } from "fs";
3
+ import { join, posix, relative, resolve, win32 } from "path";
4
+ function normalizeDirectory(path) {
5
+ return path.replaceAll("\\", "/").replace(/\/+$/, "");
6
+ }
7
+ function resolveConfiguredRoot(rootDir) {
8
+ return posix.isAbsolute(rootDir) || win32.isAbsolute(rootDir) ? rootDir : resolve(rootDir);
9
+ }
10
+ function symlinkTargetStaysInTree(linkPath, tree) {
11
+ if (!existsSync(linkPath)) return false;
12
+ const target = realpathSync.native(linkPath);
13
+ return statSync(target).isDirectory() && normalizeDirectory(target).startsWith(`${normalizeDirectory(tree)}/`);
14
+ }
15
+ function symlinkError(tree, linkPath) {
16
+ const displayPath = `server/realtime/${normalizeDirectory(relative(tree, linkPath))}`;
17
+ const replacement = existsSync(linkPath) && statSync(linkPath).isDirectory() ? "folder" : "file";
18
+ return new Error(`${displayPath} is a symbolic link; Holo can't guarantee its server code stays out of the browser bundle. Replace it with a regular ${replacement}.`);
19
+ }
20
+ function assertTreeEntries(tree, directory) {
21
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
22
+ const path = join(directory, entry.name);
23
+ if (entry.isSymbolicLink()) {
24
+ if (!symlinkTargetStaysInTree(path, tree)) throw symlinkError(tree, path);
25
+ } else if (entry.isDirectory()) {
26
+ assertTreeEntries(tree, path);
27
+ }
28
+ }
29
+ }
30
+ function assertRealtimeDefinitionTree(rootDir) {
31
+ const configured = join(resolveConfiguredRoot(rootDir), "server", "realtime");
32
+ if (!existsSync(configured)) return;
33
+ const tree = realpathSync.native(configured);
34
+ assertTreeEntries(tree, tree);
35
+ }
36
+ function resolveRealtimeDefinitionDirectories(rootDir) {
37
+ const configuredRoot = resolveConfiguredRoot(rootDir);
38
+ const configured = join(configuredRoot, "server", "realtime");
39
+ assertRealtimeDefinitionTree(rootDir);
40
+ const directories = /* @__PURE__ */ new Set([`${normalizeDirectory(configuredRoot)}/server/realtime`]);
41
+ if (existsSync(configuredRoot)) {
42
+ directories.add(`${normalizeDirectory(realpathSync.native(configuredRoot))}/server/realtime`);
43
+ }
44
+ if (existsSync(configured)) {
45
+ directories.add(normalizeDirectory(realpathSync.native(configured)));
46
+ }
47
+ return [...directories];
48
+ }
49
+ function isInRealtimeDefinitionDirectory(directories, id) {
50
+ const path = id.split("?")[0].replaceAll("\\", "/");
51
+ return directories.some((directory) => path.startsWith(`${directory}/`));
52
+ }
53
+ export {
54
+ assertRealtimeDefinitionTree,
55
+ isInRealtimeDefinitionDirectory,
56
+ resolveRealtimeDefinitionDirectories
57
+ };
@@ -10,6 +10,19 @@ type RateLimitFailure = {
10
10
  readonly values: Readonly<Record<string, never>>;
11
11
  readonly retryAfterSeconds?: number;
12
12
  };
13
+ type RequestBodyTooLargeFailure = {
14
+ readonly ok: false;
15
+ readonly valid: false;
16
+ readonly status: 413;
17
+ readonly bag: 'default';
18
+ readonly message: string;
19
+ readonly errors: {
20
+ readonly _root: readonly string[];
21
+ };
22
+ readonly values: Readonly<Record<string, never>>;
23
+ readonly retryAfterSeconds?: undefined;
24
+ };
25
+ declare function serializeRequestBodyTooLargeError(error: unknown): RequestBodyTooLargeFailure | undefined;
13
26
  declare function serializeRateLimitError(error: unknown): RateLimitFailure | undefined;
14
27
 
15
- export { serializeRateLimitError };
28
+ export { serializeRateLimitError, serializeRequestBodyTooLargeError };
@@ -1,5 +1,20 @@
1
1
  // src/server-errors.ts
2
- import { normalizeHoloHttpError } from "@holo-js/kernel/http-errors";
2
+ import { HoloRequestBodyTooLargeError, normalizeHoloHttpError } from "@holo-js/kernel";
3
+ function serializeRequestBodyTooLargeError(error) {
4
+ if (!(error instanceof HoloRequestBodyTooLargeError) && !(error instanceof Error && error.name === "HoloRequestBodyTooLargeError")) {
5
+ const cause = error && typeof error === "object" && "cause" in error ? error.cause : void 0;
6
+ return cause === void 0 ? void 0 : serializeRequestBodyTooLargeError(cause);
7
+ }
8
+ return {
9
+ ok: false,
10
+ valid: false,
11
+ status: 413,
12
+ bag: "default",
13
+ message: error.message,
14
+ errors: { _root: [error.message] },
15
+ values: {}
16
+ };
17
+ }
3
18
  function serializeRateLimitError(error) {
4
19
  const normalized = normalizeHoloHttpError(error);
5
20
  if (!normalized || normalized.status !== 429) return void 0;
@@ -20,5 +35,6 @@ function serializeRateLimitError(error) {
20
35
  };
21
36
  }
22
37
  export {
23
- serializeRateLimitError
38
+ serializeRateLimitError,
39
+ serializeRequestBodyTooLargeError
24
40
  };
@@ -3,11 +3,15 @@ type SvelteKitRequestEvent = {
3
3
  readonly cookies: {
4
4
  get(name: string): string | undefined;
5
5
  set(name: string, value: string, options: SvelteKitCookieOptions): void;
6
+ delete(name: string, options: {
7
+ readonly path: string;
8
+ }): void;
6
9
  };
7
10
  readonly request: {
8
11
  readonly method?: string;
9
12
  readonly headers: Headers;
10
13
  };
14
+ readonly getClientAddress?: () => string;
11
15
  };
12
16
  type SvelteKitCookieOptions = {
13
17
  path: string;
@@ -22,4 +26,5 @@ type SvelteKitCookieOptions = {
22
26
  declare function getCurrentSvelteKitRequestEvent(): SvelteKitRequestEvent | undefined;
23
27
  declare function runWithSvelteKitRequestEvent<TValue>(event: SvelteKitRequestEvent, callback: () => TValue): TValue;
24
28
 
25
- export { type SvelteKitRequestEvent, getCurrentSvelteKitRequestEvent, runWithSvelteKitRequestEvent };
29
+ export { getCurrentSvelteKitRequestEvent, runWithSvelteKitRequestEvent };
30
+ export type { SvelteKitRequestEvent };
package/package.json CHANGED
@@ -1,10 +1,20 @@
1
1
  {
2
2
  "name": "@holo-js/adapter-shared",
3
- "version": "0.3.28",
3
+ "version": "0.4.1",
4
4
  "description": "Holo-JS Framework - shared framework adapter infrastructure",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cobraprojects/holo-js.git",
10
+ "directory": "packages/adapter-shared"
11
+ },
7
12
  "exports": {
13
+ "./realtime-directories": {
14
+ "types": "./dist/realtime-directories.d.ts",
15
+ "import": "./dist/realtime-directories.mjs",
16
+ "default": "./dist/realtime-directories.mjs"
17
+ },
8
18
  "./server-errors": {
9
19
  "types": "./dist/server-errors.d.ts",
10
20
  "import": "./dist/server-errors.mjs",
@@ -42,7 +52,8 @@
42
52
  "main": "./dist/index.mjs",
43
53
  "types": "./dist/index.d.ts",
44
54
  "files": [
45
- "dist"
55
+ "dist",
56
+ "LICENSE"
46
57
  ],
47
58
  "scripts": {
48
59
  "build": "tsup",
@@ -51,14 +62,14 @@
51
62
  "test": "vitest --run"
52
63
  },
53
64
  "dependencies": {
54
- "@holo-js/kernel": "^0.3.28",
55
- "typescript": "^5.7.2"
65
+ "@holo-js/kernel": "^0.4.1",
66
+ "typescript": "^6.0.3"
56
67
  },
57
68
  "devDependencies": {
58
- "tsup": "^8.3.5",
59
- "vitest": "^4.1.5"
69
+ "tsup": "^9.0.0",
70
+ "vitest": "^5.0.5"
60
71
  },
61
72
  "engines": {
62
- "node": "^22.12.0 || ^24.0.0 || ^26.0.0"
73
+ "node": "^22.18.0 || ^24.0.0 || ^26.0.0"
63
74
  }
64
75
  }