Skip to content

Streams thinking - prototype#121

Closed
aarne wants to merge 10 commits intomainfrom
stream
Closed

Streams thinking - prototype#121
aarne wants to merge 10 commits intomainfrom
stream

Conversation

@aarne
Copy link
Contributor

@aarne aarne commented Mar 9, 2026

  • o <- data[] as {} - can be a async generator
  • tools can accept generators and be generator for next tools
  • patch API, hasNext logic (how to send array[0] multiple times
  • multiple push streams per response
  • GraphQL connection

Playground: https://stream-bridge.aarne-laur.workers.dev/

@changeset-bot
Copy link

changeset-bot bot commented Mar 9, 2026

🦋 Changeset detected

Latest commit: 9270bf7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@stackables/bridge Patch
@stackables/bridge-core Patch
@stackables/bridge-parser Patch
@stackables/bridge-compiler Patch
@stackables/bridge-graphql Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

} else if (typeof oldVal === "string" && typeof newVal === "string") {
accumulator[key] = oldVal + newVal;
} else {
accumulator[key] = newVal;

Check warning

Code scanning / CodeQL

Prototype-polluting function Medium

Properties are copied from
item
to
accumulator
without guarding against prototype pollution.

Copilot Autofix

AI 4 days ago

In general, to fix prototype pollution in deep merge / accumulate helpers, you should prevent assignments to dangerous prototype-related keys (__proto__, constructor, and optionally prototype) and/or only recurse into properties that are own properties of the destination object. This preserves existing semantics for normal keys while blocking the vectors used to reach Object.prototype or constructor prototypes.

For this specific code, the minimal-impact fix is to introduce a small helper like isSafeKey and use it to skip dangerous keys both at the top-level merge loop (for (const key of Object.keys(item))) and when recursing into nested objects via deepMergeStream. This avoids prototype mutation while keeping the existing merge behavior (arrays, objects, strings, overwrite) for all legitimate keys. We should implement isSafeKey directly in accumulate.ts above deepMergeStream, and add checks:

  • At line 17–18: skip any key that is not safe before reading item[key].
  • Before any recursive call to deepMergeStream on oldElem/newElem (array branch) and oldVal/newVal (object branch), rely on the top-level key check to ensure the key is safe; nested recursion works on plain objects that are now free from dangerous keys at each level.
  • Also ensure we don’t assign accumulator[key] = newVal or accumulator[key] = oldVal + newVal for unsafe keys.

The simplest way to implement this while touching as little code as possible is:

  1. Add:
function isSafeKey(key: string): boolean {
  return key !== "__proto__" && key !== "constructor" && key !== "prototype";
}
  1. At the start of the for (const key of Object.keys(item)) loop, immediately continue if !isSafeKey(key).

Because all further uses of key in this function are in that loop, this single guard prevents any writes under dangerous keys and, transitively, prevents nested dangerous keys from being merged as well.

Suggested changeset 1
packages/bridge-stdlib/src/tools/accumulate.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/bridge-stdlib/src/tools/accumulate.ts b/packages/bridge-stdlib/src/tools/accumulate.ts
--- a/packages/bridge-stdlib/src/tools/accumulate.ts
+++ b/packages/bridge-stdlib/src/tools/accumulate.ts
@@ -10,11 +10,16 @@
  * - Both strings → concatenate
  * - Otherwise → overwrite with the new value
  */
+function isSafeKey(key: string): boolean {
+  return key !== "__proto__" && key !== "constructor" && key !== "prototype";
+}
+
 function deepMergeStream(
   accumulator: Record<string, unknown>,
   item: Record<string, unknown>,
 ): void {
   for (const key of Object.keys(item)) {
+    if (!isSafeKey(key)) continue;
     const newVal = item[key];
     if (newVal === undefined || newVal === null) continue;
     const oldVal = accumulator[key];
EOF
@@ -10,11 +10,16 @@
* - Both strings concatenate
* - Otherwise overwrite with the new value
*/
function isSafeKey(key: string): boolean {
return key !== "__proto__" && key !== "constructor" && key !== "prototype";
}

function deepMergeStream(
accumulator: Record<string, unknown>,
item: Record<string, unknown>,
): void {
for (const key of Object.keys(item)) {
if (!isSafeKey(key)) continue;
const newVal = item[key];
if (newVal === undefined || newVal === null) continue;
const oldVal = accumulator[key];
Copilot is powered by AI and may make mistakes. Always verify output.
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 9, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bridge 9270bf7 Commit Preview URL

Branch Preview URL
Mar 10 2026, 09:07 AM

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

🐰 Bencher Report

Branchstream
Testbedubuntu-latest

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🚨 22 Alerts

🐰 View full continuous benchmarking report in Bencher

@aarne aarne linked an issue Mar 10, 2026 that may be closed by this pull request
@aarne aarne changed the title Streams thinking Streams thinking - prototype Mar 10, 2026
@aarne aarne closed this Mar 10, 2026
@aarne aarne removed a link to an issue Mar 13, 2026
@aarne aarne deleted the stream branch March 13, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant