@mittwald/flow-remote-core 1.3.0-next.9 → 1.3.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.
@@ -58,8 +58,23 @@ var FlowThreadSerialization = class extends ThreadSerializationStructuredClone {
58
58
  }
59
59
  });
60
60
  }
61
+ /**
62
+ * Values the base serializer hands to `postMessage` untouched, because
63
+ * structured clone carries them itself.
64
+ *
65
+ * Everything not listed here reaches the object fallback below and is spread
66
+ * into a plain object — which keeps only own enumerable properties. For a
67
+ * built-in that holds its state internally that is total loss and completely
68
+ * silent: a `Date` arrived as `{}`, a `RegExp` and an `Error` the same, a
69
+ * `Uint8Array` as `{"0":1,"1":2}`.
70
+ *
71
+ * Extending this list is backwards compatible: the value crosses in its
72
+ * native structured-clone form, and an older peer's base deserializer returns
73
+ * a non-basic object unchanged. A named serializer would not be — an older
74
+ * peer does not know the name and hands the wrapper object through as-is.
75
+ */
61
76
  isSerializableByBase(val) {
62
- return val instanceof Map || val instanceof Set || Array.isArray(val) || typeof val === "function" || isObjectType(val) && TRANSFERABLE in val;
77
+ return val instanceof Map || val instanceof Set || Array.isArray(val) || typeof val === "function" || val instanceof Date || val instanceof RegExp || val instanceof Error || val instanceof ArrayBuffer || ArrayBuffer.isView(val) || val instanceof Blob && !(val instanceof File) || isObjectType(val) && TRANSFERABLE in val;
63
78
  }
64
79
  omitSerialization(val) {
65
80
  return val instanceof HTMLElement || val === window;
@@ -1 +1 @@
1
- {"version":3,"file":"FlowThreadSerialization.mjs","names":[],"sources":["../../../../../../src/serialization/FlowThreadSerialization.ts"],"sourcesContent":["import {\n ThreadSerializationStructuredClone,\n type ThreadSerializationOptions,\n TRANSFERABLE,\n} from \"@quilted/threads\";\nimport * as serializerModules from \"./serializers\";\nimport { isObjectType } from \"remeda\";\nimport { Serializer } from \"@/serialization/Serializer\";\n\nconst serializers = Object.values(serializerModules).filter(\n (val) => val instanceof Serializer,\n);\n\n/*\n * React tags its elements, portals and lazy values with a symbol under\n * `$$typeof`. Symbols pass through serialization untouched and `postMessage`\n * refuses them — and it refuses the *whole* message, so a single element in one\n * prop takes down the entire mutation batch and the extension renders nothing at\n * all. The failure is silent apart from an unhandled `DataCloneError`.\n *\n * Rendered output belongs in a slot, not in a property (see `isSlot` in the\n * remote-components generator). Where it ends up in a property anyway, dropping\n * it keeps the batch intact: the one prop is missing instead of the whole tree.\n */\nconst isReactValue = (val: unknown): boolean =>\n isObjectType(val) &&\n \"$$typeof\" in val &&\n typeof (val as { $$typeof: unknown }).$$typeof === \"symbol\";\n\n/**\n * Names the component behind an element, for the warning below. `type` is a\n * string for a DOM element, a function for a component, and an object for a\n * context provider or a `memo`/`forwardRef` wrapper — the last of which is what\n * a rendered react-aria tree is mostly made of.\n */\nconst reactValueLabel = (val: unknown): string => {\n const type = (val as { type?: unknown }).type;\n\n if (typeof type === \"string\") {\n return type;\n }\n if (typeof type === \"function\" || isObjectType(type)) {\n const named = type as {\n displayName?: string;\n name?: string;\n $$typeof?: symbol;\n };\n return (\n named.displayName ??\n named.name ??\n named.$$typeof?.toString() ??\n \"anonymous\"\n );\n }\n\n return \"unknown\";\n};\n\nexport class FlowThreadSerialization extends ThreadSerializationStructuredClone {\n private readonly warnedAboutReactValues = new Set<string>();\n\n public constructor() {\n const options: ThreadSerializationOptions = {\n serialize: async (val, serialize) => {\n try {\n if (this.isSerializableByBase(val)) {\n return;\n }\n if (this.omitSerialization(val)) {\n return null;\n }\n if (isReactValue(val)) {\n this.warnAboutReactValue(val);\n return null;\n }\n for (const serializer of serializers) {\n const serialization = await serializer.serialize(val);\n\n if (serialization.applied) {\n return await serialize(serialization.result);\n }\n }\n\n if (isObjectType(val)) {\n return await serialize({ ...val });\n }\n\n return undefined;\n } catch (error) {\n console.error(\"Error while serializing\", error);\n throw error;\n }\n },\n\n deserialize: async (val, deserialize) => {\n try {\n for (const serializer of serializers) {\n const deserialization = await serializer.deserialize(val);\n\n if (deserialization.applied) {\n return deserialization.result.value;\n }\n }\n\n return await deserialize(val);\n } catch (error) {\n console.error(\"Error while deserializing\", error);\n throw error;\n }\n },\n };\n\n super(options);\n }\n\n private isSerializableByBase(val: unknown) {\n return (\n val instanceof Map ||\n val instanceof Set ||\n Array.isArray(val) ||\n typeof val === \"function\" ||\n (isObjectType(val) && TRANSFERABLE in val)\n );\n }\n\n private omitSerialization(val: unknown) {\n return val instanceof HTMLElement || val === window;\n }\n\n /**\n * Once per component, so a table of many rows reports its cause once instead\n * of once per cell.\n */\n private warnAboutReactValue(val: unknown) {\n const label = reactValueLabel(val);\n\n if (this.warnedAboutReactValues.has(label)) {\n return;\n }\n this.warnedAboutReactValues.add(label);\n\n console.error(\n `Cannot send the React element <${label} /> to the host: rendered output is not serializable, and it was replaced with null. Pass it as a slot instead of a property — a property would have dropped the whole update.`,\n );\n }\n}\n"],"mappings":";;;;;;AASA,IAAM,cAAc,OAAO,OAAO,mBAAiB,CAAC,CAAC,QAClD,QAAQ,eAAe,UAC1B;AAaA,IAAM,gBAAgB,QACpB,aAAa,GAAG,KAChB,cAAc,OACd,OAAQ,IAA8B,aAAa;;;;;;;AAQrD,IAAM,mBAAmB,QAAyB;CAChD,MAAM,OAAQ,IAA2B;CAEzC,IAAI,OAAO,SAAS,UAClB,OAAO;CAET,IAAI,OAAO,SAAS,cAAc,aAAa,IAAI,GAAG;EACpD,MAAM,QAAQ;EAKd,OACE,MAAM,eACN,MAAM,QACN,MAAM,UAAU,SAAS,KACzB;CAEJ;CAEA,OAAO;AACT;AAEA,IAAa,0BAAb,cAA6C,mCAAmC;CAC9E,yCAA0C,IAAI,IAAY;CAE1D,cAAqB;EAmDnB,MAAM;GAjDJ,WAAW,OAAO,KAAK,cAAc;IACnC,IAAI;KACF,IAAI,KAAK,qBAAqB,GAAG,GAC/B;KAEF,IAAI,KAAK,kBAAkB,GAAG,GAC5B,OAAO;KAET,IAAI,aAAa,GAAG,GAAG;MACrB,KAAK,oBAAoB,GAAG;MAC5B,OAAO;KACT;KACA,KAAK,MAAM,cAAc,aAAa;MACpC,MAAM,gBAAgB,MAAM,WAAW,UAAU,GAAG;MAEpD,IAAI,cAAc,SAChB,OAAO,MAAM,UAAU,cAAc,MAAM;KAE/C;KAEA,IAAI,aAAa,GAAG,GAClB,OAAO,MAAM,UAAU,EAAE,GAAG,IAAI,CAAC;KAGnC;IACF,SAAS,OAAO;KACd,QAAQ,MAAM,2BAA2B,KAAK;KAC9C,MAAM;IACR;GACF;GAEA,aAAa,OAAO,KAAK,gBAAgB;IACvC,IAAI;KACF,KAAK,MAAM,cAAc,aAAa;MACpC,MAAM,kBAAkB,MAAM,WAAW,YAAY,GAAG;MAExD,IAAI,gBAAgB,SAClB,OAAO,gBAAgB,OAAO;KAElC;KAEA,OAAO,MAAM,YAAY,GAAG;IAC9B,SAAS,OAAO;KACd,QAAQ,MAAM,6BAA6B,KAAK;KAChD,MAAM;IACR;GACF;EAGI,CAAO;CACf;CAEA,qBAA6B,KAAc;EACzC,OACE,eAAe,OACf,eAAe,OACf,MAAM,QAAQ,GAAG,KACjB,OAAO,QAAQ,cACd,aAAa,GAAG,KAAK,gBAAgB;CAE1C;CAEA,kBAA0B,KAAc;EACtC,OAAO,eAAe,eAAe,QAAQ;CAC/C;;;;;CAMA,oBAA4B,KAAc;EACxC,MAAM,QAAQ,gBAAgB,GAAG;EAEjC,IAAI,KAAK,uBAAuB,IAAI,KAAK,GACvC;EAEF,KAAK,uBAAuB,IAAI,KAAK;EAErC,QAAQ,MACN,kCAAkC,MAAM,+KAC1C;CACF;AACF"}
1
+ {"version":3,"file":"FlowThreadSerialization.mjs","names":[],"sources":["../../../../../../src/serialization/FlowThreadSerialization.ts"],"sourcesContent":["import {\n ThreadSerializationStructuredClone,\n type ThreadSerializationOptions,\n TRANSFERABLE,\n} from \"@quilted/threads\";\nimport * as serializerModules from \"./serializers\";\nimport { isObjectType } from \"remeda\";\nimport { Serializer } from \"@/serialization/Serializer\";\n\nconst serializers = Object.values(serializerModules).filter(\n (val) => val instanceof Serializer,\n);\n\n/*\n * React tags its elements, portals and lazy values with a symbol under\n * `$$typeof`. Symbols pass through serialization untouched and `postMessage`\n * refuses them — and it refuses the *whole* message, so a single element in one\n * prop takes down the entire mutation batch and the extension renders nothing at\n * all. The failure is silent apart from an unhandled `DataCloneError`.\n *\n * Rendered output belongs in a slot, not in a property (see `isSlot` in the\n * remote-components generator). Where it ends up in a property anyway, dropping\n * it keeps the batch intact: the one prop is missing instead of the whole tree.\n */\nconst isReactValue = (val: unknown): boolean =>\n isObjectType(val) &&\n \"$$typeof\" in val &&\n typeof (val as { $$typeof: unknown }).$$typeof === \"symbol\";\n\n/**\n * Names the component behind an element, for the warning below. `type` is a\n * string for a DOM element, a function for a component, and an object for a\n * context provider or a `memo`/`forwardRef` wrapper — the last of which is what\n * a rendered react-aria tree is mostly made of.\n */\nconst reactValueLabel = (val: unknown): string => {\n const type = (val as { type?: unknown }).type;\n\n if (typeof type === \"string\") {\n return type;\n }\n if (typeof type === \"function\" || isObjectType(type)) {\n const named = type as {\n displayName?: string;\n name?: string;\n $$typeof?: symbol;\n };\n return (\n named.displayName ??\n named.name ??\n named.$$typeof?.toString() ??\n \"anonymous\"\n );\n }\n\n return \"unknown\";\n};\n\nexport class FlowThreadSerialization extends ThreadSerializationStructuredClone {\n private readonly warnedAboutReactValues = new Set<string>();\n\n public constructor() {\n const options: ThreadSerializationOptions = {\n serialize: async (val, serialize) => {\n try {\n if (this.isSerializableByBase(val)) {\n return;\n }\n if (this.omitSerialization(val)) {\n return null;\n }\n if (isReactValue(val)) {\n this.warnAboutReactValue(val);\n return null;\n }\n for (const serializer of serializers) {\n const serialization = await serializer.serialize(val);\n\n if (serialization.applied) {\n return await serialize(serialization.result);\n }\n }\n\n if (isObjectType(val)) {\n return await serialize({ ...val });\n }\n\n return undefined;\n } catch (error) {\n console.error(\"Error while serializing\", error);\n throw error;\n }\n },\n\n deserialize: async (val, deserialize) => {\n try {\n for (const serializer of serializers) {\n const deserialization = await serializer.deserialize(val);\n\n if (deserialization.applied) {\n return deserialization.result.value;\n }\n }\n\n return await deserialize(val);\n } catch (error) {\n console.error(\"Error while deserializing\", error);\n throw error;\n }\n },\n };\n\n super(options);\n }\n\n /**\n * Values the base serializer hands to `postMessage` untouched, because\n * structured clone carries them itself.\n *\n * Everything not listed here reaches the object fallback below and is spread\n * into a plain object — which keeps only own enumerable properties. For a\n * built-in that holds its state internally that is total loss and completely\n * silent: a `Date` arrived as `{}`, a `RegExp` and an `Error` the same, a\n * `Uint8Array` as `{\"0\":1,\"1\":2}`.\n *\n * Extending this list is backwards compatible: the value crosses in its\n * native structured-clone form, and an older peer's base deserializer returns\n * a non-basic object unchanged. A named serializer would not be — an older\n * peer does not know the name and hands the wrapper object through as-is.\n */\n private isSerializableByBase(val: unknown) {\n return (\n val instanceof Map ||\n val instanceof Set ||\n Array.isArray(val) ||\n typeof val === \"function\" ||\n val instanceof Date ||\n val instanceof RegExp ||\n val instanceof Error ||\n val instanceof ArrayBuffer ||\n ArrayBuffer.isView(val) ||\n // `File` is a `Blob`, and `fileSerializer` owns it: it sends the content\n // as a transferable and its wire format is part of the protocol.\n (val instanceof Blob && !(val instanceof File)) ||\n (isObjectType(val) && TRANSFERABLE in val)\n );\n }\n\n private omitSerialization(val: unknown) {\n return val instanceof HTMLElement || val === window;\n }\n\n /**\n * Once per component, so a table of many rows reports its cause once instead\n * of once per cell.\n */\n private warnAboutReactValue(val: unknown) {\n const label = reactValueLabel(val);\n\n if (this.warnedAboutReactValues.has(label)) {\n return;\n }\n this.warnedAboutReactValues.add(label);\n\n console.error(\n `Cannot send the React element <${label} /> to the host: rendered output is not serializable, and it was replaced with null. Pass it as a slot instead of a property — a property would have dropped the whole update.`,\n );\n }\n}\n"],"mappings":";;;;;;AASA,IAAM,cAAc,OAAO,OAAO,mBAAiB,CAAC,CAAC,QAClD,QAAQ,eAAe,UAC1B;AAaA,IAAM,gBAAgB,QACpB,aAAa,GAAG,KAChB,cAAc,OACd,OAAQ,IAA8B,aAAa;;;;;;;AAQrD,IAAM,mBAAmB,QAAyB;CAChD,MAAM,OAAQ,IAA2B;CAEzC,IAAI,OAAO,SAAS,UAClB,OAAO;CAET,IAAI,OAAO,SAAS,cAAc,aAAa,IAAI,GAAG;EACpD,MAAM,QAAQ;EAKd,OACE,MAAM,eACN,MAAM,QACN,MAAM,UAAU,SAAS,KACzB;CAEJ;CAEA,OAAO;AACT;AAEA,IAAa,0BAAb,cAA6C,mCAAmC;CAC9E,yCAA0C,IAAI,IAAY;CAE1D,cAAqB;EAmDnB,MAAM;GAjDJ,WAAW,OAAO,KAAK,cAAc;IACnC,IAAI;KACF,IAAI,KAAK,qBAAqB,GAAG,GAC/B;KAEF,IAAI,KAAK,kBAAkB,GAAG,GAC5B,OAAO;KAET,IAAI,aAAa,GAAG,GAAG;MACrB,KAAK,oBAAoB,GAAG;MAC5B,OAAO;KACT;KACA,KAAK,MAAM,cAAc,aAAa;MACpC,MAAM,gBAAgB,MAAM,WAAW,UAAU,GAAG;MAEpD,IAAI,cAAc,SAChB,OAAO,MAAM,UAAU,cAAc,MAAM;KAE/C;KAEA,IAAI,aAAa,GAAG,GAClB,OAAO,MAAM,UAAU,EAAE,GAAG,IAAI,CAAC;KAGnC;IACF,SAAS,OAAO;KACd,QAAQ,MAAM,2BAA2B,KAAK;KAC9C,MAAM;IACR;GACF;GAEA,aAAa,OAAO,KAAK,gBAAgB;IACvC,IAAI;KACF,KAAK,MAAM,cAAc,aAAa;MACpC,MAAM,kBAAkB,MAAM,WAAW,YAAY,GAAG;MAExD,IAAI,gBAAgB,SAClB,OAAO,gBAAgB,OAAO;KAElC;KAEA,OAAO,MAAM,YAAY,GAAG;IAC9B,SAAS,OAAO;KACd,QAAQ,MAAM,6BAA6B,KAAK;KAChD,MAAM;IACR;GACF;EAGI,CAAO;CACf;;;;;;;;;;;;;;;;CAiBA,qBAA6B,KAAc;EACzC,OACE,eAAe,OACf,eAAe,OACf,MAAM,QAAQ,GAAG,KACjB,OAAO,QAAQ,cACf,eAAe,QACf,eAAe,UACf,eAAe,SACf,eAAe,eACf,YAAY,OAAO,GAAG,KAGrB,eAAe,QAAQ,EAAE,eAAe,SACxC,aAAa,GAAG,KAAK,gBAAgB;CAE1C;CAEA,kBAA0B,KAAc;EACtC,OAAO,eAAe,eAAe,QAAQ;CAC/C;;;;;CAMA,oBAA4B,KAAc;EACxC,MAAM,QAAQ,gBAAgB,GAAG;EAEjC,IAAI,KAAK,uBAAuB,IAAI,KAAK,GACvC;EAEF,KAAK,uBAAuB,IAAI,KAAK;EAErC,QAAQ,MACN,kCAAkC,MAAM,+KAC1C;CACF;AACF"}
@@ -2,6 +2,21 @@ import { ThreadSerializationStructuredClone } from '@quilted/threads';
2
2
  export declare class FlowThreadSerialization extends ThreadSerializationStructuredClone {
3
3
  private readonly warnedAboutReactValues;
4
4
  constructor();
5
+ /**
6
+ * Values the base serializer hands to `postMessage` untouched, because
7
+ * structured clone carries them itself.
8
+ *
9
+ * Everything not listed here reaches the object fallback below and is spread
10
+ * into a plain object — which keeps only own enumerable properties. For a
11
+ * built-in that holds its state internally that is total loss and completely
12
+ * silent: a `Date` arrived as `{}`, a `RegExp` and an `Error` the same, a
13
+ * `Uint8Array` as `{"0":1,"1":2}`.
14
+ *
15
+ * Extending this list is backwards compatible: the value crosses in its
16
+ * native structured-clone form, and an older peer's base deserializer returns
17
+ * a non-basic object unchanged. A named serializer would not be — an older
18
+ * peer does not know the name and hands the wrapper object through as-is.
19
+ */
5
20
  private isSerializableByBase;
6
21
  private omitSerialization;
7
22
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-remote-core",
3
- "version": "1.3.0-next.9",
3
+ "version": "1.3.1",
4
4
  "type": "module",
5
5
  "description": "Core functionality to setup a remote/host environment",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -36,10 +36,10 @@
36
36
  "zod": "^4.4.3"
37
37
  },
38
38
  "devDependencies": {
39
- "@mittwald/ext-bridge": "1.3.0-next.9",
40
- "@mittwald/flow-core": "1.3.0-next.9",
41
- "@mittwald/flow-react-components": "1.3.0-next.9",
42
- "@mittwald/typescript-config": "1.3.0-next.9",
39
+ "@mittwald/ext-bridge": "1.3.1",
40
+ "@mittwald/flow-core": "1.3.1",
41
+ "@mittwald/flow-react-components": "1.3.1",
42
+ "@mittwald/typescript-config": "1.3.1",
43
43
  "@types/node": "^25.9.3",
44
44
  "nx": "^22.7.5",
45
45
  "rimraf": "^6.1.3",
@@ -51,13 +51,13 @@
51
51
  "vitest": "^4.1.11"
52
52
  },
53
53
  "peerDependencies": {
54
- "@mittwald/ext-bridge": "1.3.0-next.9",
55
- "@mittwald/flow-react-components": "1.3.0-next.9"
54
+ "@mittwald/ext-bridge": "1.3.1",
55
+ "@mittwald/flow-react-components": "1.3.1"
56
56
  },
57
57
  "peerDependenciesMeta": {
58
58
  "@mittwald/ext-bridge": {
59
59
  "optional": true
60
60
  }
61
61
  },
62
- "gitHead": "559e56fc7e7b9b80de60ce39c179cfaeb4e7f9a1"
62
+ "gitHead": "920f9a93b116dfe0b683cc84d5d9014cef62217b"
63
63
  }