From 84ebed50b3f812b4dae9ff63d9f11ed85db46196 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 13 Dec 2024 18:30:09 +0100 Subject: [PATCH 01/38] Update `DeferredJsonMerger` to take `pending` and `completed` into account. --- .../apollo/internal/DeferredJsonMerger.kt | 101 +- .../test/defer/DeferredJsonMergerTest.kt | 1375 ++++++++++++----- 2 files changed, 1093 insertions(+), 383 deletions(-) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt index b034caecc28..f2eb734ad82 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt @@ -15,19 +15,25 @@ private typealias MutableJsonMap = MutableMap * Each call to [merge] will merge the given chunk into the [merged] Map, and will also update the [mergedFragmentIds] Set with the * value of its `path` and `label` field. * - * The fields in `data` are merged into the node found in [merged] at `path` (for the first call to [merge], the payload is - * copied to [merged] as-is). + * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field (for the first call to + * [merge], the payload is copied to [merged] as-is). * - * `errors` in incremental items (if present) are merged together in an array and then set to the `errors` field of the [merged] Map, - * at each call to [merge]. - * `extensions` in incremental items (if present) are merged together in an array and then set to the `extensions/incremental` field of the + * `errors` in incremental and completed items (if present) are merged together in an array and then set to the `errors` field of the * [merged] Map, at each call to [merge]. + * `extensions` in incremental items (if present) are merged together in an array and then set to the `extensions` field of the [merged] + * Map, at each call to [merge]. */ @ApolloInternal +@Suppress("UNCHECKED_CAST") class DeferredJsonMerger { private val _merged: MutableJsonMap = mutableMapOf() val merged: JsonMap = _merged + /** + * Map of identifiers to their corresponding DeferredFragmentIdentifier, found in `pending`. + */ + private val idsToDeferredFragmentIdentifiers = mutableMapOf() + private val _mergedFragmentIds = mutableSetOf() val mergedFragmentIds: Set = _mergedFragmentIds @@ -47,11 +53,12 @@ class DeferredJsonMerger { return merge(payloadMap) } - @Suppress("UNCHECKED_CAST") fun merge(payload: JsonMap): JsonMap { if (merged.isEmpty()) { - // Initial payload, no merging needed - _merged += payload + // Initial payload, no merging needed (strip some fields that should not appear in the final result) + _merged += payload - "hasNext" - "pending" + handlePending(payload) + handleCompleted(payload) return merged } @@ -60,48 +67,68 @@ class DeferredJsonMerger { isEmptyPayload = true } else { isEmptyPayload = false - val mergedErrors = mutableListOf() - val mergedExtensions = mutableListOf() for (incrementalItem in incrementalList) { - mergeData(incrementalItem) - // Merge errors and extensions (if any) of the incremental list - (incrementalItem["errors"] as? List)?.let { mergedErrors += it } - (incrementalItem["extensions"] as? JsonMap)?.let { mergedExtensions += it } - } - // Keep only this payload's errors and extensions, if any - if (mergedErrors.isNotEmpty()) { - _merged["errors"] = mergedErrors - } else { - _merged.remove("errors") - } - if (mergedExtensions.isNotEmpty()) { - _merged["extensions"] = mapOf("incremental" to mergedExtensions) - } else { - _merged.remove("extensions") + mergeIncrementalData(incrementalItem) + // Merge errors (if any) of the incremental item + (incrementalItem["errors"] as? List)?.let { getOrPutMergedErrors() += it } } } hasNext = payload["hasNext"] as Boolean? ?: false + handlePending(payload) + handleCompleted(payload) + + (payload["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } + return merged } - @Suppress("UNCHECKED_CAST") - private fun mergeData(incrementalItem: JsonMap) { - val data = incrementalItem["data"] as JsonMap? - val path = incrementalItem["path"] as List - val mergedData = merged["data"] as JsonMap + private fun getOrPutMergedErrors() = _merged.getOrPut("errors") { mutableListOf() } as MutableList - // payloadData can be null if there are errors - if (data != null) { - val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap - deepMerge(nodeToMergeInto, data) + private fun getOrPutExtensions() = _merged.getOrPut("extensions") { mutableMapOf() } as MutableJsonMap - _mergedFragmentIds += DeferredFragmentIdentifier(path = path, label = incrementalItem["label"] as String?) + private fun handlePending(payload: JsonMap) { + val pending = payload["pending"] as? List + if (pending != null) { + for (pendingItem in pending) { + val id = pendingItem["id"] as String + val path = pendingItem["path"] as List + val label = pendingItem["label"] as String? + idsToDeferredFragmentIdentifiers[id] = DeferredFragmentIdentifier(path = path, label = label) + } } } - @Suppress("UNCHECKED_CAST") + private fun handleCompleted(payload: JsonMap) { + val completed = payload["completed"] as? List + if (completed != null) { + for (completedItem in completed) { + val errors = completedItem["errors"] as? List + if (errors != null) { + // Merge errors (if any) of the completed item + getOrPutMergedErrors() += errors + } else { + // No errors: we have merged all the fields of the fragment so it can be parsed + val id = completedItem["id"] as String + val deferredFragmentIdentifier = idsToDeferredFragmentIdentifiers.remove(id) + ?: error("Id '$id' not found in pending results") + _mergedFragmentIds += deferredFragmentIdentifier + } + } + } + } + + private fun mergeIncrementalData(incrementalItem: JsonMap) { + val id = incrementalItem["id"] as String? ?: error("No id found in incremental item") + val data = incrementalItem["data"] as JsonMap? ?: error("No data found in incremental item") + val subPath = incrementalItem["subPath"] as List? ?: emptyList() + val path = (idsToDeferredFragmentIdentifiers[id]?.path ?: error("Id '$id' not found in pending results")) + subPath + val mergedData = merged["data"] as JsonMap + val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap + deepMerge(nodeToMergeInto, data) + } + private fun deepMerge(destination: MutableJsonMap, map: JsonMap) { for ((key, value) in map) { if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { @@ -116,7 +143,6 @@ class DeferredJsonMerger { } } - @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: BufferedSource): JsonMap = BufferedSourceJsonReader(json).readAny() as JsonMap @@ -130,7 +156,6 @@ class DeferredJsonMerger { node = if (node is List<*>) { node[key as Int] } else { - @Suppress("UNCHECKED_CAST") node as JsonMap node[key] } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt index 40918461ae3..ee2a1ff3f89 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt @@ -16,11 +16,12 @@ private fun String.buffer() = Buffer().writeUtf8(this) private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map class DeferredJsonMergerTest { - @Test - fun mergeJsonSingleIncrementalItem() { - val deferredJsonMerger = DeferredJsonMerger() + @Test + fun mergeJsonSingleIncrementalItem() { + val deferredJsonMerger = DeferredJsonMerger() - val payload1 = """ + //language=JSON + val payload1 = """ { "data": { "computers": [ @@ -38,14 +39,45 @@ class DeferredJsonMergerTest { } ] }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + } + ], "hasNext": true } """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + } + } + """ deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(payload1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) - + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + //language=JSON val payload2 = """ { "incremental": [ @@ -57,22 +89,34 @@ class DeferredJsonMergerTest { "resolution": "640x480" } }, + "id": "0" + } + ], + "completed": [ + { + "id": "0" + } + ], + "pending": [ + { + "id": "1", "path": [ "computers", - 0 + 1 ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - } + "label": "query:Query1:0" } ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, "hasNext": true } """ + //language=JSON val mergedPayloads_1_2 = """ { "data": { @@ -94,54 +138,64 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true, "extensions": { - "incremental": [ - { - "duration": { - "amount": 100, - "unit": "ms" - } - } - ] + "duration": { + "amount": 100, + "unit": "ms" + } } } """ deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + ), + deferredJsonMerger.mergedFragmentIds + ) + //language=JSON val payload3 = """ - { - "incremental": [ - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } + { + "incremental": [ + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" } - } - ], - "hasNext": true - } + }, + "id": "1" + } + ], + "completed": [ + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", + "path": [ + "computers", + 0, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + }, + "hasNext": true + } """ + //language=JSON val mergedPayloads_1_2_3 = """ { "data": { @@ -166,61 +220,64 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true, "extensions": { - "incremental": [ - { - "duration": { - "amount": 25, - "unit": "ms" - } - } - ] + "duration": { + "amount": 25, + "unit": "ms" + } } } """ deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + deferredJsonMerger.mergedFragmentIds + ) + //language=JSON val payload4 = """ - { - "incremental": [ - { - "data": null, - "path": [ - "computers", - 0, - "screen" - ], - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], - "label": "fragment:ComputerFields:0" - } - ], - "hasNext": true - } + { + "completed": [ + { + "id": "2", + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ] + } + ], + "pending": [ + { + "id": "3", + "path": [ + "computers", + 1, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "hasNext": true + } """ + //language=JSON val mergedPayloads_1_2_3_4 = """ { "data": { @@ -245,7 +302,6 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true, "errors": [ { "message": "Cannot resolve isColor", @@ -262,54 +318,63 @@ class DeferredJsonMergerTest { "isColor" ] } - ] + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } } """ deferredJsonMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + deferredJsonMerger.mergedFragmentIds + ) + //language=JSON val payload5 = """ - { - "incremental": [ - { - "data": { - "isColor": false - }, - "path": [ - "computers", - 1, - "screen" - ], - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ], - "label": "fragment:ComputerFields:0", - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } + { + "incremental": [ + { + "data": { + "isColor": false + }, + "id": "3", + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] } - } - ], - "hasNext": false - } + ] + } + ], + "completed": [ + { + "id": "3" + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false + } """ + //language=JSON val mergedPayloads_1_2_3_4_5 = """ { "data": { @@ -335,19 +400,22 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - ] - }, "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + }, { "message": "Another error", "locations": [ @@ -357,24 +425,34 @@ class DeferredJsonMergerTest { } ] } - ] + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } } """ - deferredJsonMerger.merge(payload5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - } + deferredJsonMerger.merge(payload5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + deferredJsonMerger.mergedFragmentIds + ) + } - @Test - fun mergeJsonMultipleIncrementalItems() { - val deferredJsonMerger = DeferredJsonMerger() + @Test + fun mergeJsonMultipleIncrementalItems() { + val deferredJsonMerger = DeferredJsonMerger() - val payload1 = """ + //language=JSON + val payload1 = """ { "data": { "computers": [ @@ -392,151 +470,166 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true - } - """ - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(payload1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) - - - val payload2_3 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - } - }, - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" }, - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" } - } - ], - "hasNext": true - } + ], + "hasNext": true + } """ - val mergedPayloads_1_2_3 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "computers": [ { "id": "Computer1", - "cpu": "386", - "year": 1993, "screen": { - "isTouch": true, - "resolution": "640x480" + "isTouch": true } }, { "id": "Computer2", - "cpu": "486", - "year": 1996, "screen": { - "isTouch": false, - "resolution": "640x480" - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "duration": { - "amount": 100, - "unit": "ms" - } - }, - { - "duration": { - "amount": 25, - "unit": "ms" + "isTouch": false } } ] } } """ - deferredJsonMerger.merge(payload2_3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) - val payload4_5 = """ + //language=JSON + val payload2_3 = """ { "incremental": [ { - "data": null, + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + }, + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "id": "1" + } + ], + "completed": [ + { + "id": "0" + }, + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", "path": [ "computers", 0, "screen" ], - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], "label": "fragment:ComputerFields:0" }, { - "data": { - "isColor": false - }, + "id": "3", "path": [ "computers", 1, "screen" ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + } + """ + deferredJsonMerger.merge(payload2_3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + deferredJsonMerger.mergedFragmentIds + ) + + //language=JSON + val payload4_5 = """ + { + "incremental": [ + { + "data": { + "isColor": false + }, + "id": "3", "errors": [ { "message": "Another error", @@ -547,21 +640,46 @@ class DeferredJsonMergerTest { } ] } - ], - "label": "fragment:ComputerFields:0", - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" + ] + } + ], + "completed": [ + { + "id": "2", + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] } - } + ] + }, + { + "id": "3" } ], - "hasNext": true + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false } """ - val mergedPayloads_1_2_3_4_5 = """ + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ { "data": { "computers": [ @@ -586,19 +704,16 @@ class DeferredJsonMergerTest { } ] }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - ] - }, "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + }, { "message": "Cannot resolve isColor", "locations": [ @@ -613,34 +728,35 @@ class DeferredJsonMergerTest { "screen", "isColor" ] - }, - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] } - ] + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } } """ - deferredJsonMerger.merge(payload4_5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) - assertEquals(setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), - ), deferredJsonMerger.mergedFragmentIds - ) - } + deferredJsonMerger.merge(payload4_5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + deferredJsonMerger.mergedFragmentIds + ) + } - @Test - fun emptyPayloads() { - val deferredJsonMerger = DeferredJsonMerger() + @Test + fun emptyPayloads() { + val deferredJsonMerger = DeferredJsonMerger() - val payload1 = """ + //language=JSON + val payload1 = """ { "data": { "computers": [ @@ -658,21 +774,40 @@ class DeferredJsonMergerTest { } ] }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + }, + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], "hasNext": true } """ - deferredJsonMerger.merge(payload1.buffer()) - assertFalse(deferredJsonMerger.isEmptyPayload) + deferredJsonMerger.merge(payload1.buffer()) + assertFalse(deferredJsonMerger.isEmptyPayload) - val payload2 = """ + //language=JSON + val payload2 = """ { "hasNext": true } """ - deferredJsonMerger.merge(payload2.buffer()) - assertTrue(deferredJsonMerger.isEmptyPayload) - - val payload3 = """ + deferredJsonMerger.merge(payload2.buffer()) + assertTrue(deferredJsonMerger.isEmptyPayload) + //language=JSON + val payload3 = """ { "incremental": [ { @@ -683,31 +818,581 @@ class DeferredJsonMergerTest { "resolution": "640x480" } }, - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" + "id": "0" + } + ], + "hasNext": true + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertFalse(deferredJsonMerger.isEmptyPayload) + + //language=JSON + val payload4 = """ + { + "hasNext": false + } + """ + deferredJsonMerger.merge(payload4.buffer()) + assertTrue(deferredJsonMerger.isEmptyPayload) + } + + /** + * Example A from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + */ + @Test + fun june2023ExampleA() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { "h": "h", "i": "i" } + } + } + }, + "pending": [{ "path": [], "id": "0" }], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { "h": "h", "i": "i" } + } + } + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { "id": "0", "data": { "MyFragment": "Query" } }, + { "id": "0", "subPath": ["f2", "c", "f"], "data": { "j": "j" } } + ], + "completed": [{ "id": "0" }], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { "h": "h", "i": "i", "j": "j" } + } + }, + "MyFragment": "Query" + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + ), + deferredJsonMerger.mergedFragmentIds + ) + } + + /** + * Example A2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + */ + @Test + fun june2023ExampleA2() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": {"f2": {"a": "A", "b": "B", "c": { + "d": "D", "e": "E", "f": { + "h": "H", "i": "I" + } + }}}, + "pending": [{"id": "0", "path": [], "label": "D1"}], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I" } } } + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + + //language=JSON + val payload2 = """ + { + "incremental": [ + {"id": "0", "subPath": ["f2", "c", "f"], "data": {"j": "J", "k": "K"}} + ], + "pending": [{"id": "1", "path": ["f2", "c", "f"], "label": "D2"}], + "completed": [ + {"id": "0"} ], "hasNext": true } """ - deferredJsonMerger.merge(payload3.buffer()) - assertFalse(deferredJsonMerger.isEmptyPayload) + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K" + } + } + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "D1"), + ), + deferredJsonMerger.mergedFragmentIds + ) - val payload4 = """ + //language=JSON + val payload3 = """ { + "incremental": [ + {"id": "1", "data": {"l": "L", "m": "M"}} + ], + "completed": [ + {"id": "1"} + ], "hasNext": false } """ - deferredJsonMerger.merge(payload4.buffer()) - assertTrue(deferredJsonMerger.isEmptyPayload) - } -} \ No newline at end of file + + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K", + "l": "L", + "m": "M" + } + } + } + } + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "D1"), + DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), + ), + deferredJsonMerger.mergedFragmentIds + ) + } + + /** + * Example B1 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + */ + @Test + fun june2023ExampleB1() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "a": { "b": { "c": { "d": "d" } } } + }, + "pending": [ + { "path": [], "id": "0", "label": "Blue" }, + { "path": ["a", "b"], "id": "1", "label": "Red" } + ], + "hasNext": true + } + """ + + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { "id": "1", "data": { "potentiallySlowFieldA": "potentiallySlowFieldA" } }, + { "id": "1", "data": { "e": { "f": "f" } } } + ], + "completed": [{ "id": "1" }], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + deferredJsonMerger.mergedFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { "id": "0", "data": { "g": { "h": "h" }, "potentiallySlowFieldB": "potentiallySlowFieldB" } } + ], + "completed": [{ "id": "0" }], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + deferredJsonMerger.mergedFragmentIds + ) + } + + /** + * Example B2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + */ + @Test + fun june2023ExampleB2() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "a": { "b": { "c": { "d": "d" } } } + }, + "pending": [ + { "path": [], "id": "0", "label": "Blue" }, + { "path": ["a", "b"], "id": "1", "label": "Red" } + ], + "hasNext": true + } + """ + + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { "id": "0", "data": { "g": { "h": "h" }, "potentiallySlowFieldB": "potentiallySlowFieldB" } }, + { "id": "1", "data": { "e": { "f": "f" } } } + ], + "completed": [{ "id": "0" }], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + } + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + ), + deferredJsonMerger.mergedFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { "id": "1", "data": { "potentiallySlowFieldA": "potentiallySlowFieldA" } } + ], + "completed": [{ "id": "1" }], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + deferredJsonMerger.mergedFragmentIds + ) + } + + /** + * Example D from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + */ + @Test + fun june2023ExampleD() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { "me": {} }, + "pending": [ + { "path": [], "id": "0" }, + { "path": ["me"], "id": "1" } + ], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "data": { "list": [{ "item": {} }, { "item": {} }, { "item": {} }] } + }, + { "id": "1", "subPath": ["list", 0, "item"], "data": { "id": "1" } }, + { "id": "1", "subPath": ["list", 1, "item"], "data": { "id": "2" } }, + { "id": "1", "subPath": ["list", 2, "item"], "data": { "id": "3" } } + ], + "completed": [{ "id": "1" }], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "list": [ + { "item": { "id": "1" } }, + { "item": { "id": "2" } }, + { "item": { "id": "3" } } + ] + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = null), + ), + deferredJsonMerger.mergedFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { "id": "0", "subPath": ["me", "list", 0, "item"], "data": { "value": "Foo" } }, + { "id": "0", "subPath": ["me", "list", 1, "item"], "data": { "value": "Bar" } }, + { "id": "0", "subPath": ["me", "list", 2, "item"], "data": { "value": "Baz" } } + ], + "completed": [{ "id": "0" }], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "list": [ + { "item": { "id": "1", "value": "Foo" } }, + { "item": { "id": "2", "value": "Bar" } }, + { "item": { "id": "3", "value": "Baz" } } + ] + } + } + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = null), + DeferredFragmentIdentifier(path = listOf(), label = null), + ), + deferredJsonMerger.mergedFragmentIds + ) + } +} From e7e17aee1d522ef6e7fe1279cf360b3d6e9aaaf0 Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 16 Dec 2024 14:44:46 +0100 Subject: [PATCH 02/38] Track pending fragment ids rather than completed ones. --- .../apollo/api/BooleanExpression.kt | 10 +- .../apollo/internal/DeferredJsonMerger.kt | 22 +- .../network/http/HttpNetworkTransport.kt | 2 +- .../websocket/WebSocketNetworkTransport.kt | 2 +- .../network/ws/WebSocketNetworkTransport.kt | 3 +- .../test/defer/DeferredJsonMergerTest.kt | 473 +++++++++++++++--- 6 files changed, 431 insertions(+), 81 deletions(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index 464b4833559..bddc85a8729 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -74,16 +74,20 @@ fun BooleanExpression.evaluate( return evaluate { when (it) { is BVariable -> !(variables?.contains(it.name) ?: false) - is BLabel -> hasDeferredFragment(deferredFragmentIdentifiers, croppedPath!!, it.label) + is BLabel -> !isDeferredFragmentPending(deferredFragmentIdentifiers, croppedPath!!, it.label) is BPossibleTypes -> it.possibleTypes.contains(typename) } } } -private fun hasDeferredFragment(deferredFragmentIdentifiers: Set?, path: List, label: String?): Boolean { +private fun isDeferredFragmentPending( + deferredFragmentIdentifiers: Set?, + path: List, + label: String?, +): Boolean { if (deferredFragmentIdentifiers == null) { // By default, parse all deferred fragments - this is the case when parsing from the normalized cache. - return true + return false } return deferredFragmentIdentifiers.contains(DeferredFragmentIdentifier(path, label)) } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt index f2eb734ad82..77595a2b40d 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt @@ -12,7 +12,7 @@ private typealias MutableJsonMap = MutableMap /** * Utility class for merging GraphQL JSON payloads received in multiple chunks when using the `@defer` directive. * - * Each call to [merge] will merge the given chunk into the [merged] Map, and will also update the [mergedFragmentIds] Set with the + * Each call to [merge] will merge the given chunk into the [merged] Map, and will also update the [pendingFragmentIds] Set with the * value of its `path` and `label` field. * * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field (for the first call to @@ -32,10 +32,8 @@ class DeferredJsonMerger { /** * Map of identifiers to their corresponding DeferredFragmentIdentifier, found in `pending`. */ - private val idsToDeferredFragmentIdentifiers = mutableMapOf() - - private val _mergedFragmentIds = mutableSetOf() - val mergedFragmentIds: Set = _mergedFragmentIds + private val _pendingFragmentIds = mutableMapOf() + val pendingFragmentIds: Set get() = _pendingFragmentIds.values.toSet() var hasNext: Boolean = true private set @@ -95,7 +93,7 @@ class DeferredJsonMerger { val id = pendingItem["id"] as String val path = pendingItem["path"] as List val label = pendingItem["label"] as String? - idsToDeferredFragmentIdentifiers[id] = DeferredFragmentIdentifier(path = path, label = label) + _pendingFragmentIds[id] = DeferredFragmentIdentifier(path = path, label = label) } } } @@ -104,16 +102,14 @@ class DeferredJsonMerger { val completed = payload["completed"] as? List if (completed != null) { for (completedItem in completed) { + // Merge errors (if any) of the completed item val errors = completedItem["errors"] as? List if (errors != null) { - // Merge errors (if any) of the completed item getOrPutMergedErrors() += errors } else { - // No errors: we have merged all the fields of the fragment so it can be parsed + // Fragment is no longer pending - only if there were no errors val id = completedItem["id"] as String - val deferredFragmentIdentifier = idsToDeferredFragmentIdentifiers.remove(id) - ?: error("Id '$id' not found in pending results") - _mergedFragmentIds += deferredFragmentIdentifier + _pendingFragmentIds.remove(id) ?: error("Id '$id' not found in pending results") } } } @@ -123,7 +119,7 @@ class DeferredJsonMerger { val id = incrementalItem["id"] as String? ?: error("No id found in incremental item") val data = incrementalItem["data"] as JsonMap? ?: error("No data found in incremental item") val subPath = incrementalItem["subPath"] as List? ?: emptyList() - val path = (idsToDeferredFragmentIdentifiers[id]?.path ?: error("Id '$id' not found in pending results")) + subPath + val path = (_pendingFragmentIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath val mergedData = merged["data"] as JsonMap val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap deepMerge(nodeToMergeInto, data) @@ -165,7 +161,7 @@ class DeferredJsonMerger { fun reset() { _merged.clear() - _mergedFragmentIds.clear() + _pendingFragmentIds.clear() hasNext = true isEmptyPayload = false } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 3cd3bbada72..974b032e43b 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -222,7 +222,7 @@ private constructor( jsonMerger = DeferredJsonMerger() } val merged = jsonMerger.merge(part) - val deferredFragmentIds = jsonMerger.mergedFragmentIds + val deferredFragmentIds = jsonMerger.pendingFragmentIds val isLast = !jsonMerger.hasNext if (jsonMerger.isEmptyPayload) { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index 7dec7222547..1bfe1f0b98a 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -215,7 +215,7 @@ private class DefaultSubscriptionParser(private val request: } val (payload, mergedFragmentIds) = if (responseMap.isDeferred()) { - deferredJsonMerger.merge(responseMap) to deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.merge(responseMap) to deferredJsonMerger.pendingFragmentIds } else { responseMap to null } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index 80383932fcc..8ffda6b9285 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -45,7 +45,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onSubscription import kotlinx.coroutines.launch -import okio.use /** * A [NetworkTransport] that manages a single instance of a [WebSocketConnection]. @@ -304,7 +303,7 @@ private constructor( val responsePayload = response.payload val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters]!! val (payload, mergedFragmentIds) = if (responsePayload.isDeferred()) { - deferredJsonMerger.merge(responsePayload) to deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.merge(responsePayload) to deferredJsonMerger.pendingFragmentIds } else { responsePayload to null } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt index ee2a1ff3f89..c0a9c4cf480 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt @@ -75,7 +75,12 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -150,9 +155,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0") ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -232,10 +237,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -331,10 +335,10 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -439,11 +443,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) } @@ -514,7 +516,13 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2_3 = """ @@ -615,10 +623,10 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -743,11 +751,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) } @@ -838,7 +844,7 @@ class DeferredJsonMergerTest { } /** - * Example A from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + * Example A from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) */ @Test fun june2023ExampleA() { @@ -879,7 +885,12 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -912,15 +923,13 @@ class DeferredJsonMergerTest { deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), - ), - deferredJsonMerger.mergedFragmentIds + setOf(), + deferredJsonMerger.pendingFragmentIds ) } /** - * Example A2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + * Example A2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) */ @Test fun june2023ExampleA2() { @@ -958,7 +967,12 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "D1"), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -998,9 +1012,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "D1"), + DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -1042,16 +1056,13 @@ class DeferredJsonMergerTest { deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "D1"), - DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), - ), - deferredJsonMerger.mergedFragmentIds + setOf(), + deferredJsonMerger.pendingFragmentIds ) } /** - * Example B1 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + * Example B1 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) */ @Test fun june2023ExampleB1() { @@ -1086,7 +1097,13 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -1121,9 +1138,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -1161,16 +1178,13 @@ class DeferredJsonMergerTest { deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), - ), - deferredJsonMerger.mergedFragmentIds + setOf(), + deferredJsonMerger.pendingFragmentIds ) } /** - * Example B2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + * Example B2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) */ @Test fun june2023ExampleB2() { @@ -1205,7 +1219,13 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -1243,9 +1263,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -1283,16 +1303,13 @@ class DeferredJsonMergerTest { deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), - ), - deferredJsonMerger.mergedFragmentIds + setOf(), + deferredJsonMerger.pendingFragmentIds ) } /** - * Example D from https://github.com/graphql/defer-stream-wg/discussions/69 (Nov 1 2024 version) + * Example D from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) */ @Test fun june2023ExampleD() { @@ -1318,7 +1335,13 @@ class DeferredJsonMergerTest { """ deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) - assertEquals(setOf(), deferredJsonMerger.mergedFragmentIds) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf("me"), label = null), + ), + deferredJsonMerger.pendingFragmentIds + ) //language=JSON val payload2 = """ @@ -1354,9 +1377,9 @@ class DeferredJsonMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = null), + DeferredFragmentIdentifier(path = listOf(), label = null), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds ) //language=JSON @@ -1388,11 +1411,339 @@ class DeferredJsonMergerTest { deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( + setOf(), + deferredJsonMerger.pendingFragmentIds + ) + } + + /** + * Example F from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleF() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": {} + }, + "pending": [ + {"id": "0", "path": ["me"], "label": "B"} + ], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = null), - DeferredFragmentIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), ), - deferredJsonMerger.mergedFragmentIds + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + {"id":"0" , "data": {"a": "A", "b": "B"}} + ], + "completed": [ + {"id": "0"} + ], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "a": "A", + "b": "B" + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf(), + deferredJsonMerger.pendingFragmentIds ) } + + /** + * Example G from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleG() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [{ "name": "My Project" }] + } + }, + "pending": [ + { "id": "0", "path": ["me"], "label": "Billing" }, + { "id": "1", "path": ["me"], "label": "Prev" } + ], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [{ "name": "My Project" }] + } + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "Billing"), + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34" + } + } + ], + "completed": [{ "id": "0" }], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [{ "name": "My Project" }], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34" + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "1", + "data": { "previousInvoices": [{ "name": "My Invoice" }] } + } + ], + "completed": [{ "id": "1" }], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [{ "name": "My Project" }], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34", + "previousInvoices": [{ "name": "My Invoice" }] + } + } + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf(), + deferredJsonMerger.pendingFragmentIds + ) + } + + /** + * Example H from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleH() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": {} + }, + "pending": [ + {"id": "0", "path": [], "label": "A"}, + {"id": "1", "path": ["me"], "label": "B"} + ], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """ + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "A"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "subPath": ["me"], + "data": { "foo": { "bar": {} } } + }, + { + "id": "0", + "subPath": ["me", "foo", "bar"], + "data": { + "baz": "BAZ" + } + } + ], + "completed": [ + {"id": "0"} + ], + "hasNext": true + } + """ + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" + } + } + } + } + } + """ + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1", + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": ["foo", "bar", "qux"] + } + ] + } + ], + "hasNext": false + } + """ + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" + } + } + } + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": ["foo", "bar", "qux"] + } + ] + } + """ + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + deferredJsonMerger.pendingFragmentIds + ) + } } From 3bc336a99031954234740933c20928082104771b Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 16 Dec 2024 17:46:57 +0100 Subject: [PATCH 03/38] Update more tests --- .../apollo/internal/DeferredJsonMerger.kt | 2 +- .../kotlin/test/DeferNormalizedCacheTest.kt | 239 +++++++++++------- .../kotlin/test/DeferSubscriptionsTest.kt | 80 ------ .../src/commonTest/kotlin/test/DeferTest.kt | 217 ++++++---------- 4 files changed, 218 insertions(+), 320 deletions(-) delete mode 100644 tests/defer/src/commonTest/kotlin/test/DeferSubscriptionsTest.kt diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt index 77595a2b40d..747ac84990b 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt @@ -59,6 +59,7 @@ class DeferredJsonMerger { handleCompleted(payload) return merged } + handlePending(payload) val incrementalList = payload["incremental"] as? List if (incrementalList == null) { @@ -74,7 +75,6 @@ class DeferredJsonMerger { hasNext = payload["hasNext"] as Boolean? ?: false - handlePending(payload) handleCompleted(payload) (payload["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } diff --git a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt index 91bea1f6f93..e6bced95233 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt @@ -4,6 +4,7 @@ import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.api.Operation import com.apollographql.apollo.cache.normalized.ApolloStore import com.apollographql.apollo.cache.normalized.FetchPolicy @@ -72,9 +73,8 @@ class DeferNormalizedCacheTest { // Fill the cache by doing a network only request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -86,9 +86,20 @@ class DeferNormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ) assertEquals(cacheExpected, cacheActual) } @@ -99,9 +110,8 @@ class DeferNormalizedCacheTest { // Fill the cache by doing a first request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -114,16 +124,26 @@ class DeferNormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -134,9 +154,8 @@ class DeferNormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -148,16 +167,26 @@ class DeferNormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -176,9 +205,8 @@ class DeferNormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.NetworkFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -188,16 +216,26 @@ class DeferNormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -216,9 +254,8 @@ class DeferNormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheAndNetwork).build() val jsonList1 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"isColor":false},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList1) @@ -232,10 +269,6 @@ class DeferNormalizedCacheTest { WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", @@ -245,9 +278,8 @@ class DeferNormalizedCacheTest { assertEquals(networkExpected, networkActual) val jsonList2 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"0"},{"data":{"isColor":true},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList2) @@ -262,10 +294,6 @@ class DeferNormalizedCacheTest { WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null)) ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null)))) - ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", @@ -281,9 +309,8 @@ class DeferNormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -299,36 +326,40 @@ class DeferNormalizedCacheTest { query, uuid, ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - )).build(), + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ).build(), - ApolloResponse.Builder( - query, - uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - )).build(), ApolloResponse.Builder( query, uuid, - ) - .data( - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - ) - ) - .errors( + ).data( + WithFragmentSpreadsQuery.Data( listOf( - Error.Builder(message = "Cannot resolve isColor") - .locations(listOf(Error.Location(1, 119))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), ) ) - .build(), + ).errors( + listOf( + Builder("Error field") + .locations(listOf(Error.Location(3, 35))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ).build() ) assertResponseListEquals(networkExpected, networkActual) @@ -337,7 +368,7 @@ class DeferNormalizedCacheTest { val exception = apolloClient.query(WithFragmentSpreadsQuery()).execute().exception check(exception is CacheMissException) assertIs(exception.suppressedExceptions.first()) - assertEquals("Object 'computers.0.screen' has no field named 'isColor'", exception.message) + assertEquals("Object 'computers.0' has no field named 'cpu'", exception.message) mockServer.awaitRequest() } @@ -404,9 +435,8 @@ class DeferNormalizedCacheTest { @Test fun mutation() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val networkActual = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataOrThrow() } @@ -414,16 +444,25 @@ class DeferNormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsMutation.Data( listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ) + ) ), ) assertEquals(networkExpected, networkActual) @@ -433,9 +472,20 @@ class DeferNormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ) assertEquals(cacheExpected, cacheActual) } @@ -443,9 +493,8 @@ class DeferNormalizedCacheTest { @Test fun mutationWithOptimisticDataFails() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val responses = apolloClient.mutation(WithFragmentSpreadsMutation()).optimisticUpdates( @@ -468,8 +517,8 @@ class DeferNormalizedCacheTest { return@runTest } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", ) val multipartBody = mockServer.enqueueMultipart("application/json") multipartBody.enqueuePart(jsonList[0].encodeUtf8(), false) diff --git a/tests/defer/src/commonTest/kotlin/test/DeferSubscriptionsTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferSubscriptionsTest.kt deleted file mode 100644 index 2942cac0b13..00000000000 --- a/tests/defer/src/commonTest/kotlin/test/DeferSubscriptionsTest.kt +++ /dev/null @@ -1,80 +0,0 @@ -package test - -import com.apollographql.apollo.ApolloClient -import com.apollographql.apollo.network.websocket.WebSocketNetworkTransport -import com.apollographql.apollo.testing.internal.runTest -import defer.WithFragmentSpreadsSubscription -import defer.WithInlineFragmentsSubscription -import defer.fragment.CounterFields -import kotlinx.coroutines.flow.toList -import kotlin.test.Ignore -import kotlin.test.Test -import kotlin.test.assertEquals - -/** - * This test is ignored on the CI because it requires a specific server to run. - * - * It can be manually tested by running the server from https://github.com/BoD/DeferDemo/tree/master/helix - */ -@Ignore -class DeferSubscriptionsTest { - private lateinit var apolloClient: ApolloClient - - private fun setUp() { - apolloClient = ApolloClient.Builder() - .serverUrl("http://localhost:4000/graphql") - .subscriptionNetworkTransport( - WebSocketNetworkTransport.Builder() - .serverUrl("ws://localhost:4000/graphql") - .build() - ) - .build() - } - - private fun tearDown() { - apolloClient.close() - } - - @Test - fun subscriptionWithInlineFragment() = runTest(before = { setUp() }, after = { tearDown() }) { - val expectedDataList = listOf( - // Emission 0, deferred payload 0 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 1, null)), - // Emission 0, deferred payload 1 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 1, WithInlineFragmentsSubscription.OnCounter(2))), - // Emission 1, deferred payload 0 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 2, null)), - // Emission 1, deferred payload 1 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 2, WithInlineFragmentsSubscription.OnCounter(4))), - // Emission 2, deferred payload 0 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 3, null)), - // Emission 2, deferred payload 1 - WithInlineFragmentsSubscription.Data(WithInlineFragmentsSubscription.Count("Counter", 3, WithInlineFragmentsSubscription.OnCounter(6))), - ) - - val actualDataList = apolloClient.subscription(WithInlineFragmentsSubscription()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - } - - @Test - fun subscriptionWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { - val expectedDataList = listOf( - // Emission 0, deferred payload 0 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 1, null)), - // Emission 0, deferred payload 1 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 1, CounterFields(2))), - // Emission 1, deferred payload 0 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 2, null)), - // Emission 1, deferred payload 1 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 2, CounterFields(4))), - // Emission 2, deferred payload 0 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 3, null)), - // Emission 2, deferred payload 1 - WithFragmentSpreadsSubscription.Data(WithFragmentSpreadsSubscription.Count("Counter", 3, CounterFields(6))), - ) - - val actualDataList = apolloClient.subscription(WithFragmentSpreadsSubscription()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - } - -} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferTest.kt index d04b2dc3b79..9d7a80a4d00 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferTest.kt @@ -3,6 +3,7 @@ package test import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.autoPersistedQueryInfo import com.apollographql.apollo.mpp.currentTimeMillis import com.apollographql.apollo.testing.internal.runTest @@ -43,11 +44,8 @@ class DeferTest { @Test fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) val expectedDataList = listOf( @@ -57,38 +55,20 @@ class DeferTest { WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ), WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), + ScreenFields(false) + ) + ) + ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), + ScreenFields(true) + ) + ) + ), ) ), ) @@ -101,11 +81,8 @@ class DeferTest { @Test fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"b"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"b"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"b"},{"id":"3","path":["computers",1,"screen"],"label":"b"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) val expectedDataList = listOf( @@ -115,38 +92,20 @@ class DeferTest { WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), ) ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false)))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), - ) - ), WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false)))), + WithInlineFragmentsQuery.OnScreen(false) + ) + ) + ), WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, WithInlineFragmentsQuery.Screen("Screen", "800x600", - WithInlineFragmentsQuery.OnScreen(true)))), + WithInlineFragmentsQuery.OnScreen(true) + ) + ) + ), ) ), ) @@ -159,11 +118,8 @@ class DeferTest { @Test fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", ) val query = WithFragmentSpreadsQuery() @@ -178,58 +134,10 @@ class DeferTest { WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) - )).build(), - - ApolloResponse.Builder( - query, - uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ) - ).build(), - - ApolloResponse.Builder( - query, - uuid, ) - .data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ) - ) - .errors( - listOf( - Error.Builder(message = "Cannot resolve isColor") - .locations(listOf(Error.Location(1, 119))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() - ) - ) - .build(), - - ApolloResponse.Builder( - query, - uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ) ).build(), + ApolloResponse.Builder( query, uuid, @@ -237,13 +145,25 @@ class DeferTest { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), + ComputerFields.Screen("Screen", "640x480", null) + ) + ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), + ScreenFields(true) + ) + ) + ), ) ) - ).build(), + ).errors( + listOf( + Builder("Error field") + .locations(listOf(Error.Location(3, 35))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ).build() ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -270,11 +190,8 @@ class DeferTest { } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) jsonList.withIndex().forEach { (index, value) -> @@ -292,21 +209,27 @@ class DeferTest { @Test fun emptyPayloadsAreIgnored() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonWithEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":true}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", """{"hasNext":false}""", ) val jsonWithoutEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", ) val expectedDataList = listOf( SimpleDeferQuery.Data( - listOf(SimpleDeferQuery.Computer("Computer", "computer1", null)) + listOf( + SimpleDeferQuery.Computer("Computer", "Computer1", null), + SimpleDeferQuery.Computer("Computer", "Computer2", null), + ) ), SimpleDeferQuery.Data( - listOf(SimpleDeferQuery.Computer("Computer", "computer1", SimpleDeferQuery.OnComputer("386"))) + listOf( + SimpleDeferQuery.Computer("Computer", "Computer1", SimpleDeferQuery.OnComputer("386")), + SimpleDeferQuery.Computer("Computer", "Computer2", SimpleDeferQuery.OnComputer("486")), + ) ), ) @@ -327,11 +250,8 @@ class DeferTest { .build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() @@ -341,10 +261,16 @@ class DeferTest { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), + ScreenFields(false) + ) + ) + ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), + ScreenFields(true) + ) + ) + ), ) ), finalResponse.dataOrThrow() @@ -360,11 +286,8 @@ class DeferTest { mockServer.enqueueString("""{"errors":[{"message":"PersistedQueryNotFound"}]}""") val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() @@ -374,10 +297,16 @@ class DeferTest { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), + ScreenFields(false) + ) + ) + ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), + ScreenFields(true) + ) + ) + ), ) ), finalResponse.dataOrThrow() From a46668127488714a6ea18f0cea5751cad4df4b6f Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 16 Dec 2024 19:41:00 +0100 Subject: [PATCH 04/38] Add Apollo Server end-to-end tests --- .../defer-with-apollo-server-tests.yml | 37 ++ .../apollo/internal/DeferredJsonMerger.kt | 14 +- tests/defer/README.md | 13 + tests/defer/apollo-server/README.md | 4 + tests/defer/apollo-server/computers.graphqls | 27 ++ tests/defer/apollo-server/computers.js | 40 ++ tests/defer/apollo-server/package.json | 18 + .../patches/@apollo+server+4.11.2.patch | 28 ++ tests/defer/build.gradle.kts | 9 +- .../commonMain/graphql/base/operation.graphql | 18 + .../kotlin/test/DeferWithApolloServerTest.kt | 360 ++++++++++++++++++ 11 files changed, 559 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/defer-with-apollo-server-tests.yml create mode 100644 tests/defer/apollo-server/README.md create mode 100644 tests/defer/apollo-server/computers.graphqls create mode 100644 tests/defer/apollo-server/computers.js create mode 100644 tests/defer/apollo-server/package.json create mode 100644 tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch create mode 100644 tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt diff --git a/.github/workflows/defer-with-apollo-server-tests.yml b/.github/workflows/defer-with-apollo-server-tests.yml new file mode 100644 index 00000000000..8397a845919 --- /dev/null +++ b/.github/workflows/defer-with-apollo-server-tests.yml @@ -0,0 +1,37 @@ +name: defer-router-tests + +on: + schedule: + - cron: '0 3 * * *' +env: + DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} + +jobs: + defer-with-router-tests: + runs-on: ubuntu-latest + if: github.repository == 'apollographql/apollo-kotlin' + steps: + - name: Checkout project + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 + + - name: Install and run graph + working-directory: tests/defer/apollo-server/ + run: | + npm install --legacy-peer-deps + npx patch-package + APOLLO_PORT=4000 npm start & + + - name: Setup Java + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1 + with: + distribution: 'temurin' + java-version: 17 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2 + + - name: Run Apollo Kotlin @defer tests + env: + DEFER_WITH_APOLLO_SERVER_TESTS: true + run: | + ./gradlew --no-daemon --console plain -p tests :defer:allTests diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt index 747ac84990b..c0d0e0d1d09 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt @@ -52,30 +52,29 @@ class DeferredJsonMerger { } fun merge(payload: JsonMap): JsonMap { + val completed = payload["completed"] as? List if (merged.isEmpty()) { // Initial payload, no merging needed (strip some fields that should not appear in the final result) _merged += payload - "hasNext" - "pending" handlePending(payload) - handleCompleted(payload) + handleCompleted(completed) return merged } handlePending(payload) val incrementalList = payload["incremental"] as? List - if (incrementalList == null) { - isEmptyPayload = true - } else { - isEmptyPayload = false + if (incrementalList != null) { for (incrementalItem in incrementalList) { mergeIncrementalData(incrementalItem) // Merge errors (if any) of the incremental item (incrementalItem["errors"] as? List)?.let { getOrPutMergedErrors() += it } } } + isEmptyPayload = completed == null && incrementalList == null hasNext = payload["hasNext"] as Boolean? ?: false - handleCompleted(payload) + handleCompleted(completed) (payload["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } @@ -98,8 +97,7 @@ class DeferredJsonMerger { } } - private fun handleCompleted(payload: JsonMap) { - val completed = payload["completed"] as? List + private fun handleCompleted(completed: List?) { if (completed != null) { for (completedItem in completed) { // Merge errors (if any) of the completed item diff --git a/tests/defer/README.md b/tests/defer/README.md index 9e4ad207f3d..4f4ac085620 100644 --- a/tests/defer/README.md +++ b/tests/defer/README.md @@ -16,3 +16,16 @@ To run them locally: subgraph: `(cd tests/defer/router/subgraphs/computers && npm install && APOLLO_PORT=4001 npm start)&` 2. Run the router: `path/to/router --supergraph tests/defer/router/simple-supergraph.graphqls &` 3. Run the tests: `DEFER_WITH_ROUTER_TESTS=true ./gradlew -p tests :defer:allTests` + +## End-to-end tests with Apollo Server + +The tests in `DeferWithApolloServerTest` are not run by default (they are excluded in the gradle conf) because they +expect an instance of [Apollo Server](https://www.apollographql.com/docs/apollo-server) running locally. + +They are enabled only when running from the specific `defer-with-apollo-server-tests` CI workflow. + +To run them locally: + +1. Install and run the + subgraph: `(cd tests/defer/apollo-server && npm install --legacy-peer-deps && npx patch-package && APOLLO_PORT=4000 npm start)&` +2. Run the tests: `DEFER_WITH_APOLLO_SERVER_TESTS=true ./gradlew -p tests :defer:allTests` diff --git a/tests/defer/apollo-server/README.md b/tests/defer/apollo-server/README.md new file mode 100644 index 00000000000..ef149563b19 --- /dev/null +++ b/tests/defer/apollo-server/README.md @@ -0,0 +1,4 @@ +# Test server using Apollo Server, for `@defer` tests + +- This uses graphql-js `17.0.0-alpha.7`, which implements the latest draft of the `@defer` incremental format (as of 2024-12-16). +- Apollo Server `4.11.2` needs a patch (in `patches`) to surface this format in the responses. diff --git a/tests/defer/apollo-server/computers.graphqls b/tests/defer/apollo-server/computers.graphqls new file mode 100644 index 00000000000..4c410b7fe7e --- /dev/null +++ b/tests/defer/apollo-server/computers.graphqls @@ -0,0 +1,27 @@ +type Query { + computers: [Computer!]! + computer(id: ID!): Computer +} + +type Mutation { + computers: [Computer!]! +} + +type Computer { + id: ID! + cpu: String! + year: Int! + screen: Screen! + errorField: String + nonNullErrorField: String! +} + +type Screen { + resolution: String! + isColor: Boolean! +} + +directive @defer( + if: Boolean! = true + label: String +) on FRAGMENT_SPREAD | INLINE_FRAGMENT diff --git a/tests/defer/apollo-server/computers.js b/tests/defer/apollo-server/computers.js new file mode 100644 index 00000000000..d5cedcd16b0 --- /dev/null +++ b/tests/defer/apollo-server/computers.js @@ -0,0 +1,40 @@ +import {ApolloServer} from '@apollo/server'; +import {startStandaloneServer} from '@apollo/server/standalone'; +import {readFileSync} from 'fs'; + +const port = process.env.APOLLO_PORT || 4000; + +const computers = [ + {id: 'Computer1', cpu: "386", year: 1993, screen: {resolution: "640x480", isColor: false}}, + {id: 'Computer2', cpu: "486", year: 1996, screen: {resolution: "800x600", isColor: true}}, +] + +const typeDefs = readFileSync('./computers.graphqls', {encoding: 'utf-8'}); +const resolvers = { + Query: { + computers: (_, args, context) => { + return computers; + }, + computer: (_, args, context) => { + return computers.find(p => p.id === args.id); + } + }, + Mutation: { + computers: (_, args, context) => { + return computers; + } + }, + Computer: { + errorField: (_, args, context) => { + throw new Error("Error field"); + }, + nonNullErrorField: (_, args, context) => { + return null; + } + } +} +const server = new ApolloServer({typeDefs, resolvers}); +const {url} = await startStandaloneServer(server, { + listen: {port: port}, +}); +console.log(`🚀 Computers subgraph ready at ${url}`); diff --git a/tests/defer/apollo-server/package.json b/tests/defer/apollo-server/package.json new file mode 100644 index 00000000000..1b469e77c4e --- /dev/null +++ b/tests/defer/apollo-server/package.json @@ -0,0 +1,18 @@ +{ + "type": "module", + "name": "subgraph-computers", + "version": "1.1.0", + "description": "", + "main": "computers.js", + "scripts": { + "start": "node computers.js" + }, + "dependencies": { + "@apollo/server": "4.11.2", + "graphql": "17.0.0-alpha.7", + "patch-package": "^8.0.0" + }, + "keywords": [], + "author": "", + "license": "MIT" +} diff --git a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch new file mode 100644 index 00000000000..d6a742855b7 --- /dev/null +++ b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch @@ -0,0 +1,28 @@ +diff --git a/node_modules/@apollo/server/dist/esm/runHttpQuery.js b/node_modules/@apollo/server/dist/esm/runHttpQuery.js +index 96ef0ab..0d341fa 100644 +--- a/node_modules/@apollo/server/dist/esm/runHttpQuery.js ++++ b/node_modules/@apollo/server/dist/esm/runHttpQuery.js +@@ -187,6 +187,7 @@ function orderExecutionResultFields(result) { + } + function orderInitialIncrementalExecutionResultFields(result) { + return { ++ ...result, + hasNext: result.hasNext, + errors: result.errors, + data: result.data, +@@ -196,6 +197,7 @@ function orderInitialIncrementalExecutionResultFields(result) { + } + function orderSubsequentIncrementalExecutionResultFields(result) { + return { ++ ...result, + hasNext: result.hasNext, + incremental: orderIncrementalResultFields(result.incremental), + extensions: result.extensions, +@@ -203,6 +205,7 @@ function orderSubsequentIncrementalExecutionResultFields(result) { + } + function orderIncrementalResultFields(incremental) { + return incremental?.map((i) => ({ ++ ...i, + hasNext: i.hasNext, + errors: i.errors, + path: i.path, diff --git a/tests/defer/build.gradle.kts b/tests/defer/build.gradle.kts index 2ae9d57fbd4..3ce2eb8231f 100644 --- a/tests/defer/build.gradle.kts +++ b/tests/defer/build.gradle.kts @@ -68,5 +68,12 @@ tasks.withType(AbstractTestTask::class.java) { } else { filter.setExcludePatterns("test.DeferWithRouterTest") } -} + // Run the defer with Apollo Server tests only from a specific CI job + val runDeferWithApolloServerTests = System.getenv("DEFER_WITH_APOLLO_SERVER_TESTS").toBoolean() + if (runDeferWithApolloServerTests) { + filter.setIncludePatterns("test.DeferWithApolloServerTest") + } else { + filter.setExcludePatterns("test.DeferWithApolloServerTest") + } +} diff --git a/tests/defer/src/commonMain/graphql/base/operation.graphql b/tests/defer/src/commonMain/graphql/base/operation.graphql index fad24d09441..3f6c144b4a2 100644 --- a/tests/defer/src/commonMain/graphql/base/operation.graphql +++ b/tests/defer/src/commonMain/graphql/base/operation.graphql @@ -109,6 +109,15 @@ query CanDeferAFragmentThatIsAlsoNotDeferredDeferredFragmentIsFirstQuery { } } +query DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query { + computer(id: "Computer1") { + screen { + ...ScreenFields @defer + ...ScreenFields + } + } +} + query CanDeferAFragmentThatIsAlsoNotDeferredNotDeferredFragmentIsFirstQuery { computer(id: "Computer1") { screen { @@ -118,6 +127,15 @@ query CanDeferAFragmentThatIsAlsoNotDeferredNotDeferredFragmentIsFirstQuery { } } +query DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query { + computer(id: "Computer1") { + screen { + ...ScreenFields + ...ScreenFields @defer + } + } +} + query HandlesErrorsThrownInDeferredFragmentsQuery { computer(id: "Computer1") { id diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt new file mode 100644 index 00000000000..b04a788138b --- /dev/null +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -0,0 +1,360 @@ +package test + +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.api.ApolloResponse +import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Optional +import com.apollographql.apollo.testing.internal.runTest +import com.benasher44.uuid.uuid4 +import defer.CanDeferFragmentsOnTheTopLevelQueryFieldQuery +import defer.CanDisableDeferUsingIfArgumentQuery +import defer.DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query +import defer.DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query +import defer.DoesNotDisableDeferWithNullIfArgumentQuery +import defer.HandlesErrorsThrownInDeferredFragmentsQuery +import defer.HandlesNonNullableErrorsThrownInDeferredFragmentsQuery +import defer.HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery +import defer.WithFragmentSpreadsMutation +import defer.WithFragmentSpreadsQuery +import defer.WithInlineFragmentsQuery +import defer.fragment.ComputerErrorField +import defer.fragment.ComputerFields +import defer.fragment.FragmentOnQuery +import defer.fragment.ScreenFields +import kotlinx.coroutines.flow.toList +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * End-to-end tests for `@defer`. + * + * These tests are not run by default (they are excluded in the gradle conf) because they expect an instance of + * [Apollo Server](https://www.apollographql.com/docs/apollo-server) running locally. + * + * They are enabled only when running from the specific `defer-with-apollo-server-tests` CI workflow. + */ +class DeferWithApolloServerTest { + private lateinit var apolloClient: ApolloClient + + private fun setUp() { + apolloClient = ApolloClient.Builder() + .serverUrl("http://127.0.0.1:4000/") + .build() + } + + private fun tearDown() { + apolloClient.close() + } + + @Test + fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true} + // {"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]} + val expectedDataList = listOf( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + ) + + val actualDataList = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true} + // {"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"b"},{"id":"3","path":["computers",1,"screen"],"label":"b"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]} + val expectedDataList = listOf( + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", null), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", + WithInlineFragmentsQuery.OnScreen(false) + ) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", + WithInlineFragmentsQuery.OnScreen(true) + ) + ) + ), + ) + ), + ) + val actualDataList = apolloClient.query(WithInlineFragmentsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithFragmentSpreadsMutation() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true} + // {"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]} + val expectedDataList = listOf( + WithFragmentSpreadsMutation.Data( + listOf( + WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsMutation.Data( + listOf( + WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + ) + + val actualDataList = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun canDisableDeferUsingIfArgument() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"__typename":"Computer","id":"Computer1","cpu":"386"},{"__typename":"Computer","id":"Computer2","cpu":"486"}]} + val expectedDataList = listOf( + CanDisableDeferUsingIfArgumentQuery.Data( + listOf( + CanDisableDeferUsingIfArgumentQuery.Computer("Computer", "Computer1", CanDisableDeferUsingIfArgumentQuery.OnComputer("386")), + CanDisableDeferUsingIfArgumentQuery.Computer("Computer", "Computer2", CanDisableDeferUsingIfArgumentQuery.OnComputer("486")), + ) + ), + ) + val actualDataList = apolloClient.query(CanDisableDeferUsingIfArgumentQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun doesNotDisableDeferWithNullIfArgument() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]} + val expectedDataList = listOf( + DoesNotDisableDeferWithNullIfArgumentQuery.Data( + listOf( + DoesNotDisableDeferWithNullIfArgumentQuery.Computer("Computer", "Computer1", null), + DoesNotDisableDeferWithNullIfArgumentQuery.Computer("Computer", "Computer2", null), + ) + ), + DoesNotDisableDeferWithNullIfArgumentQuery.Data( + listOf( + DoesNotDisableDeferWithNullIfArgumentQuery.Computer("Computer", "Computer1", DoesNotDisableDeferWithNullIfArgumentQuery.OnComputer("386")), + DoesNotDisableDeferWithNullIfArgumentQuery.Computer("Computer", "Computer2", DoesNotDisableDeferWithNullIfArgumentQuery.OnComputer("486")), + ) + ) + ) + val actualDataList = + apolloClient.query(DoesNotDisableDeferWithNullIfArgumentQuery(Optional.Absent)).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun canDeferFragmentsOnTheTopLevelQueryField() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"__typename":"Query"},"pending":[{"id":"0","path":[]}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"computers":[{"id":"Computer1"},{"id":"Computer2"}]},"id":"0"}],"completed":[{"id":"0"}]} + val expectedDataList = listOf( + CanDeferFragmentsOnTheTopLevelQueryFieldQuery.Data( + "Query", + null + ), + CanDeferFragmentsOnTheTopLevelQueryFieldQuery.Data( + "Query", + FragmentOnQuery( + listOf( + FragmentOnQuery.Computer("Computer1"), + FragmentOnQuery.Computer("Computer2"), + ) + ) + ), + ) + val actualDataList = apolloClient.query(CanDeferFragmentsOnTheTopLevelQueryFieldQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferFragmentThatIsAlsoNotDeferredIsSkipped1() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"screen":{"__typename":"Screen","isColor":false}}}} + val expectedDataList = listOf( + DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query.Data( + DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query.Computer( + DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query.Screen("Screen", ScreenFields(false)) + ) + ), + ) + val actualDataList = apolloClient.query(DeferFragmentThatIsAlsoNotDeferredIsSkipped1Query()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferFragmentThatIsAlsoNotDeferredIsSkipped2() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"screen":{"__typename":"Screen","isColor":false}}}} + val expectedDataList = listOf( + DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query.Data( + DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query.Computer( + DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query.Screen("Screen", ScreenFields(false)) + ) + ), + ) + val actualDataList = apolloClient.query(DeferFragmentThatIsAlsoNotDeferredIsSkipped2Query()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun handlesErrorsThrownInDeferredFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"__typename":"Computer","id":"Computer1"}},"pending":[{"id":"0","path":["computer"]}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"errorField":null},"errors":[{"message":"Error field","locations":[{"line":3,"column":43}],"path":["computer","errorField"],"extensions":{"code":"INTERNAL_SERVER_ERROR","stacktrace":["Error: Error field"," at Object.errorField (file:///Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/computers.js:29:19)"," at field.resolve (file:///Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/@apollo/server/dist/esm/utils/schemaInstrumentation.js:36:28)"," at executeField (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:567:20)"," at executeFields (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:476:22)"," at executeExecutionGroup (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:1855:14)"," at executor (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:1803:7)"," at pendingExecutionGroup.result (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:1825:58)"," at IncrementalGraph._onExecutionGroup (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/IncrementalGraph.js:192:33)"," at IncrementalGraph._promoteNonEmptyToRoot (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/IncrementalGraph.js:146:20)"," at IncrementalGraph.getNewRootNodes (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/IncrementalGraph.js:25:17)"]}}],"id":"0"}],"completed":[{"id":"0"}]} + val query = HandlesErrorsThrownInDeferredFragmentsQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ) + .data( + HandlesErrorsThrownInDeferredFragmentsQuery.Data( + HandlesErrorsThrownInDeferredFragmentsQuery.Computer( + "Computer", "Computer1", null + ) + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ) + .data( + HandlesErrorsThrownInDeferredFragmentsQuery.Data( + HandlesErrorsThrownInDeferredFragmentsQuery.Computer( + "Computer", "Computer1", ComputerErrorField(null) + ) + ) + ) + .errors( + listOf( + Error.Builder(message = "Error field") + .path(listOf("computer", "errorField")) + .build() + ) + ) + .build(), + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun handlesNonNullableErrorsThrownInDeferredFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"__typename":"Computer","id":"Computer1"}},"pending":[{"id":"0","path":["computer"]}],"hasNext":true} + // {"hasNext":false,"completed":[{"id":"0","errors":[{"message":"Cannot return null for non-nullable field Computer.nonNullErrorField.","locations":[{"line":3,"column":54}],"path":["computer","nonNullErrorField"]}]}]} + val query = HandlesNonNullableErrorsThrownInDeferredFragmentsQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + HandlesNonNullableErrorsThrownInDeferredFragmentsQuery.Data( + HandlesNonNullableErrorsThrownInDeferredFragmentsQuery.Computer( + "Computer", "Computer1", null + ) + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ) + .data( + HandlesNonNullableErrorsThrownInDeferredFragmentsQuery.Data( + HandlesNonNullableErrorsThrownInDeferredFragmentsQuery.Computer( + "Computer", "Computer1", null + ) + ) + ) + .errors(listOf(Error.Builder(message = "Cannot return null for non-nullable field Computer.nonNullErrorField.") + .path(listOf("computer", "nonNullErrorField")).build() + ) + ) + .build(), + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun handlesNonNullableErrorsThrownOutsideDeferredFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"errors":[{"message":"Cannot return null for non-nullable field Computer.nonNullErrorField.","locations":[{"line":1,"column":108}],"path":["computer","nonNullErrorField"],"extensions":{"code":"INTERNAL_SERVER_ERROR","stacktrace":["Error: Cannot return null for non-nullable field Computer.nonNullErrorField."," at completeValue (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:716:13)"," at executeField (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:580:23)"," at executeFields (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:476:22)"," at collectAndExecuteSubfields (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:1491:21)"," at completeObjectValue (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:1395:10)"," at completeValue (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:760:12)"," at executeField (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:580:23)"," at executeFields (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:476:22)"," at executeRootGroupedFieldSet (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:373:14)"," at executeOperation (/Users/bod/gitrepo/apollo-kotlin-0/tests/defer/apollo-server/node_modules/graphql/execution/execute.js:159:30)"]}}],"data":{"computer":null}} + val query = HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery.Data( + null + ) + ) + .errors( + listOf( + Error.Builder(message = "Cannot return null for non-nullable field Computer.nonNullErrorField.") + .path(listOf("computer", "nonNullErrorField")) + .build() + ) + ) + .build() + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } +} From f2b4179e80630b25305e2252a2f71d715524f7c6 Mon Sep 17 00:00:00 2001 From: BoD Date: Tue, 17 Dec 2024 15:26:28 +0100 Subject: [PATCH 05/38] Add a few more edge case tests --- .../defer-with-apollo-server-tests.yml | 37 ---- .github/workflows/defer-with-router-tests.yml | 28 +++ tests/defer/build.gradle.kts | 30 +-- .../commonMain/graphql/base/operation.graphql | 43 +++++ .../graphql/noTypename/operation.graphql | 11 ++ .../graphql/noTypename/schema.graphqls | 31 +++ .../kotlin/test/DeferWithApolloServerTest.kt | 176 ++++++++++++++++++ 7 files changed, 306 insertions(+), 50 deletions(-) delete mode 100644 .github/workflows/defer-with-apollo-server-tests.yml create mode 100644 tests/defer/src/commonMain/graphql/noTypename/operation.graphql create mode 100644 tests/defer/src/commonMain/graphql/noTypename/schema.graphqls diff --git a/.github/workflows/defer-with-apollo-server-tests.yml b/.github/workflows/defer-with-apollo-server-tests.yml deleted file mode 100644 index 8397a845919..00000000000 --- a/.github/workflows/defer-with-apollo-server-tests.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: defer-router-tests - -on: - schedule: - - cron: '0 3 * * *' -env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - -jobs: - defer-with-router-tests: - runs-on: ubuntu-latest - if: github.repository == 'apollographql/apollo-kotlin' - steps: - - name: Checkout project - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 - - - name: Install and run graph - working-directory: tests/defer/apollo-server/ - run: | - npm install --legacy-peer-deps - npx patch-package - APOLLO_PORT=4000 npm start & - - - name: Setup Java - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1 - with: - distribution: 'temurin' - java-version: 17 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2 - - - name: Run Apollo Kotlin @defer tests - env: - DEFER_WITH_APOLLO_SERVER_TESTS: true - run: | - ./gradlew --no-daemon --console plain -p tests :defer:allTests diff --git a/.github/workflows/defer-with-router-tests.yml b/.github/workflows/defer-with-router-tests.yml index ce6d49072ff..4088364734c 100644 --- a/.github/workflows/defer-with-router-tests.yml +++ b/.github/workflows/defer-with-router-tests.yml @@ -30,3 +30,31 @@ jobs: DEFER_WITH_ROUTER_TESTS: true run: | ./gradlew --no-daemon --console plain -p tests :defer:allTests + defer-with-apollo-server-tests: + runs-on: ubuntu-latest + if: github.repository == 'apollographql/apollo-kotlin' + steps: + - name: Checkout project + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 + + - name: Install and run graph + working-directory: tests/defer/apollo-server/ + run: | + npm install --legacy-peer-deps + npx patch-package + APOLLO_PORT=4000 npm start & + + - name: Setup Java + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1 + with: + distribution: 'temurin' + java-version: 17 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2 + + - name: Run Apollo Kotlin @defer tests + env: + DEFER_WITH_APOLLO_SERVER_TESTS: true + run: | + ./gradlew --no-daemon --console plain -p tests :defer:allTests diff --git a/tests/defer/build.gradle.kts b/tests/defer/build.gradle.kts index 3ce2eb8231f..0936f857a3d 100644 --- a/tests/defer/build.gradle.kts +++ b/tests/defer/build.gradle.kts @@ -44,6 +44,14 @@ fun configureApollo(generateKotlinModels: Boolean) { } } +apollo { + service("noTypename") { + packageName.set("defer.notypename") + srcDir("src/commonMain/graphql/noTypename") + addTypename.set("ifPolymorphic") + } +} + configureApollo(true) if (System.getProperty("idea.sync.active") == null) { registerJavaCodegenTestTask() @@ -61,19 +69,15 @@ fun com.apollographql.apollo.gradle.api.Service.configureConnection(generateKotl } tasks.withType(AbstractTestTask::class.java) { - // Run the defer with Router tests only from a specific CI job + // Run the defer with Router and defer with Apollo Server tests only from a specific CI job val runDeferWithRouterTests = System.getenv("DEFER_WITH_ROUTER_TESTS").toBoolean() - if (runDeferWithRouterTests) { - filter.setIncludePatterns("test.DeferWithRouterTest") - } else { - filter.setExcludePatterns("test.DeferWithRouterTest") - } - - // Run the defer with Apollo Server tests only from a specific CI job val runDeferWithApolloServerTests = System.getenv("DEFER_WITH_APOLLO_SERVER_TESTS").toBoolean() - if (runDeferWithApolloServerTests) { - filter.setIncludePatterns("test.DeferWithApolloServerTest") - } else { - filter.setExcludePatterns("test.DeferWithApolloServerTest") - } + filter.setIncludePatterns(*buildList { + if (runDeferWithRouterTests) add("test.DeferWithRouterTest") + if (runDeferWithApolloServerTests) add("test.DeferWithApolloServerTest") + }.toTypedArray()) + filter.setExcludePatterns(*buildList { + if (!runDeferWithRouterTests) add("test.DeferWithRouterTest") + if (!runDeferWithApolloServerTests) add("test.DeferWithApolloServerTest") + }.toTypedArray()) } diff --git a/tests/defer/src/commonMain/graphql/base/operation.graphql b/tests/defer/src/commonMain/graphql/base/operation.graphql index 3f6c144b4a2..e89921fd895 100644 --- a/tests/defer/src/commonMain/graphql/base/operation.graphql +++ b/tests/defer/src/commonMain/graphql/base/operation.graphql @@ -168,3 +168,46 @@ query HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery { fragment ComputerIdField on Computer { id } + +query OverlappingQuery { + computer(id: "Computer1") { + id + ... on Computer @defer(label: "a") { + id + ... on Computer @defer(label: "b") { + id + cpu + year + } + } + } +} + +query Overlapping2Query { + computer(id: "Computer1") { + id + ... on Computer @defer(label: "a") { + id + } + ... on Computer @defer(label: "b") { + id + cpu + year + } + } +} + +query SubPathQuery { + computer(id: "Computer1") { + id + } + ... on Query @defer(label: "a") { + MyFragment: __typename + computer(id: "Computer1") { + id + screen { + isColor + } + } + } +} diff --git a/tests/defer/src/commonMain/graphql/noTypename/operation.graphql b/tests/defer/src/commonMain/graphql/noTypename/operation.graphql new file mode 100644 index 00000000000..90ca0b72dda --- /dev/null +++ b/tests/defer/src/commonMain/graphql/noTypename/operation.graphql @@ -0,0 +1,11 @@ +query SkippingEmptyFragmentQuery { + computer(id: "Computer1") { + ... on Computer @defer(label: "a") { + ... on Computer @defer(label: "b") { + ... on Computer @defer(label: "c") { + id + } + } + } + } +} diff --git a/tests/defer/src/commonMain/graphql/noTypename/schema.graphqls b/tests/defer/src/commonMain/graphql/noTypename/schema.graphqls new file mode 100644 index 00000000000..0892f0f4075 --- /dev/null +++ b/tests/defer/src/commonMain/graphql/noTypename/schema.graphqls @@ -0,0 +1,31 @@ +type Query { + computers: [Computer!]! + computer(id: ID!): Computer +} + +type Mutation { + computers: [Computer!]! +} + +type Subscription { + count(to: Int!): Counter! +} + +type Counter { + value: Int! + valueTimesTwo: Int! +} + +type Computer { + id: ID! + cpu: String! + year: Int! + screen: Screen! + errorField: String + nonNullErrorField: String! +} + +type Screen { + resolution: String! + isColor: Boolean! +} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index b04a788138b..c6ddf98bcdd 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -14,6 +14,9 @@ import defer.DoesNotDisableDeferWithNullIfArgumentQuery import defer.HandlesErrorsThrownInDeferredFragmentsQuery import defer.HandlesNonNullableErrorsThrownInDeferredFragmentsQuery import defer.HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery +import defer.Overlapping2Query +import defer.OverlappingQuery +import defer.SubPathQuery import defer.WithFragmentSpreadsMutation import defer.WithFragmentSpreadsQuery import defer.WithInlineFragmentsQuery @@ -21,6 +24,7 @@ import defer.fragment.ComputerErrorField import defer.fragment.ComputerFields import defer.fragment.FragmentOnQuery import defer.fragment.ScreenFields +import defer.notypename.SkippingEmptyFragmentQuery import kotlinx.coroutines.flow.toList import kotlin.test.Test import kotlin.test.assertEquals @@ -357,4 +361,176 @@ class DeferWithApolloServerTest { val actualResponseList = apolloClient.query(query).toFlow().toList() assertResponseListEquals(expectedDataList, actualResponseList) } + + @Test + fun overlapping() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"__typename":"Computer","id":"Computer1"}},"pending":[{"id":"0","path":["computer"],"label":"b"}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"cpu":"386","year":1993},"id":"0"}],"completed":[{"id":"0"}]} + val query = OverlappingQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + OverlappingQuery.Data( + OverlappingQuery.Computer( + "Computer", "Computer1", OverlappingQuery.OnComputer( + "Computer", "Computer1", null, + ) + ) + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + OverlappingQuery.Data( + OverlappingQuery.Computer( + "Computer", "Computer1", OverlappingQuery.OnComputer( + "Computer", "Computer1", OverlappingQuery.OnComputer1("Computer1", "386", 1993) + ) + ) + ) + ) + .build() + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun overlapping2() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{"__typename":"Computer","id":"Computer1"}},"pending":[{"id":"0","path":["computer"],"label":"b"}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"cpu":"386","year":1993},"id":"0"}],"completed":[{"id":"0"}]} + val query = Overlapping2Query() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + Overlapping2Query.Data( + Overlapping2Query.Computer( + "Computer", "Computer1", Overlapping2Query.OnComputerDeferA("Computer1" + ), null + ) + ) + ) + .build(), + ApolloResponse.Builder( + query, + uuid, + ).data( + Overlapping2Query.Data( + Overlapping2Query.Computer( + "Computer", "Computer1", Overlapping2Query.OnComputerDeferA("Computer1" + ), Overlapping2Query.OnComputerDeferB( + "Computer1", "386", 1993 + ) + ) + ) + ) + .build() + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun subPath() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"__typename":"Query","computer":{"id":"Computer1"}},"pending":[{"id":"0","path":[],"label":"a"}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"screen":{"isColor":false}},"id":"0","subPath":["computer"]},{"data":{"MyFragment":"Query"},"id":"0"}],"completed":[{"id":"0"}]} + val query = SubPathQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + SubPathQuery.Data( + "Query", SubPathQuery.Computer( + "Computer1" + ), null + ) + ) + .build(), + ApolloResponse.Builder( + query, + uuid, + ).data( + SubPathQuery.Data( + "Query", SubPathQuery.Computer( + "Computer1" + ), SubPathQuery.OnQuery( + "Query", SubPathQuery.Computer1( + "Computer1", + SubPathQuery.Screen(false + ) + ) + ) + ) + ) + .build() + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun skippingEmptyFragment() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computer":{}},"pending":[{"id":"0","path":["computer"],"label":"c"}],"hasNext":true} + // {"hasNext":false,"incremental":[{"data":{"id":"Computer1"},"id":"0"}],"completed":[{"id":"0"}]} + val query = SkippingEmptyFragmentQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + SkippingEmptyFragmentQuery.Data( + SkippingEmptyFragmentQuery.Computer( + SkippingEmptyFragmentQuery.OnComputer( + SkippingEmptyFragmentQuery.OnComputer1( + null + ) + ) + ) + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + SkippingEmptyFragmentQuery.Data( + SkippingEmptyFragmentQuery.Computer( + SkippingEmptyFragmentQuery.OnComputer( + SkippingEmptyFragmentQuery.OnComputer1( + SkippingEmptyFragmentQuery.OnComputer2( + "Computer1" + ) + ) + ) + ) + ) + ) + .build() + ) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + } From 6bb062d28780ef940147157f1dddfc72e725a18d Mon Sep 17 00:00:00 2001 From: BoD Date: Tue, 17 Dec 2024 16:08:43 +0100 Subject: [PATCH 06/38] Fix missed test --- .../src/jvmTest/kotlin/test/DeferJvmTest.kt | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt index 08d61e2f807..a300ee18e93 100644 --- a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt +++ b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt @@ -2,13 +2,13 @@ package test import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.http.DefaultHttpRequestComposer -import com.apollographql.mockserver.MockServer -import com.apollographql.mockserver.enqueueMultipart import com.apollographql.apollo.mpp.currentTimeMillis import com.apollographql.apollo.network.http.DefaultHttpEngine import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.testing.awaitElement import com.apollographql.apollo.testing.internal.runTest +import com.apollographql.mockserver.MockServer +import com.apollographql.mockserver.enqueueMultipart import defer.WithFragmentSpreadsQuery import defer.fragment.ComputerFields import defer.fragment.ScreenFields @@ -22,7 +22,6 @@ import kotlinx.coroutines.runBlocking import okhttp3.Cache import okhttp3.OkHttpClient import okio.ByteString.Companion.encodeUtf8 -import okio.Path.Companion.toPath import org.junit.Ignore import org.junit.Test import supergraph.ProductQuery @@ -73,11 +72,8 @@ class DeferJvmTest { } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental":[{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental":[{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) for ((index, json) in jsonList.withIndex()) { @@ -102,9 +98,7 @@ class DeferJvmTest { ) ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) + ComputerFields.Screen("Screen", "800x600", ScreenFields(true)) ) ), ) From 891569fae6cc3fc25e4c73609961b3104ca80835 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 18 Jul 2025 15:59:10 +0200 Subject: [PATCH 07/38] Support appending lists in DeferredJsonMerger (for @stream) --- .../apollo/internal/DeferredJsonMerger.kt | 31 +- .../test/defer/DeferredJsonMergerTest.kt | 2980 +++++++++++------ tests/defer/apollo-server/computers.graphqls | 7 + tests/defer/apollo-server/computers.js | 16 +- tests/defer/build.gradle.kts | 2 +- .../commonMain/graphql/base/operation.graphql | 13 + .../commonMain/graphql/base/schema.graphqls | 7 + .../kotlin/test/DeferWithApolloServerTest.kt | 116 + 8 files changed, 2052 insertions(+), 1120 deletions(-) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt index c0d0e0d1d09..47a6fdb4204 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt @@ -114,22 +114,35 @@ class DeferredJsonMerger { } private fun mergeIncrementalData(incrementalItem: JsonMap) { - val id = incrementalItem["id"] as String? ?: error("No id found in incremental item") - val data = incrementalItem["data"] as JsonMap? ?: error("No data found in incremental item") + val id = incrementalItem["id"] as String? ?: error("No id found in incremental result") + val data = incrementalItem["data"] as JsonMap? + val items = incrementalItem["items"] as List? val subPath = incrementalItem["subPath"] as List? ?: emptyList() val path = (_pendingFragmentIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath val mergedData = merged["data"] as JsonMap - val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap - deepMerge(nodeToMergeInto, data) + val nodeToMergeInto = nodeAtPath(mergedData, path) + when { + data != null -> { + deepMergeObject(nodeToMergeInto as MutableJsonMap, data) + } + + items != null -> { + mergeList(nodeToMergeInto as MutableList, items) + } + + else -> { + error("Neither data nor items found in incremental result") + } + } } - private fun deepMerge(destination: MutableJsonMap, map: JsonMap) { - for ((key, value) in map) { + private fun deepMergeObject(destination: MutableJsonMap, obj: JsonMap) { + for ((key, value) in obj) { if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { // Objects: merge recursively val fieldDestination = destination[key] as MutableJsonMap val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map") - deepMerge(destination = fieldDestination, map = fieldMap) + deepMergeObject(destination = fieldDestination, obj = fieldMap) } else { // Other types: add / overwrite destination[key] = value @@ -137,6 +150,10 @@ class DeferredJsonMerger { } } + private fun mergeList(destination: MutableList, items: List) { + destination.addAll(items) + } + private fun jsonToMap(json: BufferedSource): JsonMap = BufferedSourceJsonReader(json).readAny() as JsonMap diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt index c0a9c4cf480..f1c189ad919 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt @@ -1,5 +1,8 @@ +@file:OptIn(ApolloInternal::class) + package test.defer +import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny @@ -22,57 +25,57 @@ class DeferredJsonMergerTest { //language=JSON val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true } - ] - }, - "pending": [ + }, { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" + "id": "Computer2", + "screen": { + "isTouch": false + } } - ], - "hasNext": true - } - """ + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true } - ] - } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -84,73 +87,73 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "id": "0" - } - ], - "completed": [ - { - "id": "0" - } - ], - "pending": [ - { - "id": "1", - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0" - } - ], - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - }, - "hasNext": true - } - """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + } + ], + "completed": [ + { + "id": "0" + } + ], + "pending": [ + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false } - ] - }, - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" } + ] + }, + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" } - } - """ + } + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -162,77 +165,77 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "incremental": [ - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } - }, - "id": "1" - } - ], - "completed": [ - { - "id": "1" - } - ], - "pending": [ - { - "id": "2", - "path": [ - "computers", - 0, - "screen" - ], - "label": "fragment:ComputerFields:0" - } - ], - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } - }, - "hasNext": true - } - """ + { + "incremental": [ + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "id": "1" + } + ], + "completed": [ + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", + "path": [ + "computers", + 0, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" } - ] - }, - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" } + ] + }, + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -244,93 +247,93 @@ class DeferredJsonMergerTest { //language=JSON val payload4 = """ - { - "completed": [ + { + "completed": [ + { + "id": "2", + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ] + } + ], + "pending": [ + { + "id": "3", + "path": [ + "computers", + 1, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4 = """ + { + "data": { + "computers": [ { - "id": "2", - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ] - } - ], - "pending": [ + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, { - "id": "3", - "path": [ - "computers", - 1, - "screen" - ], - "label": "fragment:ComputerFields:0" + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } } - ], - "hasNext": true - } - """ - //language=JSON - val mergedPayloads_1_2_3_4 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, + ] + }, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } + "line": 12, + "column": 11 } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" ] - }, - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } + } + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload4.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) assertEquals( @@ -343,102 +346,102 @@ class DeferredJsonMergerTest { //language=JSON val payload5 = """ - { - "incremental": [ - { - "data": { - "isColor": false - }, - "id": "3", - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ] - } - ], - "completed": [ - { - "id": "3" - } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - }, - "hasNext": false - } - """ - //language=JSON - val mergedPayloads_1_2_3_4_5 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, + { + "incremental": [ + { + "data": { + "isColor": false + }, + "id": "3", + "errors": [ { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480", - "isColor": false - } + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] } ] - }, - "errors": [ + } + ], + "completed": [ + { + "id": "3" + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } }, { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } } + ] + }, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + }, + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload5.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) assertEquals( @@ -455,65 +458,65 @@ class DeferredJsonMergerTest { //language=JSON val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "pending": [ + { + "data": { + "computers": [ { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" + "id": "Computer1", + "screen": { + "isTouch": true + } }, { - "id": "1", - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0" + "id": "Computer2", + "screen": { + "isTouch": false + } } - ], - "hasNext": true - } - """ + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + }, + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true } - ] - } - } - """ + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + } + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -526,99 +529,99 @@ class DeferredJsonMergerTest { //language=JSON val payload2_3 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "id": "0" - }, - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } - }, - "id": "1" - } - ], - "completed": [ - { - "id": "0" + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } }, - { - "id": "1" - } - ], - "pending": [ - { - "id": "2", - "path": [ - "computers", - 0, - "screen" - ], - "label": "fragment:ComputerFields:0" + "id": "0" + }, + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } }, - { - "id": "3", - "path": [ - "computers", - 1, - "screen" - ], - "label": "fragment:ComputerFields:0" - } - ], - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } + "id": "1" + } + ], + "completed": [ + { + "id": "0" }, - "hasNext": true - } - """ + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", + "path": [ + "computers", + 0, + "screen" + ], + "label": "fragment:ComputerFields:0" + }, + { + "id": "3", + "path": [ + "computers", + 1, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" } - ] - }, - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" } + ] + }, + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2_3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -631,122 +634,122 @@ class DeferredJsonMergerTest { //language=JSON val payload4_5 = """ - { - "incremental": [ - { - "data": { - "isColor": false - }, - "id": "3", - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ] - } - ], - "completed": [ - { - "id": "2", - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ] + { + "incremental": [ + { + "data": { + "isColor": false }, - { - "id": "3" - } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - }, - "hasNext": false - } - """ - //language=JSON - val mergedPayloads_1_2_3_4_5 = """ - { - "data": { - "computers": [ + "id": "3", + "errors": [ { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ] + } + ], + "completed": [ + { + "id": "2", + "errors": [ { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480", - "isColor": false - } + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] } ] }, - "errors": [ + { + "id": "3" + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } }, { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } } + ] + }, + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + }, + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload4_5.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) assertEquals( @@ -763,82 +766,82 @@ class DeferredJsonMergerTest { //language=JSON val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "pending": [ + { + "data": { + "computers": [ { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" + "id": "Computer1", + "screen": { + "isTouch": true + } }, { - "id": "1", - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0" + "id": "Computer2", + "screen": { + "isTouch": false + } } - ], - "hasNext": true - } - """ + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + }, + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertFalse(deferredJsonMerger.isEmptyPayload) //language=JSON val payload2 = """ - { - "hasNext": true - } - """ + { + "hasNext": true + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertTrue(deferredJsonMerger.isEmptyPayload) //language=JSON val payload3 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "id": "0" - } - ], - "hasNext": true - } - """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertFalse(deferredJsonMerger.isEmptyPayload) //language=JSON val payload4 = """ - { - "hasNext": false - } - """ + { + "hasNext": false + } + """.trimIndent() deferredJsonMerger.merge(payload4.buffer()) assertTrue(deferredJsonMerger.isEmptyPayload) } @@ -851,38 +854,49 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { "h": "h", "i": "i" } + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i" } } - }, - "pending": [{ "path": [], "id": "0" }], - "hasNext": true - } - """ + } + }, + "pending": [ + { + "path": [], + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { "h": "h", "i": "i" } + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -894,32 +908,55 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { "id": "0", "data": { "MyFragment": "Query" } }, - { "id": "0", "subPath": ["f2", "c", "f"], "data": { "j": "j" } } - ], - "completed": [{ "id": "0" }], - "hasNext": false - } - """ + { + "incremental": [ + { + "id": "0", + "data": { + "MyFragment": "Query" + } + }, + { + "id": "0", + "subPath": [ + "f2", + "c", + "f" + ], + "data": { + "j": "j" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { "h": "h", "i": "i", "j": "j" } + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i", + "j": "j" } - }, - "MyFragment": "Query" - } + } + }, + "MyFragment": "Query" } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -936,35 +973,50 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": {"f2": {"a": "A", "b": "B", "c": { - "d": "D", "e": "E", "f": { - "h": "H", "i": "I" + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I" + } } - }}}, - "pending": [{"id": "0", "path": [], "label": "D1"}], - "hasNext": true - } - """ + } + }, + "pending": [ + { + "id": "0", + "path": [], + "label": "D1" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I" - } + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -976,38 +1028,61 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - {"id": "0", "subPath": ["f2", "c", "f"], "data": {"j": "J", "k": "K"}} - ], - "pending": [{"id": "1", "path": ["f2", "c", "f"], "label": "D2"}], - "completed": [ - {"id": "0"} - ], - "hasNext": true - } - """ + { + "incremental": [ + { + "id": "0", + "subPath": [ + "f2", + "c", + "f" + ], + "data": { + "j": "J", + "k": "K" + } + } + ], + "pending": [ + { + "id": "1", + "path": [ + "f2", + "c", + "f" + ], + "label": "D2" + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I", - "j": "J", - "k": "K" - } + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1019,40 +1094,48 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "incremental": [ - {"id": "1", "data": {"l": "L", "m": "M"}} - ], - "completed": [ - {"id": "1"} - ], - "hasNext": false - } - """ + { + "incremental": [ + { + "id": "1", + "data": { + "l": "L", + "m": "M" + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I", - "j": "J", - "k": "K", - "l": "L", - "m": "M" - } + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K", + "l": "L", + "m": "M" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -1069,32 +1152,49 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "a": { "b": { "c": { "d": "d" } } } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + }, + "pending": [ + { + "path": [], + "id": "0", + "label": "Blue" }, - "pending": [ - { "path": [], "id": "0", "label": "Blue" }, - { "path": ["a", "b"], "id": "1", "label": "Red" } - ], - "hasNext": true - } - """ + { + "path": [ + "a", + "b" + ], + "id": "1", + "label": "Red" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1107,33 +1207,49 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { "id": "1", "data": { "potentiallySlowFieldA": "potentiallySlowFieldA" } }, - { "id": "1", "data": { "e": { "f": "f" } } } - ], - "completed": [{ "id": "1" }], - "hasNext": true - } - """ + { + "incremental": [ + { + "id": "1", + "data": { + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + { + "id": "1", + "data": { + "e": { + "f": "f" + } + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1145,36 +1261,48 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "incremental": [ - { "id": "0", "data": { "g": { "h": "h" }, "potentiallySlowFieldB": "potentiallySlowFieldB" } } - ], - "completed": [{ "id": "0" }], - "hasNext": false - } - """ + { + "incremental": [ + { + "id": "0", + "data": { + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -1191,32 +1319,49 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "a": { "b": { "c": { "d": "d" } } } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + }, + "pending": [ + { + "path": [], + "id": "0", + "label": "Blue" }, - "pending": [ - { "path": [], "id": "0", "label": "Blue" }, - { "path": ["a", "b"], "id": "1", "label": "Red" } - ], - "hasNext": true - } - """ + { + "path": [ + "a", + "b" + ], + "id": "1", + "label": "Red" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1229,36 +1374,55 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { "id": "0", "data": { "g": { "h": "h" }, "potentiallySlowFieldB": "potentiallySlowFieldB" } }, - { "id": "1", "data": { "e": { "f": "f" } } } - ], - "completed": [{ "id": "0" }], - "hasNext": true - } - """ + { + "incremental": [ + { + "id": "0", + "data": { + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + }, + { + "id": "1", + "data": { + "e": { + "f": "f" + } + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1272,13 +1436,21 @@ class DeferredJsonMergerTest { val payload3 = """ { "incremental": [ - { "id": "1", "data": { "potentiallySlowFieldA": "potentiallySlowFieldA" } } + { + "id": "1", + "data": { + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + } + ], + "completed": [ + { + "id": "1" + } ], - "completed": [{ "id": "1" }], "hasNext": false } - """ - //language=JSON + """.trimIndent() val mergedPayloads_1_2_3 = """ { "data": { @@ -1316,23 +1488,33 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { "me": {} }, - "pending": [ - { "path": [], "id": "0" }, - { "path": ["me"], "id": "1" } - ], - "hasNext": true - } - """ + { + "data": { + "me": {} + }, + "pending": [ + { + "path": [], + "id": "0" + }, + { + "path": [ + "me" + ], + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "me": {} - } + { + "data": { + "me": {} } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1345,34 +1527,92 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { - "id": "1", - "data": { "list": [{ "item": {} }, { "item": {} }, { "item": {} }] } - }, - { "id": "1", "subPath": ["list", 0, "item"], "data": { "id": "1" } }, - { "id": "1", "subPath": ["list", 1, "item"], "data": { "id": "2" } }, - { "id": "1", "subPath": ["list", 2, "item"], "data": { "id": "3" } } - ], - "completed": [{ "id": "1" }], - "hasNext": true - } - """ - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { + { + "incremental": [ + { + "id": "1", + "data": { "list": [ - { "item": { "id": "1" } }, - { "item": { "id": "2" } }, - { "item": { "id": "3" } } + { + "item": {} + }, + { + "item": {} + }, + { + "item": {} + } ] } + }, + { + "id": "1", + "subPath": [ + "list", + 0, + "item" + ], + "data": { + "id": "1" + } + }, + { + "id": "1", + "subPath": [ + "list", + 1, + "item" + ], + "data": { + "id": "2" + } + }, + { + "id": "1", + "subPath": [ + "list", + 2, + "item" + ], + "data": { + "id": "3" + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "list": [ + { + "item": { + "id": "1" + } + }, + { + "item": { + "id": "2" + } + }, + { + "item": { + "id": "3" + } + } + ] } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1384,30 +1624,82 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "incremental": [ - { "id": "0", "subPath": ["me", "list", 0, "item"], "data": { "value": "Foo" } }, - { "id": "0", "subPath": ["me", "list", 1, "item"], "data": { "value": "Bar" } }, - { "id": "0", "subPath": ["me", "list", 2, "item"], "data": { "value": "Baz" } } - ], - "completed": [{ "id": "0" }], - "hasNext": false - } - """ + { + "incremental": [ + { + "id": "0", + "subPath": [ + "me", + "list", + 0, + "item" + ], + "data": { + "value": "Foo" + } + }, + { + "id": "0", + "subPath": [ + "me", + "list", + 1, + "item" + ], + "data": { + "value": "Bar" + } + }, + { + "id": "0", + "subPath": [ + "me", + "list", + 2, + "item" + ], + "data": { + "value": "Baz" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "list": [ - { "item": { "id": "1", "value": "Foo" } }, - { "item": { "id": "2", "value": "Bar" } }, - { "item": { "id": "3", "value": "Baz" } } - ] - } + { + "data": { + "me": { + "list": [ + { + "item": { + "id": "1", + "value": "Foo" + } + }, + { + "item": { + "id": "2", + "value": "Bar" + } + }, + { + "item": { + "id": "3", + "value": "Baz" + } + } + ] } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -1424,24 +1716,30 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "me": {} - }, - "pending": [ - {"id": "0", "path": ["me"], "label": "B"} - ], - "hasNext": true - } - """ + { + "data": { + "me": {} + }, + "pending": [ + { + "id": "0", + "path": [ + "me" + ], + "label": "B" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "me": {} - } + { + "data": { + "me": {} } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1453,27 +1751,35 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - {"id":"0" , "data": {"a": "A", "b": "B"}} - ], - "completed": [ - {"id": "0"} - ], - "hasNext": false - } - """ - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { + { + "incremental": [ + { + "id": "0", + "data": { "a": "A", "b": "B" } } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "a": "A", + "b": "B" + } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1490,33 +1796,53 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [{ "name": "My Project" }] - } + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ] + } + }, + "pending": [ + { + "id": "0", + "path": [ + "me" + ], + "label": "Billing" }, - "pending": [ - { "id": "0", "path": ["me"], "label": "Billing" }, - { "id": "1", "path": ["me"], "label": "Prev" } - ], - "hasNext": true - } - """ + { + "id": "1", + "path": [ + "me" + ], + "label": "Prev" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [{ "name": "My Project" }] - } + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ] } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1529,36 +1855,44 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "tier": "BRONZE", - "renewalDate": "2023-03-20", - "latestInvoiceTotal": "${'$'}12.34" - } - } - ], - "completed": [{ "id": "0" }], - "hasNext": true - } - """ - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [{ "name": "My Project" }], + { + "incremental": [ + { + "id": "0", + "data": { "tier": "BRONZE", "renewalDate": "2023-03-20", "latestInvoiceTotal": "${'$'}12.34" } } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34" + } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1570,33 +1904,51 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "incremental": [ - { - "id": "1", - "data": { "previousInvoices": [{ "name": "My Invoice" }] } + { + "incremental": [ + { + "id": "1", + "data": { + "previousInvoices": [ + { + "name": "My Invoice" + } + ] } - ], - "completed": [{ "id": "1" }], - "hasNext": false - } - """ + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [{ "name": "My Project" }], - "tier": "BRONZE", - "renewalDate": "2023-03-20", - "latestInvoiceTotal": "${'$'}12.34", - "previousInvoices": [{ "name": "My Invoice" }] - } + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34", + "previousInvoices": [ + { + "name": "My Invoice" + } + ] } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( @@ -1613,25 +1965,35 @@ class DeferredJsonMergerTest { val deferredJsonMerger = DeferredJsonMerger() //language=JSON val payload1 = """ - { - "data": { - "me": {} + { + "data": { + "me": {} + }, + "pending": [ + { + "id": "0", + "path": [], + "label": "A" }, - "pending": [ - {"id": "0", "path": [], "label": "A"}, - {"id": "1", "path": ["me"], "label": "B"} - ], - "hasNext": true - } - """ + { + "id": "1", + "path": [ + "me" + ], + "label": "B" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1 = """ - { - "data": { - "me": {} - } + { + "data": { + "me": {} } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) assertEquals( @@ -1644,41 +2006,53 @@ class DeferredJsonMergerTest { //language=JSON val payload2 = """ - { - "incremental": [ - { - "id": "0", - "subPath": ["me"], - "data": { "foo": { "bar": {} } } - }, - { - "id": "0", - "subPath": ["me", "foo", "bar"], - "data": { - "baz": "BAZ" + { + "incremental": [ + { + "id": "0", + "subPath": [ + "me" + ], + "data": { + "foo": { + "bar": {} } } - ], - "completed": [ - {"id": "0"} - ], - "hasNext": true - } - """ + }, + { + "id": "0", + "subPath": [ + "me", + "foo", + "bar" + ], + "data": { + "baz": "BAZ" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() //language=JSON val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "foo": { - "bar": { - "baz": "BAZ" - } + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" } } } } - """ + } + """.trimIndent() deferredJsonMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) assertEquals( @@ -1690,58 +2064,444 @@ class DeferredJsonMergerTest { //language=JSON val payload3 = """ - { - "completed": [ - { - "id": "1", - "errors": [ - { - "message": "Cannot return null for non-nullable field Bar.qux.", - "locations": [ - { - "line": 1, - "column": 1 - } - ], - "path": ["foo", "bar", "qux"] - } - ] - } - ], - "hasNext": false - } - """ + { + "completed": [ + { + "id": "1", + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": [ + "foo", + "bar", + "qux" + ] + } + ] + } + ], + "hasNext": false + } + """.trimIndent() //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "foo": { - "bar": { - "baz": "BAZ" - } + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" } } + } + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": [ + "foo", + "bar", + "qux" + ] + } + ] + } + """.trimIndent() + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + deferredJsonMerger.pendingFragmentIds + ) + } + + /** + * Example I from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleI() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + }, + "pending": [ + { + "id": "0", + "path": [ + "person" + ], + "label": "homeWorldDefer" }, - "errors": [ - { - "message": "Cannot return null for non-nullable field Bar.qux.", - "locations": [ - { - "line": 1, - "column": 1 - } - ], - "path": ["foo", "bar", "qux"] + { + "id": "1", + "path": [ + "person", + "films" + ], + "label": "filmsStream" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + } + } + """.trimIndent() + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "items": [ + { + "title": "Return of the Jedi" + } + ] + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } + } + } + """.trimIndent() + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } + } + } + """.trimIndent() + + deferredJsonMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload4 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "homeworld": { + "name": "Tatooine" + } } - ] + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "homeworld": { + "name": "Tatooine" + }, + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } } - """ + } + """.trimIndent() + + deferredJsonMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) + assertEquals( + setOf(), + deferredJsonMerger.pendingFragmentIds + ) + } + + /** + * Example J from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleJ() { + val deferredJsonMerger = DeferredJsonMerger() + //language=JSON + val payload1 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + } + ] + } + }, + "pending": [ + { + "id": "1", + "path": [ + "person", + "films" + ], + "label": "filmsStream" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + } + ] + } + } + } + """.trimIndent() + deferredJsonMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "items": [ + { + "title": "The Empire Strikes Back" + } + ] + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + } + } + """.trimIndent() + deferredJsonMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + deferredJsonMerger.pendingFragmentIds + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1", + "errors": [ + { + "message": "Cannot return null for non-nullable field Person.films.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "person", + "films" + ] + } + ] + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field Person.films.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "person", + "films" + ] + } + ] + } + """.trimIndent() + deferredJsonMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), deferredJsonMerger.pendingFragmentIds ) diff --git a/tests/defer/apollo-server/computers.graphqls b/tests/defer/apollo-server/computers.graphqls index 4c410b7fe7e..a1875342b39 100644 --- a/tests/defer/apollo-server/computers.graphqls +++ b/tests/defer/apollo-server/computers.graphqls @@ -14,6 +14,7 @@ type Computer { screen: Screen! errorField: String nonNullErrorField: String! + peripherals: [String!]! } type Screen { @@ -25,3 +26,9 @@ directive @defer( if: Boolean! = true label: String ) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @stream( + label: String + if: Boolean! = true + initialCount: Int = 0 +) on FIELD diff --git a/tests/defer/apollo-server/computers.js b/tests/defer/apollo-server/computers.js index d5cedcd16b0..77e673b7985 100644 --- a/tests/defer/apollo-server/computers.js +++ b/tests/defer/apollo-server/computers.js @@ -5,8 +5,20 @@ import {readFileSync} from 'fs'; const port = process.env.APOLLO_PORT || 4000; const computers = [ - {id: 'Computer1', cpu: "386", year: 1993, screen: {resolution: "640x480", isColor: false}}, - {id: 'Computer2', cpu: "486", year: 1996, screen: {resolution: "800x600", isColor: true}}, + { + id: 'Computer1', + cpu: "386", + year: 1993, + screen: {resolution: "640x480", isColor: false}, + peripherals: ["Keyboard", "Mouse", "Printer"], + }, + { + id: 'Computer2', + cpu: "486", + year: 1996, + screen: {resolution: "800x600", isColor: true}, + peripherals: ["Keyboard", "Mouse", "Printer", "Scanner"], + }, ] const typeDefs = readFileSync('./computers.graphqls', {encoding: 'utf-8'}); diff --git a/tests/defer/build.gradle.kts b/tests/defer/build.gradle.kts index 0936f857a3d..14b0fb6fa3b 100644 --- a/tests/defer/build.gradle.kts +++ b/tests/defer/build.gradle.kts @@ -71,7 +71,7 @@ fun com.apollographql.apollo.gradle.api.Service.configureConnection(generateKotl tasks.withType(AbstractTestTask::class.java) { // Run the defer with Router and defer with Apollo Server tests only from a specific CI job val runDeferWithRouterTests = System.getenv("DEFER_WITH_ROUTER_TESTS").toBoolean() - val runDeferWithApolloServerTests = System.getenv("DEFER_WITH_APOLLO_SERVER_TESTS").toBoolean() + val runDeferWithApolloServerTests = true filter.setIncludePatterns(*buildList { if (runDeferWithRouterTests) add("test.DeferWithRouterTest") if (runDeferWithApolloServerTests) add("test.DeferWithApolloServerTest") diff --git a/tests/defer/src/commonMain/graphql/base/operation.graphql b/tests/defer/src/commonMain/graphql/base/operation.graphql index e89921fd895..c3b9b8d57b9 100644 --- a/tests/defer/src/commonMain/graphql/base/operation.graphql +++ b/tests/defer/src/commonMain/graphql/base/operation.graphql @@ -211,3 +211,16 @@ query SubPathQuery { } } } + +query SimpleStreamQuery($initialCount: Int!) { + computers @stream(initialCount: $initialCount) { + id + } +} + +query NestedStreamQuery($initialCount: Int!) { + computers @stream(initialCount: $initialCount) { + id + peripherals @stream(initialCount: $initialCount) + } +} diff --git a/tests/defer/src/commonMain/graphql/base/schema.graphqls b/tests/defer/src/commonMain/graphql/base/schema.graphqls index 0892f0f4075..9de38fc9a65 100644 --- a/tests/defer/src/commonMain/graphql/base/schema.graphqls +++ b/tests/defer/src/commonMain/graphql/base/schema.graphqls @@ -23,9 +23,16 @@ type Computer { screen: Screen! errorField: String nonNullErrorField: String! + peripherals: [String!]! } type Screen { resolution: String! isColor: Boolean! } + +directive @stream( + label: String + if: Boolean! = true + initialCount: Int = 0 +) on FIELD diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index c6ddf98bcdd..1746e941c95 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -14,8 +14,10 @@ import defer.DoesNotDisableDeferWithNullIfArgumentQuery import defer.HandlesErrorsThrownInDeferredFragmentsQuery import defer.HandlesNonNullableErrorsThrownInDeferredFragmentsQuery import defer.HandlesNonNullableErrorsThrownOutsideDeferredFragmentsQuery +import defer.NestedStreamQuery import defer.Overlapping2Query import defer.OverlappingQuery +import defer.SimpleStreamQuery import defer.SubPathQuery import defer.WithFragmentSpreadsMutation import defer.WithFragmentSpreadsQuery @@ -532,5 +534,119 @@ class DeferWithApolloServerTest { assertResponseListEquals(expectedDataList, actualResponseList) } + @Test + fun simpleStream0Initial() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[]},"pending":[{"id":"0","path":["computers"]}],"hasNext":true} + // {"hasNext":false,"incremental":[{"id":"0","items":[{"id":"Computer1"},{"id":"Computer2"}]}],"completed":[{"id":"0"}]} + val query = SimpleStreamQuery(0) + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + SimpleStreamQuery.Data( + listOf() + ) + ) + .build(), + + + ApolloResponse.Builder( + query, + uuid, + ).data( + SimpleStreamQuery.Data( + listOf( + SimpleStreamQuery.Computer("Computer1"), + SimpleStreamQuery.Computer("Computer2"), + ) + ) + ) + .build() + ) + + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun simpleStream1Initial() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"id":"Computer1"}]},"pending":[{"id":"0","path":["computers"]}],"hasNext":true} + // {"hasNext":false,"incremental":[{"id":"0","items":[{"id":"Computer2"}]}],"completed":[{"id":"0"}]} + val query = SimpleStreamQuery(1) + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + SimpleStreamQuery.Data( + listOf( + SimpleStreamQuery.Computer("Computer1"), + ) + ) + ) + .build(), + ApolloResponse.Builder( + query, + uuid, + ).data( + SimpleStreamQuery.Data( + listOf( + SimpleStreamQuery.Computer("Computer1"), + SimpleStreamQuery.Computer("Computer2"), + ) + ) + ) + .build() + ) + + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun nestedStream() = runTest(before = { setUp() }, after = { tearDown() }) { + // Expected payloads: + // {"data":{"computers":[{"id":"Computer1","peripherals":["Keyboard"]}]},"pending":[{"id":"0","path":["computers",0,"peripherals"]},{"id":"1","path":["computers"]}],"hasNext":true} + // {"hasNext":false,"pending":[{"id":"2","path":["computers",1,"peripherals"]}],"incremental":[{"id":"0","items":["Mouse","Printer"]},{"id":"1","items":[{"id":"Computer2","peripherals":["Keyboard"]}]},{"id":"2","items":["Mouse","Printer","Scanner"]}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"}]} + val query = NestedStreamQuery(1) + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data( + NestedStreamQuery.Data( + listOf( + NestedStreamQuery.Computer("Computer1", listOf("Keyboard")) + ) + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + NestedStreamQuery.Data( + listOf( + NestedStreamQuery.Computer("Computer1", listOf("Keyboard", "Mouse", "Printer")), + NestedStreamQuery.Computer("Computer2", listOf("Keyboard", "Mouse", "Printer", "Scanner")) + ) + ) + ) + .build() + ) + + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } } From d5c8cb9d52234c67f19240ea8ceff745fab1cdb3 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 18 Jul 2025 18:24:58 +0200 Subject: [PATCH 08/38] Rename DeferredJsonMerger -> IncrementalResultsMerger and DeferredFragmentIdentifier -> IncrementalResultIdentifier --- libraries/apollo-api/api/apollo-api.api | 1 + libraries/apollo-api/api/apollo-api.klib.api | 1 + .../apollo/api/BooleanExpression.kt | 13 +- .../apollo/api/CustomScalarAdapters.kt | 19 +- .../apollographql/apollo/api/Executables.kt | 8 +- ...fier.kt => IncrementalResultIdentifier.kt} | 6 + .../apollographql/apollo/api/Operations.kt | 8 +- .../apollo/api/internal/ResponseParser.kt | 12 +- .../apollo/internal/DeferredJsonMerger.kt | 187 ---------- .../internal/IncrementalResultsMerger.kt | 186 ++++++++++ .../network/http/HttpNetworkTransport.kt | 19 +- .../websocket/WebSocketNetworkTransport.kt | 14 +- .../network/ws/WebSocketNetworkTransport.kt | 18 +- ...est.kt => IncrementalResultsMergerTest.kt} | 348 +++++++++--------- 14 files changed, 427 insertions(+), 413 deletions(-) rename libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/{DeferredFragmentIdentifier.kt => IncrementalResultIdentifier.kt} (54%) delete mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt rename libraries/apollo-runtime/src/commonTest/kotlin/test/defer/{DeferredJsonMergerTest.kt => IncrementalResultsMergerTest.kt} (78%) diff --git a/libraries/apollo-api/api/apollo-api.api b/libraries/apollo-api/api/apollo-api.api index ac9a3dd17bc..4c7c43fb3f3 100644 --- a/libraries/apollo-api/api/apollo-api.api +++ b/libraries/apollo-api/api/apollo-api.api @@ -426,6 +426,7 @@ public final class com/apollographql/apollo/api/CustomScalarAdapters$Builder { public final fun deferredFragmentIdentifiers (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; public final fun errors (Ljava/util/List;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; public final fun falseVariables (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; + public final fun pendingResultIds (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; } public final class com/apollographql/apollo/api/CustomScalarAdapters$Key : com/apollographql/apollo/api/ExecutionContext$Key { diff --git a/libraries/apollo-api/api/apollo-api.klib.api b/libraries/apollo-api/api/apollo-api.klib.api index bcf1f648be1..19dce8acfcf 100644 --- a/libraries/apollo-api/api/apollo-api.klib.api +++ b/libraries/apollo-api/api/apollo-api.klib.api @@ -953,6 +953,7 @@ final class com.apollographql.apollo.api/CustomScalarAdapters : com.apollographq final fun deferredFragmentIdentifiers(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.deferredFragmentIdentifiers|deferredFragmentIdentifiers(kotlin.collections.Set?){}[0] final fun errors(kotlin.collections/List?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.errors|errors(kotlin.collections.List?){}[0] final fun falseVariables(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.falseVariables|falseVariables(kotlin.collections.Set?){}[0] + final fun pendingResultIds(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.pendingResultIds|pendingResultIds(kotlin.collections.Set?){}[0] } final object Key : com.apollographql.apollo.api/ExecutionContext.Key { // com.apollographql.apollo.api/CustomScalarAdapters.Key|null[0] diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index bddc85a8729..cfe2db00c53 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -50,7 +50,8 @@ fun and(vararg other: BooleanExpression): BooleanExpression = Bo fun not(other: BooleanExpression): BooleanExpression = BooleanExpression.Not(other) fun variable(name: String): BooleanExpression = BooleanExpression.Element(BVariable(name)) fun label(label: String? = null): BooleanExpression = BooleanExpression.Element(BLabel(label)) -fun possibleTypes(vararg typenames: String): BooleanExpression = BooleanExpression.Element(BPossibleTypes(typenames.toSet())) +fun possibleTypes(vararg typenames: String): BooleanExpression = + BooleanExpression.Element(BPossibleTypes(typenames.toSet())) internal fun BooleanExpression.evaluate(block: (T) -> Boolean): Boolean { return when (this) { @@ -66,7 +67,7 @@ internal fun BooleanExpression.evaluate(block: (T) -> Boolean): Boo fun BooleanExpression.evaluate( variables: Set?, typename: String?, - deferredFragmentIdentifiers: Set?, + pendingResultIds: Set?, path: List?, ): Boolean { // Remove "data" from the path @@ -74,22 +75,22 @@ fun BooleanExpression.evaluate( return evaluate { when (it) { is BVariable -> !(variables?.contains(it.name) ?: false) - is BLabel -> !isDeferredFragmentPending(deferredFragmentIdentifiers, croppedPath!!, it.label) + is BLabel -> !isDeferredFragmentPending(pendingResultIds, croppedPath!!, it.label) is BPossibleTypes -> it.possibleTypes.contains(typename) } } } private fun isDeferredFragmentPending( - deferredFragmentIdentifiers: Set?, + pendingResultIds: Set?, path: List, label: String?, ): Boolean { - if (deferredFragmentIdentifiers == null) { + if (pendingResultIds == null) { // By default, parse all deferred fragments - this is the case when parsing from the normalized cache. return false } - return deferredFragmentIdentifiers.contains(DeferredFragmentIdentifier(path, label)) + return pendingResultIds.contains(IncrementalResultIdentifier(path, label)) } /** diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt index a02faf1ac44..585aa94a4c3 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt @@ -19,10 +19,10 @@ class CustomScalarAdapters private constructor( @JvmField val falseVariables: Set?, /** - * Defer identifiers used to determine whether the parser must parse @defer fragments + * Pending incremental result identifiers used to determine whether the parser must parse deferred fragments */ @JvmField - val deferredFragmentIdentifiers: Set?, + val deferredFragmentIdentifiers: Set?, /** * Errors to use with @catch */ @@ -125,21 +125,26 @@ class CustomScalarAdapters private constructor( fun newBuilder(): Builder { return Builder().addAll(this) .falseVariables(falseVariables) - .deferredFragmentIdentifiers(deferredFragmentIdentifiers) + .pendingResultIds(deferredFragmentIdentifiers) } class Builder { private val adaptersMap: MutableMap> = mutableMapOf() private var falseVariables: Set? = null - private var deferredFragmentIdentifiers: Set? = null + private var pendingResultIds: Set? = null private var errors: List? = null fun falseVariables(falseVariables: Set?) = apply { this.falseVariables = falseVariables } - fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set?) = apply { - this.deferredFragmentIdentifiers = deferredFragmentIdentifiers + @Deprecated("Use pendingResultIds instead", ReplaceWith("pendingResultIds(pendingResultIds = deferredFragmentIdentifiers)")) + fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set?) = apply { + this.pendingResultIds = deferredFragmentIdentifiers + } + + fun pendingResultIds(pendingResultIds: Set?) = apply { + this.pendingResultIds = pendingResultIds } fun errors(errors: List?) = apply { @@ -173,7 +178,7 @@ class CustomScalarAdapters private constructor( return CustomScalarAdapters( adaptersMap, falseVariables, - deferredFragmentIdentifiers, + pendingResultIds, errors, ) } diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt index 28d0fe1c090..59fda6ca3ef 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt @@ -71,12 +71,12 @@ fun Executable.parseData( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, falseVariables: Set? = null, - deferredFragmentIds: Set? = null, - errors: List? = null + deferredFragmentIds: Set? = null, + errors: List? = null, ): D? { val customScalarAdapters1 = customScalarAdapters.newBuilder() .falseVariables(falseVariables) - .deferredFragmentIdentifiers(deferredFragmentIds) + .pendingResultIds(pendingResultIds = deferredFragmentIds) .errors(errors) .build() return adapter().nullable().fromJson(jsonReader, customScalarAdapters1) @@ -89,4 +89,4 @@ fun Executable.composeData( value: D ) { adapter().toJson(jsonWriter, customScalarAdapters, value) -} \ No newline at end of file +} diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt similarity index 54% rename from libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt rename to libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt index 13f5e2d4dac..0d3d6ec5d6a 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt @@ -7,3 +7,9 @@ data class DeferredFragmentIdentifier( val path: List, val label: String?, ) + +/** + * Identifies an incremental result. + * [DeferredFragmentIdentifier] is kept to not break the API/ABI, but this alias is more descriptive of its purpose. + */ +typealias IncrementalResultIdentifier = DeferredFragmentIdentifier diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt index 9dc586f8147..a97159aa4a0 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt @@ -70,7 +70,7 @@ fun Operation.composeJsonRequest( fun Operation.parseJsonResponse( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return jsonReader.use { ResponseParser.parse( @@ -103,7 +103,7 @@ fun Operation.parseResponse( jsonReader: JsonReader, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return try { ResponseParser.parse( @@ -177,7 +177,7 @@ fun JsonReader.toApolloResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return use { try { @@ -213,7 +213,7 @@ fun JsonReader.parseResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return try { ResponseParser.parse( diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt index 2ce1c62f70f..fc611c83e17 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt @@ -3,8 +3,8 @@ package com.apollographql.apollo.api.internal import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters -import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.IncrementalResultIdentifier import com.apollographql.apollo.api.Operation import com.apollographql.apollo.api.falseVariables import com.apollographql.apollo.api.json.JsonReader @@ -24,7 +24,7 @@ internal object ResponseParser { operation: Operation, requestUuid: Uuid?, customScalarAdapters: CustomScalarAdapters, - deferredFragmentIds: Set?, + pendingResultIds: Set?, ): ApolloResponse { jsonReader.beginObject() @@ -36,8 +36,9 @@ internal object ResponseParser { when (val name = jsonReader.nextName()) { "data" -> { val falseVariables = operation.falseVariables(customScalarAdapters) - data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, deferredFragmentIds, errors) + data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, pendingResultIds, errors) } + "errors" -> errors = jsonReader.readErrors() "extensions" -> extensions = jsonReader.readAny() as? Map else -> { @@ -100,7 +101,8 @@ private fun JsonReader.readError(): Error { @Suppress("DEPRECATION") - return Error.Builder(message = message).locations(locations).path(path).extensions(extensions).nonStandardFields(nonStandardFields).build() + return Error.Builder(message = message).locations(locations).path(path).extensions(extensions).nonStandardFields(nonStandardFields) + .build() } private fun JsonReader.readPath(): List? { @@ -164,4 +166,4 @@ fun JsonReader.readErrors(): List { } endArray() return list -} \ No newline at end of file +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt deleted file mode 100644 index 47a6fdb4204..00000000000 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/DeferredJsonMerger.kt +++ /dev/null @@ -1,187 +0,0 @@ -package com.apollographql.apollo.internal - -import com.apollographql.apollo.annotations.ApolloInternal -import com.apollographql.apollo.api.DeferredFragmentIdentifier -import com.apollographql.apollo.api.json.BufferedSourceJsonReader -import com.apollographql.apollo.api.json.readAny -import okio.BufferedSource - -private typealias JsonMap = Map -private typealias MutableJsonMap = MutableMap - -/** - * Utility class for merging GraphQL JSON payloads received in multiple chunks when using the `@defer` directive. - * - * Each call to [merge] will merge the given chunk into the [merged] Map, and will also update the [pendingFragmentIds] Set with the - * value of its `path` and `label` field. - * - * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field (for the first call to - * [merge], the payload is copied to [merged] as-is). - * - * `errors` in incremental and completed items (if present) are merged together in an array and then set to the `errors` field of the - * [merged] Map, at each call to [merge]. - * `extensions` in incremental items (if present) are merged together in an array and then set to the `extensions` field of the [merged] - * Map, at each call to [merge]. - */ -@ApolloInternal -@Suppress("UNCHECKED_CAST") -class DeferredJsonMerger { - private val _merged: MutableJsonMap = mutableMapOf() - val merged: JsonMap = _merged - - /** - * Map of identifiers to their corresponding DeferredFragmentIdentifier, found in `pending`. - */ - private val _pendingFragmentIds = mutableMapOf() - val pendingFragmentIds: Set get() = _pendingFragmentIds.values.toSet() - - var hasNext: Boolean = true - private set - - /** - * A payload can sometimes have no `incremental` field, e.g. when the server couldn't predict if there were more data after the last - * emitted payload. This field allows to test for this in order to ignore such payloads. - * See https://github.com/apollographql/router/issues/1687. - */ - var isEmptyPayload: Boolean = false - private set - - fun merge(payload: BufferedSource): JsonMap { - val payloadMap = jsonToMap(payload) - return merge(payloadMap) - } - - fun merge(payload: JsonMap): JsonMap { - val completed = payload["completed"] as? List - if (merged.isEmpty()) { - // Initial payload, no merging needed (strip some fields that should not appear in the final result) - _merged += payload - "hasNext" - "pending" - handlePending(payload) - handleCompleted(completed) - return merged - } - handlePending(payload) - - val incrementalList = payload["incremental"] as? List - if (incrementalList != null) { - for (incrementalItem in incrementalList) { - mergeIncrementalData(incrementalItem) - // Merge errors (if any) of the incremental item - (incrementalItem["errors"] as? List)?.let { getOrPutMergedErrors() += it } - } - } - isEmptyPayload = completed == null && incrementalList == null - - hasNext = payload["hasNext"] as Boolean? ?: false - - handleCompleted(completed) - - (payload["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } - - return merged - } - - private fun getOrPutMergedErrors() = _merged.getOrPut("errors") { mutableListOf() } as MutableList - - private fun getOrPutExtensions() = _merged.getOrPut("extensions") { mutableMapOf() } as MutableJsonMap - - private fun handlePending(payload: JsonMap) { - val pending = payload["pending"] as? List - if (pending != null) { - for (pendingItem in pending) { - val id = pendingItem["id"] as String - val path = pendingItem["path"] as List - val label = pendingItem["label"] as String? - _pendingFragmentIds[id] = DeferredFragmentIdentifier(path = path, label = label) - } - } - } - - private fun handleCompleted(completed: List?) { - if (completed != null) { - for (completedItem in completed) { - // Merge errors (if any) of the completed item - val errors = completedItem["errors"] as? List - if (errors != null) { - getOrPutMergedErrors() += errors - } else { - // Fragment is no longer pending - only if there were no errors - val id = completedItem["id"] as String - _pendingFragmentIds.remove(id) ?: error("Id '$id' not found in pending results") - } - } - } - } - - private fun mergeIncrementalData(incrementalItem: JsonMap) { - val id = incrementalItem["id"] as String? ?: error("No id found in incremental result") - val data = incrementalItem["data"] as JsonMap? - val items = incrementalItem["items"] as List? - val subPath = incrementalItem["subPath"] as List? ?: emptyList() - val path = (_pendingFragmentIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath - val mergedData = merged["data"] as JsonMap - val nodeToMergeInto = nodeAtPath(mergedData, path) - when { - data != null -> { - deepMergeObject(nodeToMergeInto as MutableJsonMap, data) - } - - items != null -> { - mergeList(nodeToMergeInto as MutableList, items) - } - - else -> { - error("Neither data nor items found in incremental result") - } - } - } - - private fun deepMergeObject(destination: MutableJsonMap, obj: JsonMap) { - for ((key, value) in obj) { - if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { - // Objects: merge recursively - val fieldDestination = destination[key] as MutableJsonMap - val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map") - deepMergeObject(destination = fieldDestination, obj = fieldMap) - } else { - // Other types: add / overwrite - destination[key] = value - } - } - } - - private fun mergeList(destination: MutableList, items: List) { - destination.addAll(items) - } - - private fun jsonToMap(json: BufferedSource): JsonMap = BufferedSourceJsonReader(json).readAny() as JsonMap - - - /** - * Find the node in the [map] at the given [path]. - * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array). - */ - private fun nodeAtPath(map: JsonMap, path: List): Any? { - var node: Any? = map - for (key in path) { - node = if (node is List<*>) { - node[key as Int] - } else { - node as JsonMap - node[key] - } - } - return node - } - - fun reset() { - _merged.clear() - _pendingFragmentIds.clear() - hasNext = true - isEmptyPayload = false - } -} - -internal fun JsonMap.isDeferred(): Boolean { - return keys.contains("hasNext") -} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt new file mode 100644 index 00000000000..b49e1af3dd0 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt @@ -0,0 +1,186 @@ +package com.apollographql.apollo.internal + +import com.apollographql.apollo.annotations.ApolloInternal +import com.apollographql.apollo.api.IncrementalResultIdentifier +import com.apollographql.apollo.api.json.BufferedSourceJsonReader +import com.apollographql.apollo.api.json.readAny +import okio.BufferedSource + +private typealias JsonMap = Map +private typealias MutableJsonMap = MutableMap + +/** + * Utility class for merging GraphQL incremental results received in multiple chunks when using the `@defer` and/or `@stream` directives. + * + * Each call to [merge] will merge the given results into the [merged] Map, and will also update the [pendingResultIds] Set with the + * value of their `path` and `label` fields. + * + * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field. For the first call to + * [merge], the payload is copied to [merged] as-is. + * + * `errors` in incremental and completed results (if present) are merged together in an array and then set to the `errors` field of the + * [merged] Map. + * `extensions` in incremental results (if present) are merged together in an array and then set to the `extensions` field of the [merged] + * Map. + */ +@ApolloInternal +@Suppress("UNCHECKED_CAST") +class IncrementalResultsMerger { + private val _merged: MutableJsonMap = mutableMapOf() + val merged: JsonMap = _merged + + /** + * Map of identifiers to their corresponding IncrementalResultIdentifier, found in `pending`. + */ + private val _pendingResultIds = mutableMapOf() + val pendingResultIds: Set get() = _pendingResultIds.values.toSet() + + var hasNext: Boolean = true + private set + + /** + * A response can sometimes have no `incremental` field, e.g. when the server couldn't predict if there were more data after the last + * emitted payload. This field allows to test for this in order to ignore such payloads. + * See https://github.com/apollographql/router/issues/1687. + */ + var isEmptyResponse: Boolean = false + private set + + fun merge(part: BufferedSource): JsonMap { + return merge(part.toJsonMap()) + } + + fun merge(part: JsonMap): JsonMap { + val completed = part["completed"] as? List + if (merged.isEmpty()) { + // Initial part, no merging needed (strip some fields that should not appear in the final result) + _merged += part - "hasNext" - "pending" + handlePending(part) + handleCompleted(completed) + return merged + } + handlePending(part) + + val incremental = part["incremental"] as? List + if (incremental != null) { + for (incrementalResult in incremental) { + mergeIncrementalResult(incrementalResult) + // Merge errors (if any) of the incremental result + (incrementalResult["errors"] as? List)?.let { getOrPutMergedErrors() += it } + } + } + isEmptyResponse = completed == null && incremental == null + + hasNext = part["hasNext"] as Boolean? ?: false + + handleCompleted(completed) + + (part["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } + + return merged + } + + private fun getOrPutMergedErrors() = _merged.getOrPut("errors") { mutableListOf() } as MutableList + + private fun getOrPutExtensions() = _merged.getOrPut("extensions") { mutableMapOf() } as MutableJsonMap + + private fun handlePending(part: JsonMap) { + val pending = part["pending"] as? List + if (pending != null) { + for (pendingResult in pending) { + val id = pendingResult["id"] as String + val path = pendingResult["path"] as List + val label = pendingResult["label"] as String? + _pendingResultIds[id] = IncrementalResultIdentifier(path = path, label = label) + } + } + } + + private fun handleCompleted(completed: List?) { + if (completed != null) { + for (completedResult in completed) { + // Merge errors (if any) of the completed result + val errors = completedResult["errors"] as? List + if (errors != null) { + getOrPutMergedErrors() += errors + } else { + // Fragment is no longer pending - only if there were no errors + val id = completedResult["id"] as String + _pendingResultIds.remove(id) ?: error("Id '$id' not found in pending results") + } + } + } + } + + private fun mergeIncrementalResult(incrementalResult: JsonMap) { + val id = incrementalResult["id"] as String? ?: error("No id found in incremental result") + val data = incrementalResult["data"] as JsonMap? + val items = incrementalResult["items"] as List? + val subPath = incrementalResult["subPath"] as List? ?: emptyList() + val path = (_pendingResultIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath + val mergedData = merged["data"] as JsonMap + val nodeToMergeInto = nodeAtPath(mergedData, path) + when { + data != null -> { + deepMergeObject(nodeToMergeInto as MutableJsonMap, data) + } + + items != null -> { + mergeList(nodeToMergeInto as MutableList, items) + } + + else -> { + error("Neither data nor items found in incremental result") + } + } + } + + private fun deepMergeObject(destination: MutableJsonMap, obj: JsonMap) { + for ((key, value) in obj) { + if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { + // Objects: merge recursively + val fieldDestination = destination[key] as MutableJsonMap + val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map") + deepMergeObject(destination = fieldDestination, obj = fieldMap) + } else { + // Other types: add / overwrite + destination[key] = value + } + } + } + + private fun mergeList(destination: MutableList, items: List) { + destination.addAll(items) + } + + private fun BufferedSource.toJsonMap(): JsonMap = BufferedSourceJsonReader(this).readAny() as JsonMap + + + /** + * Find the node in the [map] at the given [path]. + * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array). + */ + private fun nodeAtPath(map: JsonMap, path: List): Any? { + var node: Any? = map + for (key in path) { + node = if (node is List<*>) { + node[key as Int] + } else { + node as JsonMap + node[key] + } + } + return node + } + + fun reset() { + _merged.clear() + _pendingResultIds.clear() + hasNext = true + isEmptyResponse = false + } +} + +internal fun JsonMap.isIncremental(): Boolean { + return keys.contains("hasNext") +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 974b032e43b..4acb6af02bf 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -21,7 +21,7 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.RouterError -import com.apollographql.apollo.internal.DeferredJsonMerger +import com.apollographql.apollo.internal.IncrementalResultsMerger import com.apollographql.apollo.internal.isGraphQLResponse import com.apollographql.apollo.internal.isMultipart import com.apollographql.apollo.internal.multipartBodyFlow @@ -170,7 +170,7 @@ private constructor( customScalarAdapters: CustomScalarAdapters, httpResponse: HttpResponse, ): Flow> { - var jsonMerger: DeferredJsonMerger? = null + var incrementalResultsMerger: IncrementalResultsMerger? = null val operation = request.operation return multipartBodyFlow(httpResponse) @@ -218,21 +218,20 @@ private constructor( else -> null } } else { - if (jsonMerger == null) { - jsonMerger = DeferredJsonMerger() + if (incrementalResultsMerger == null) { + incrementalResultsMerger = IncrementalResultsMerger() } - val merged = jsonMerger.merge(part) - val deferredFragmentIds = jsonMerger.pendingFragmentIds - val isLast = !jsonMerger.hasNext + val merged = incrementalResultsMerger.merge(part) + val pendingResultIds = incrementalResultsMerger.pendingResultIds + val isLast = !incrementalResultsMerger.hasNext - if (jsonMerger.isEmptyPayload) { + if (incrementalResultsMerger.isEmptyResponse) { null } else { - @Suppress("DEPRECATION") merged.jsonReader().toApolloResponse( operation = operation, customScalarAdapters = customScalarAdapters, - deferredFragmentIdentifiers = deferredFragmentIds + deferredFragmentIdentifiers = pendingResultIds ).newBuilder().isLast(isLast).build() } } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index 1bfe1f0b98a..e6e2370c49f 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -12,7 +12,7 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloWebSocketForceCloseException import com.apollographql.apollo.exception.DefaultApolloException import com.apollographql.apollo.exception.SubscriptionOperationException -import com.apollographql.apollo.internal.DeferredJsonMerger +import com.apollographql.apollo.internal.IncrementalResultsMerger import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.websocket.internal.OperationListener import com.apollographql.apollo.network.websocket.internal.WebSocketPool @@ -202,7 +202,7 @@ private object DefaultSubscriptionParserFactory: SubscriptionParserFactory { } private class DefaultSubscriptionParser(private val request: ApolloRequest) : SubscriptionParser { - private var deferredJsonMerger: DeferredJsonMerger = DeferredJsonMerger() + private var incrementalResultsMerger: IncrementalResultsMerger = IncrementalResultsMerger() private val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters] ?: CustomScalarAdapters.Empty @Suppress("NAME_SHADOWING") @@ -215,7 +215,7 @@ private class DefaultSubscriptionParser(private val request: } val (payload, mergedFragmentIds) = if (responseMap.isDeferred()) { - deferredJsonMerger.merge(responseMap) to deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.merge(responseMap) to incrementalResultsMerger.pendingResultIds } else { responseMap to null } @@ -226,12 +226,12 @@ private class DefaultSubscriptionParser(private val request: deferredFragmentIdentifiers = mergedFragmentIds ) - if (!deferredJsonMerger.hasNext) { - // Last deferred payload: reset the deferredJsonMerger for potential subsequent responses - deferredJsonMerger.reset() + if (!incrementalResultsMerger.hasNext) { + // Last incremental result: reset the incrementalResultsMerger for potential subsequent responses + incrementalResultsMerger.reset() } - return if (deferredJsonMerger.isEmptyPayload) { + return if (incrementalResultsMerger.isEmptyResponse) { null } else { apolloResponse diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index 8ffda6b9285..549638ed687 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -10,8 +10,8 @@ import com.apollographql.apollo.api.toApolloResponse import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.SubscriptionOperationException -import com.apollographql.apollo.internal.DeferredJsonMerger -import com.apollographql.apollo.internal.isDeferred +import com.apollographql.apollo.internal.IncrementalResultsMerger +import com.apollographql.apollo.internal.isIncremental import com.apollographql.apollo.internal.transformWhile import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.ws.internal.Command @@ -262,7 +262,7 @@ private constructor( override fun execute( request: ApolloRequest, ): Flow> { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() return events.onSubscription { messages.send(StartOperation(request)) @@ -302,8 +302,8 @@ private constructor( is OperationResponse -> { val responsePayload = response.payload val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters]!! - val (payload, mergedFragmentIds) = if (responsePayload.isDeferred()) { - deferredJsonMerger.merge(responsePayload) to deferredJsonMerger.pendingFragmentIds + val (payload, mergedFragmentIds) = if (responsePayload.isIncremental()) { + incrementalResultsMerger.merge(responsePayload) to incrementalResultsMerger.pendingResultIds } else { responsePayload to null } @@ -314,9 +314,9 @@ private constructor( deferredFragmentIdentifiers = mergedFragmentIds ) - if (!deferredJsonMerger.hasNext) { - // Last deferred payload: reset the deferredJsonMerger for potential subsequent responses - deferredJsonMerger.reset() + if (!incrementalResultsMerger.hasNext) { + // Last incremental result: reset the incrementalResultsMerger for potential subsequent responses + incrementalResultsMerger.reset() } apolloResponse } @@ -328,7 +328,7 @@ private constructor( is ConnectionReEstablished, is OperationComplete, is GeneralError -> error("Unexpected event $response") } }.filterNot { - deferredJsonMerger.isEmptyPayload + incrementalResultsMerger.isEmptyResponse }.onCompletion { messages.send(StopOperation(request)) } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt similarity index 78% rename from libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt rename to libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt index f1c189ad919..e743fa6f2ac 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/DeferredJsonMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt @@ -3,10 +3,10 @@ package test.defer import com.apollographql.apollo.annotations.ApolloInternal -import com.apollographql.apollo.api.DeferredFragmentIdentifier +import com.apollographql.apollo.api.IncrementalResultIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.DeferredJsonMerger +import com.apollographql.apollo.internal.IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -18,10 +18,10 @@ private fun String.buffer() = Buffer().writeUtf8(this) @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map -class DeferredJsonMergerTest { +class IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -76,13 +76,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0") ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -154,13 +154,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0") + IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0") ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -236,13 +236,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -334,14 +334,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -442,19 +442,19 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @Test fun mergeJsonMultipleIncrementalItems() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -517,14 +517,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -622,14 +622,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2_3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2_3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -750,19 +750,19 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload4_5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload4_5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @Test fun emptyPayloads() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -804,8 +804,8 @@ class DeferredJsonMergerTest { "hasNext": true } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertFalse(deferredJsonMerger.isEmptyPayload) + incrementalResultsMerger.merge(payload1.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) //language=JSON val payload2 = """ @@ -813,8 +813,8 @@ class DeferredJsonMergerTest { "hasNext": true } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertTrue(deferredJsonMerger.isEmptyPayload) + incrementalResultsMerger.merge(payload2.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) //language=JSON val payload3 = """ { @@ -833,8 +833,8 @@ class DeferredJsonMergerTest { "hasNext": true } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertFalse(deferredJsonMerger.isEmptyPayload) + incrementalResultsMerger.merge(payload3.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) //language=JSON val payload4 = """ @@ -842,8 +842,8 @@ class DeferredJsonMergerTest { "hasNext": false } """.trimIndent() - deferredJsonMerger.merge(payload4.buffer()) - assertTrue(deferredJsonMerger.isEmptyPayload) + incrementalResultsMerger.merge(payload4.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) } /** @@ -851,7 +851,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleA() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -897,13 +897,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), + IncrementalResultIdentifier(path = listOf(), label = null), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -957,11 +957,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -970,7 +970,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleA2() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1017,13 +1017,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "D1"), + IncrementalResultIdentifier(path = listOf(), label = "D1"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1083,13 +1083,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), + IncrementalResultIdentifier(path = listOf("f2", "c", "f"), label = "D2"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1136,11 +1136,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1149,7 +1149,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleB1() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1195,14 +1195,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + IncrementalResultIdentifier(path = listOf(), label = "Blue"), + IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1250,13 +1250,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + IncrementalResultIdentifier(path = listOf(), label = "Blue"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1303,11 +1303,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1316,7 +1316,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleB2() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1362,14 +1362,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + IncrementalResultIdentifier(path = listOf(), label = "Blue"), + IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1423,13 +1423,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1472,11 +1472,11 @@ class DeferredJsonMergerTest { } } """ - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1485,7 +1485,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleD() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1515,14 +1515,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), - DeferredFragmentIdentifier(path = listOf("me"), label = null), + IncrementalResultIdentifier(path = listOf(), label = null), + IncrementalResultIdentifier(path = listOf("me"), label = null), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1613,13 +1613,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), + IncrementalResultIdentifier(path = listOf(), label = null), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1700,11 +1700,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1713,7 +1713,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleF() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1740,13 +1740,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + IncrementalResultIdentifier(path = listOf("me"), label = "B"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1780,11 +1780,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1793,7 +1793,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleG() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1843,14 +1843,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "Billing"), - DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + IncrementalResultIdentifier(path = listOf("me"), label = "Billing"), + IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1893,13 +1893,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -1949,11 +1949,11 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -1962,7 +1962,7 @@ class DeferredJsonMergerTest { */ @Test fun june2023ExampleH() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1994,14 +1994,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf(), label = "A"), - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + IncrementalResultIdentifier(path = listOf(), label = "A"), + IncrementalResultIdentifier(path = listOf("me"), label = "B"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2053,13 +2053,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + IncrementalResultIdentifier(path = listOf("me"), label = "B"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2119,13 +2119,13 @@ class DeferredJsonMergerTest { ] } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + IncrementalResultIdentifier(path = listOf("me"), label = "B"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -2134,7 +2134,7 @@ class DeferredJsonMergerTest { */ @Test fun july2025ExampleI() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2189,14 +2189,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2236,14 +2236,14 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2279,13 +2279,13 @@ class DeferredJsonMergerTest { } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2334,11 +2334,11 @@ class DeferredJsonMergerTest { } """.trimIndent() - deferredJsonMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) assertEquals( setOf(), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } @@ -2347,7 +2347,7 @@ class DeferredJsonMergerTest { */ @Test fun july2025ExampleJ() { - val deferredJsonMerger = DeferredJsonMerger() + val incrementalResultsMerger = IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2387,13 +2387,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2429,13 +2429,13 @@ class DeferredJsonMergerTest { } } """.trimIndent() - deferredJsonMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) //language=JSON @@ -2497,13 +2497,13 @@ class DeferredJsonMergerTest { } """.trimIndent() - deferredJsonMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), deferredJsonMerger.merged) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - deferredJsonMerger.pendingFragmentIds + incrementalResultsMerger.pendingResultIds ) } } From e327f81cd29e53b6c5c492ddbb4cb66092ec4d18 Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 21 Jul 2025 10:00:31 +0200 Subject: [PATCH 09/38] Fix running tests hardcoded to true --- tests/defer/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/defer/build.gradle.kts b/tests/defer/build.gradle.kts index 14b0fb6fa3b..0936f857a3d 100644 --- a/tests/defer/build.gradle.kts +++ b/tests/defer/build.gradle.kts @@ -71,7 +71,7 @@ fun com.apollographql.apollo.gradle.api.Service.configureConnection(generateKotl tasks.withType(AbstractTestTask::class.java) { // Run the defer with Router and defer with Apollo Server tests only from a specific CI job val runDeferWithRouterTests = System.getenv("DEFER_WITH_ROUTER_TESTS").toBoolean() - val runDeferWithApolloServerTests = true + val runDeferWithApolloServerTests = System.getenv("DEFER_WITH_APOLLO_SERVER_TESTS").toBoolean() filter.setIncludePatterns(*buildList { if (runDeferWithRouterTests) add("test.DeferWithRouterTest") if (runDeferWithApolloServerTests) add("test.DeferWithApolloServerTest") From ad05aa0fabd113d48e98d8957e63daf17ac3cb6c Mon Sep 17 00:00:00 2001 From: BoD Date: Wed, 24 Sep 2025 18:12:58 +0200 Subject: [PATCH 10/38] Support both legacy and modern incremental delivery protocols, with a configuration. Defaults to the legacy one. --- ...-tests.yml => defer-integration-tests.yml} | 2 +- .../apollo/api/BooleanExpression.kt | 21 +- .../apollo/api/CustomScalarAdapters.kt | 19 +- .../apollographql/apollo/api/Executables.kt | 4 +- .../apollo/api/IncrementalResultIdentifier.kt | 30 +- .../apollographql/apollo/api/Operations.kt | 8 +- .../api/http/DefaultHttpRequestComposer.kt | 36 +- .../apollo/api/internal/ResponseParser.kt | 6 +- .../Defer20220824IncrementalResultsMerger.kt | 92 +++ ...raphQL17Alpha9IncrementalResultsMerger.kt} | 85 +- .../incremental/IncrementalResultsMerger.kt | 41 + .../apollo/internal/incremental/JsonMap.kt | 45 ++ .../network/http/HttpNetworkTransport.kt | 65 +- .../websocket/WebSocketNetworkTransport.kt | 38 +- .../network/ws/WebSocketNetworkTransport.kt | 22 +- ...fer20220824IncrementalResultsMergerTest.kt | 747 ++++++++++++++++++ ...QL17Alpha9IncrementalResultsMergerTest.kt} | 628 +++++++-------- tests/defer/apollo-server/README.md | 2 +- tests/defer/apollo-server/package.json | 2 +- .../patches/@apollo+server+4.11.2.patch | 27 +- .../test/Defer20220824NormalizedCacheTest.kt | 548 +++++++++++++ .../kotlin/test/Defer20220824Test.kt | 446 +++++++++++ ...eferGraphQL17Alpha9NormalizedCacheTest.kt} | 200 +++-- ...ferTest.kt => DeferGraphQL17Alpha9Test.kt} | 21 +- .../kotlin/test/DeferWithApolloServerTest.kt | 9 +- .../src/jvmTest/kotlin/test/DeferJvmTest.kt | 11 +- 26 files changed, 2588 insertions(+), 567 deletions(-) rename .github/workflows/{defer-with-router-tests.yml => defer-integration-tests.yml} (98%) create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/{IncrementalResultsMerger.kt => incremental/GraphQL17Alpha9IncrementalResultsMerger.kt} (56%) create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt create mode 100644 libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt rename libraries/apollo-runtime/src/commonTest/kotlin/test/defer/{IncrementalResultsMergerTest.kt => GraphQL17Alpha9IncrementalResultsMergerTest.kt} (78%) create mode 100644 tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt create mode 100644 tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt rename tests/defer/src/commonTest/kotlin/test/{DeferNormalizedCacheTest.kt => DeferGraphQL17Alpha9NormalizedCacheTest.kt} (86%) rename tests/defer/src/commonTest/kotlin/test/{DeferTest.kt => DeferGraphQL17Alpha9Test.kt} (95%) diff --git a/.github/workflows/defer-with-router-tests.yml b/.github/workflows/defer-integration-tests.yml similarity index 98% rename from .github/workflows/defer-with-router-tests.yml rename to .github/workflows/defer-integration-tests.yml index 4088364734c..313085f6379 100644 --- a/.github/workflows/defer-with-router-tests.yml +++ b/.github/workflows/defer-integration-tests.yml @@ -1,4 +1,4 @@ -name: defer-with-router-tests +name: defer-integration-tests on: schedule: diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index cfe2db00c53..8c33d3e88ad 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -67,7 +67,7 @@ internal fun BooleanExpression.evaluate(block: (T) -> Boolean): Boo fun BooleanExpression.evaluate( variables: Set?, typename: String?, - pendingResultIds: Set?, + deferredFragmentIdentifiers: IncrementalResultIdentifiers?, path: List?, ): Boolean { // Remove "data" from the path @@ -75,22 +75,29 @@ fun BooleanExpression.evaluate( return evaluate { when (it) { is BVariable -> !(variables?.contains(it.name) ?: false) - is BLabel -> !isDeferredFragmentPending(pendingResultIds, croppedPath!!, it.label) + is BLabel -> shouldParseFragment(deferredFragmentIdentifiers = deferredFragmentIdentifiers, path = croppedPath!!, label = it.label) is BPossibleTypes -> it.possibleTypes.contains(typename) } } } -private fun isDeferredFragmentPending( - pendingResultIds: Set?, +private fun shouldParseFragment( + deferredFragmentIdentifiers: IncrementalResultIdentifiers?, path: List, label: String?, ): Boolean { - if (pendingResultIds == null) { + if (deferredFragmentIdentifiers == null) { // By default, parse all deferred fragments - this is the case when parsing from the normalized cache. - return false + return true + } + val identifier = IncrementalResultIdentifier(path, label) + return if (deferredFragmentIdentifiers.isPending()) { + // Modern protocol: parse fragments that are _not_ pending + !deferredFragmentIdentifiers.contains(identifier) + } else { + // Legacy 20220824 protocol: parse fragments that have been merged + deferredFragmentIdentifiers.contains(identifier) } - return pendingResultIds.contains(IncrementalResultIdentifier(path, label)) } /** diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt index 585aa94a4c3..324d58c95a6 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt @@ -19,10 +19,10 @@ class CustomScalarAdapters private constructor( @JvmField val falseVariables: Set?, /** - * Pending incremental result identifiers used to determine whether the parser must parse deferred fragments + * Incremental result identifiers used to determine whether the parser must parse deferred fragments */ @JvmField - val deferredFragmentIdentifiers: Set?, + val deferredFragmentIdentifiers: IncrementalResultIdentifiers?, /** * Errors to use with @catch */ @@ -125,26 +125,21 @@ class CustomScalarAdapters private constructor( fun newBuilder(): Builder { return Builder().addAll(this) .falseVariables(falseVariables) - .pendingResultIds(deferredFragmentIdentifiers) + .deferredFragmentIdentifiers(deferredFragmentIdentifiers) } class Builder { private val adaptersMap: MutableMap> = mutableMapOf() private var falseVariables: Set? = null - private var pendingResultIds: Set? = null + private var deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null private var errors: List? = null fun falseVariables(falseVariables: Set?) = apply { this.falseVariables = falseVariables } - @Deprecated("Use pendingResultIds instead", ReplaceWith("pendingResultIds(pendingResultIds = deferredFragmentIdentifiers)")) - fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set?) = apply { - this.pendingResultIds = deferredFragmentIdentifiers - } - - fun pendingResultIds(pendingResultIds: Set?) = apply { - this.pendingResultIds = pendingResultIds + fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: IncrementalResultIdentifiers?) = apply { + this.deferredFragmentIdentifiers = deferredFragmentIdentifiers } fun errors(errors: List?) = apply { @@ -178,7 +173,7 @@ class CustomScalarAdapters private constructor( return CustomScalarAdapters( adaptersMap, falseVariables, - pendingResultIds, + deferredFragmentIdentifiers, errors, ) } diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt index 59fda6ca3ef..0cd35e4c604 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt @@ -71,12 +71,12 @@ fun Executable.parseData( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, falseVariables: Set? = null, - deferredFragmentIds: Set? = null, + deferredFragmentIds: IncrementalResultIdentifiers? = null, errors: List? = null, ): D? { val customScalarAdapters1 = customScalarAdapters.newBuilder() .falseVariables(falseVariables) - .pendingResultIds(pendingResultIds = deferredFragmentIds) + .deferredFragmentIdentifiers(deferredFragmentIds) .errors(errors) .build() return adapter().nullable().fromJson(jsonReader, customScalarAdapters1) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt index 0d3d6ec5d6a..b2455047c37 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt @@ -1,15 +1,41 @@ package com.apollographql.apollo.api +import com.apollographql.apollo.annotations.ApolloInternal + data class DeferredFragmentIdentifier( /** * Path of the fragment in the overall JSON response. The elements can either be Strings (names) or Integers (array indices). */ val path: List, val label: String?, -) +) { + internal companion object { + /** + * Special identifier to signal that the identifiers are pending, as in the modern version of the protocol. + */ + internal val Pending = DeferredFragmentIdentifier(emptyList(), "__pending") + } +} /** * Identifies an incremental result. - * [DeferredFragmentIdentifier] is kept to not break the API/ABI, but this alias is more descriptive of its purpose. + * [DeferredFragmentIdentifier] is kept to preserve the API/ABI, but this alias is more descriptive of its purpose. */ typealias IncrementalResultIdentifier = DeferredFragmentIdentifier + +typealias IncrementalResultIdentifiers = Set + +@ApolloInternal +fun IncrementalResultIdentifiers.isPending(): Boolean { + return any { it === DeferredFragmentIdentifier.Pending } +} + +@ApolloInternal +fun IncrementalResultIdentifiers.pending(): IncrementalResultIdentifiers { + return this + DeferredFragmentIdentifier.Pending +} + +@ApolloInternal +fun IncrementalResultIdentifiers.nonPending(): IncrementalResultIdentifiers { + return filter { it !== DeferredFragmentIdentifier.Pending }.toSet() +} diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt index a97159aa4a0..b9e4414818c 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt @@ -70,7 +70,7 @@ fun Operation.composeJsonRequest( fun Operation.parseJsonResponse( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, ): ApolloResponse { return jsonReader.use { ResponseParser.parse( @@ -103,7 +103,7 @@ fun Operation.parseResponse( jsonReader: JsonReader, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, ): ApolloResponse { return try { ResponseParser.parse( @@ -177,7 +177,7 @@ fun JsonReader.toApolloResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, ): ApolloResponse { return use { try { @@ -213,7 +213,7 @@ fun JsonReader.parseResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: Set? = null, + deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, ): ApolloResponse { return try { ResponseParser.parse( diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index b4449cd962b..75b990b7daf 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -8,6 +8,7 @@ import com.apollographql.apollo.api.Operation import com.apollographql.apollo.api.Query import com.apollographql.apollo.api.Subscription import com.apollographql.apollo.api.Upload +import com.apollographql.apollo.api.http.DefaultHttpRequestComposer.Companion.composePostParams import com.apollographql.apollo.api.http.internal.urlEncode import com.apollographql.apollo.api.json.BufferedSinkJsonWriter import com.apollographql.apollo.api.json.JsonWriter @@ -38,10 +39,17 @@ import okio.buffer */ class DefaultHttpRequestComposer( private val serverUrl: String, - private val enablePostCaching: Boolean + private val enablePostCaching: Boolean, + private val acceptHeaderQueriesAndMutations: String = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824, + private val acceptHeaderSubscriptions: String = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0, ) : HttpRequestComposer { - constructor(serverUrl: String): this(serverUrl, false) + constructor(serverUrl: String) : this( + serverUrl = serverUrl, + enablePostCaching = false, + acceptHeaderQueriesAndMutations = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824, + acceptHeaderSubscriptions = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0, + ) override fun compose(apolloRequest: ApolloRequest): HttpRequest { val operation = apolloRequest.operation @@ -49,9 +57,9 @@ class DefaultHttpRequestComposer( val requestHeaders = mutableListOf().apply { if (apolloRequest.operation is Subscription<*>) { - add(HttpHeader(HEADER_ACCEPT_NAME, HEADER_ACCEPT_VALUE_MULTIPART)) + add(HttpHeader(HEADER_ACCEPT_NAME, acceptHeaderSubscriptions)) } else { - add(HttpHeader(HEADER_ACCEPT_NAME, HEADER_ACCEPT_VALUE_DEFER)) + add(HttpHeader(HEADER_ACCEPT_NAME, acceptHeaderQueriesAndMutations)) } if (apolloRequest.httpHeaders != null) { addAll(apolloRequest.httpHeaders) @@ -123,11 +131,21 @@ class DefaultHttpRequestComposer( val HEADER_ACCEPT_NAME = "Accept" - // TODO The deferSpec=20220824 part is a temporary measure so early backend implementations of the @defer directive - // can recognize early client implementations and potentially reply in a compatible way. - // This should be removed in later versions. - val HEADER_ACCEPT_VALUE_DEFER = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - val HEADER_ACCEPT_VALUE_MULTIPART = "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 = + "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + + // TODO To be agreed upon with the router and other clients + const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 = + "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" + + const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 = + "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + + @Deprecated("Use HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 instead", ReplaceWith("HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0")) + val HEADER_ACCEPT_VALUE_MULTIPART = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 + + @Deprecated("Use HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 instead", ReplaceWith("HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824")) + val HEADER_ACCEPT_VALUE_DEFER = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 private fun buildGetUrl( serverUrl: String, diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt index fc611c83e17..0c92b233578 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt @@ -4,7 +4,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters import com.apollographql.apollo.api.Error -import com.apollographql.apollo.api.IncrementalResultIdentifier +import com.apollographql.apollo.api.IncrementalResultIdentifiers import com.apollographql.apollo.api.Operation import com.apollographql.apollo.api.falseVariables import com.apollographql.apollo.api.json.JsonReader @@ -24,7 +24,7 @@ internal object ResponseParser { operation: Operation, requestUuid: Uuid?, customScalarAdapters: CustomScalarAdapters, - pendingResultIds: Set?, + deferredFragmentIds: IncrementalResultIdentifiers?, ): ApolloResponse { jsonReader.beginObject() @@ -36,7 +36,7 @@ internal object ResponseParser { when (val name = jsonReader.nextName()) { "data" -> { val falseVariables = operation.falseVariables(customScalarAdapters) - data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, pendingResultIds, errors) + data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, deferredFragmentIds, errors) } "errors" -> errors = jsonReader.readErrors() diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt new file mode 100644 index 00000000000..e9db40042f9 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt @@ -0,0 +1,92 @@ +package com.apollographql.apollo.internal.incremental + +import com.apollographql.apollo.annotations.ApolloInternal +import com.apollographql.apollo.api.IncrementalResultIdentifier +import com.apollographql.apollo.api.IncrementalResultIdentifiers +import okio.BufferedSource + +/** + * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.Defer20220824] protocol format. + */ +@ApolloInternal +@Suppress("UNCHECKED_CAST") +class Defer20220824IncrementalResultsMerger : IncrementalResultsMerger { + private val _merged: MutableJsonMap = mutableMapOf() + override val merged: JsonMap = _merged + + private val _incrementalResultIds = mutableSetOf() + + /** + * For this protocol, this represents the set of fragment ids that are already merged. + */ + override val incrementalResultIdentifiers: IncrementalResultIdentifiers = _incrementalResultIds + + override var hasNext: Boolean = true + private set + + override var isEmptyResponse: Boolean = false + private set + + override fun merge(part: BufferedSource): JsonMap { + return merge(part.toJsonMap()) + } + + override fun merge(part: JsonMap): JsonMap { + if (merged.isEmpty()) { + // Initial part, no merging needed + _merged += part + return merged + } + + val incremental = part["incremental"] as? List + if (incremental == null) { + isEmptyResponse = true + } else { + isEmptyResponse = false + val mergedErrors = mutableListOf() + val mergedExtensions = mutableListOf() + for (incrementalResult in incremental) { + incrementalResult(incrementalResult) + // Merge errors and extensions (if any) of the incremental result + (incrementalResult["errors"] as? List)?.let { mergedErrors += it } + (incrementalResult["extensions"] as? JsonMap)?.let { mergedExtensions += it } + } + // Keep only this payload's errors and extensions, if any + if (mergedErrors.isNotEmpty()) { + _merged["errors"] = mergedErrors + } else { + _merged.remove("errors") + } + if (mergedExtensions.isNotEmpty()) { + _merged["extensions"] = mapOf("incremental" to mergedExtensions) + } else { + _merged.remove("extensions") + } + } + + hasNext = part["hasNext"] as Boolean? ?: false + + return merged + } + + private fun incrementalResult(incrementalResult: JsonMap) { + val data = incrementalResult["data"] as JsonMap? + val path = incrementalResult["path"] as List + val mergedData = merged["data"] as JsonMap + + // data can be null if there are errors + if (data != null) { + val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap + deepMergeObject(nodeToMergeInto, data) + + _incrementalResultIds += IncrementalResultIdentifier(path = path, label = incrementalResult["label"] as String?) + } + } + + override fun reset() { + _merged.clear() + _incrementalResultIds.clear() + hasNext = true + isEmptyResponse = false + } +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt similarity index 56% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt index b49e1af3dd0..5d4b88b2dca 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt @@ -1,56 +1,41 @@ -package com.apollographql.apollo.internal +package com.apollographql.apollo.internal.incremental import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.IncrementalResultIdentifier -import com.apollographql.apollo.api.json.BufferedSourceJsonReader -import com.apollographql.apollo.api.json.readAny +import com.apollographql.apollo.api.IncrementalResultIdentifiers +import com.apollographql.apollo.api.pending import okio.BufferedSource -private typealias JsonMap = Map -private typealias MutableJsonMap = MutableMap - /** - * Utility class for merging GraphQL incremental results received in multiple chunks when using the `@defer` and/or `@stream` directives. - * - * Each call to [merge] will merge the given results into the [merged] Map, and will also update the [pendingResultIds] Set with the - * value of their `path` and `label` fields. - * - * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field. For the first call to - * [merge], the payload is copied to [merged] as-is. - * - * `errors` in incremental and completed results (if present) are merged together in an array and then set to the `errors` field of the - * [merged] Map. - * `extensions` in incremental results (if present) are merged together in an array and then set to the `extensions` field of the [merged] - * Map. + * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9] protocol format. */ @ApolloInternal @Suppress("UNCHECKED_CAST") -class IncrementalResultsMerger { +class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() - val merged: JsonMap = _merged + override val merged: JsonMap = _merged /** * Map of identifiers to their corresponding IncrementalResultIdentifier, found in `pending`. */ private val _pendingResultIds = mutableMapOf() - val pendingResultIds: Set get() = _pendingResultIds.values.toSet() - - var hasNext: Boolean = true - private set /** - * A response can sometimes have no `incremental` field, e.g. when the server couldn't predict if there were more data after the last - * emitted payload. This field allows to test for this in order to ignore such payloads. - * See https://github.com/apollographql/router/issues/1687. + * For this protocol, this represents the set of ids that are pending. */ - var isEmptyResponse: Boolean = false + override val incrementalResultIdentifiers: IncrementalResultIdentifiers get() = _pendingResultIds.values.toSet().pending() + + override var hasNext: Boolean = true + private set + + override var isEmptyResponse: Boolean = false private set - fun merge(part: BufferedSource): JsonMap { + override fun merge(part: BufferedSource): JsonMap { return merge(part.toJsonMap()) } - fun merge(part: JsonMap): JsonMap { + override fun merge(part: JsonMap): JsonMap { val completed = part["completed"] as? List if (merged.isEmpty()) { // Initial part, no merging needed (strip some fields that should not appear in the final result) @@ -135,52 +120,14 @@ class IncrementalResultsMerger { } } - private fun deepMergeObject(destination: MutableJsonMap, obj: JsonMap) { - for ((key, value) in obj) { - if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { - // Objects: merge recursively - val fieldDestination = destination[key] as MutableJsonMap - val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map") - deepMergeObject(destination = fieldDestination, obj = fieldMap) - } else { - // Other types: add / overwrite - destination[key] = value - } - } - } - private fun mergeList(destination: MutableList, items: List) { destination.addAll(items) } - private fun BufferedSource.toJsonMap(): JsonMap = BufferedSourceJsonReader(this).readAny() as JsonMap - - - /** - * Find the node in the [map] at the given [path]. - * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array). - */ - private fun nodeAtPath(map: JsonMap, path: List): Any? { - var node: Any? = map - for (key in path) { - node = if (node is List<*>) { - node[key as Int] - } else { - node as JsonMap - node[key] - } - } - return node - } - - fun reset() { + override fun reset() { _merged.clear() _pendingResultIds.clear() hasNext = true isEmptyResponse = false } } - -internal fun JsonMap.isIncremental(): Boolean { - return keys.contains("hasNext") -} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt new file mode 100644 index 00000000000..c2fa48e989f --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt @@ -0,0 +1,41 @@ +package com.apollographql.apollo.internal.incremental + +import com.apollographql.apollo.annotations.ApolloInternal +import com.apollographql.apollo.api.IncrementalResultIdentifiers +import okio.BufferedSource + +/** + * Utility for merging GraphQL incremental results received in multiple chunks when using the `@defer` and/or `@stream` directives. + * + * Each call to [merge] will merge the given results into the [merged] Map, and will also update [incrementalResultIdentifiers] with the + * value of their `path` and `label` fields. + * + * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field. For the first call to + * [merge], the payload is copied to [merged] as-is. + * + * `errors` in incremental and completed results (if present) are merged together in an array and then set to the `errors` field of the + * [merged] Map. + * `extensions` in incremental results (if present) are merged together in an array and then set to the `extensions` field of the [merged] + * Map. + */ +@ApolloInternal +interface IncrementalResultsMerger { + val merged: JsonMap + + val incrementalResultIdentifiers: IncrementalResultIdentifiers + + val hasNext: Boolean + + /** + * A response can sometimes have no `incremental` field, e.g. when the server couldn't predict if there were more data after the last + * emitted payload. This field allows to test for this in order to ignore such payloads. + * See https://github.com/apollographql/router/issues/1687. + */ + val isEmptyResponse: Boolean + + fun merge(part: BufferedSource): JsonMap + + fun merge(part: JsonMap): JsonMap + + fun reset() +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt new file mode 100644 index 00000000000..a7aea249326 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt @@ -0,0 +1,45 @@ +@file:Suppress("UNCHECKED_CAST") + +package com.apollographql.apollo.internal.incremental + +import com.apollographql.apollo.api.json.BufferedSourceJsonReader +import com.apollographql.apollo.api.json.readAny +import okio.BufferedSource + +internal typealias JsonMap = Map +internal typealias MutableJsonMap = MutableMap + + +/** + * Find the node in the [map] at the given [path]. + * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array). + */ +internal fun nodeAtPath(map: JsonMap, path: List): Any? { + var node: Any? = map + for (key in path) { + node = if (node is List<*>) { + node[key as Int] + } else { + node as JsonMap + node[key] + } + } + return node +} + +internal fun deepMergeObject(destination: MutableJsonMap, obj: JsonMap) { + for ((key, value) in obj) { + if (destination.containsKey(key) && destination[key] is MutableMap<*, *>) { + // Objects: merge recursively + val fieldDestination = destination[key] as MutableJsonMap + val fieldMap = value as? JsonMap ?: error("'$key' is an object in destination but not in map") + deepMergeObject(destination = fieldDestination, obj = fieldMap) + } else { + // Other types: add / overwrite + destination[key] = value + } + } +} + +internal fun BufferedSource.toJsonMap(): JsonMap = BufferedSourceJsonReader(this).readAny() as JsonMap + diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 4acb6af02bf..919f31b0ab6 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -1,6 +1,7 @@ package com.apollographql.apollo.network.http import com.apollographql.apollo.annotations.ApolloDeprecatedSince +import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters @@ -21,7 +22,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.RouterError -import com.apollographql.apollo.internal.IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.Defer20220824IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.GraphQL17Alpha9IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.IncrementalResultsMerger import com.apollographql.apollo.internal.isGraphQLResponse import com.apollographql.apollo.internal.isMultipart import com.apollographql.apollo.internal.multipartBodyFlow @@ -44,6 +47,7 @@ private constructor( private val engine: HttpEngine, val interceptors: List, private val exposeErrorBody: Boolean, + private val incrementalDeliveryProtocol: IncrementalDeliveryProtocol, ) : NetworkTransport { private val engineInterceptor = EngineInterceptor() @@ -219,10 +223,10 @@ private constructor( } } else { if (incrementalResultsMerger == null) { - incrementalResultsMerger = IncrementalResultsMerger() + incrementalResultsMerger = incrementalDeliveryProtocol.newIncrementalResultsMerger() } val merged = incrementalResultsMerger.merge(part) - val pendingResultIds = incrementalResultsMerger.pendingResultIds + val deferredFragmentIds = incrementalResultsMerger.incrementalResultIdentifiers val isLast = !incrementalResultsMerger.hasNext if (incrementalResultsMerger.isEmptyResponse) { @@ -231,7 +235,7 @@ private constructor( merged.jsonReader().toApolloResponse( operation = operation, customScalarAdapters = customScalarAdapters, - deferredFragmentIdentifiers = pendingResultIds + deferredFragmentIdentifiers = deferredFragmentIds ).newBuilder().isLast(isLast).build() } } @@ -310,6 +314,7 @@ private constructor( private var engine: HttpEngine? = null private val interceptors: MutableList = mutableListOf() private var exposeErrorBody: Boolean = false + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.Defer20220824 private val headers: MutableList = mutableListOf() fun httpRequestComposer(httpRequestComposer: HttpRequestComposer) = apply { @@ -353,6 +358,10 @@ private constructor( this.engine = httpEngine } + fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { + this.incrementalDeliveryProtocol = incrementalDeliveryProtocol + } + fun interceptors(interceptors: List) = apply { this.interceptors.clear() this.interceptors.addAll(interceptors) @@ -367,7 +376,12 @@ private constructor( "It is an error to set both 'httpRequestComposer' and 'serverUrl'" } val composer = httpRequestComposer - ?: serverUrl?.let { DefaultHttpRequestComposer(it) } + ?: serverUrl?.let { + DefaultHttpRequestComposer( + serverUrl = it, + acceptHeaderQueriesAndMutations = incrementalDeliveryProtocol.acceptHeader, + ) + } ?: error("No HttpRequestComposer found. Use 'httpRequestComposer' or 'serverUrl'") if (headers.isNotEmpty()) { @@ -379,6 +393,7 @@ private constructor( engine = engine ?: DefaultHttpEngine(), interceptors = interceptors, exposeErrorBody = exposeErrorBody, + incrementalDeliveryProtocol = incrementalDeliveryProtocol, ) } } @@ -391,4 +406,44 @@ private constructor( return chain.proceed(request.newBuilder().addHeaders(headers).build()) } } + + /** + * The protocol to use for incremental delivery (`@defer` and `@stream`). + */ + sealed interface IncrementalDeliveryProtocol { + @ApolloInternal + val acceptHeader: String + + @ApolloInternal + fun newIncrementalResultsMerger(): IncrementalResultsMerger + + /** + * Format specified in this historical commit: + * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec + * + * Only `@defer` is supported with this format. + * + * This is the default. + */ + object Defer20220824 : IncrementalDeliveryProtocol { + @ApolloInternal + override val acceptHeader: String = DefaultHttpRequestComposer.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 + + @ApolloInternal + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Defer20220824IncrementalResultsMerger() + } + + /** + * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. + * + * Both `@defer` and `@stream` are supported with this format. + */ + object GraphQL17Alpha9 : IncrementalDeliveryProtocol { + @ApolloInternal + override val acceptHeader: String = DefaultHttpRequestComposer.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 + + @ApolloInternal + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + } + } } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index e6e2370c49f..631d40e6dfc 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -12,7 +12,6 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloWebSocketForceCloseException import com.apollographql.apollo.exception.DefaultApolloException import com.apollographql.apollo.exception.SubscriptionOperationException -import com.apollographql.apollo.internal.IncrementalResultsMerger import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.websocket.internal.OperationListener import com.apollographql.apollo.network.websocket.internal.WebSocketPool @@ -41,7 +40,7 @@ class WebSocketNetworkTransport private constructor( private val connectionAcknowledgeTimeout: Duration, private val pingInterval: Duration?, private val idleTimeout: Duration, - private val parserFactory: SubscriptionParserFactory + private val parserFactory: SubscriptionParserFactory, ) : NetworkTransport { private val pool = WebSocketPool( @@ -195,14 +194,13 @@ class WebSocketNetworkTransport private constructor( } } -private object DefaultSubscriptionParserFactory: SubscriptionParserFactory { +private object DefaultSubscriptionParserFactory : SubscriptionParserFactory { override fun createParser(request: ApolloRequest): SubscriptionParser { return DefaultSubscriptionParser(request) } } private class DefaultSubscriptionParser(private val request: ApolloRequest) : SubscriptionParser { - private var incrementalResultsMerger: IncrementalResultsMerger = IncrementalResultsMerger() private val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters] ?: CustomScalarAdapters.Empty @Suppress("NAME_SHADOWING") @@ -214,35 +212,20 @@ private class DefaultSubscriptionParser(private val request: .exception(DefaultApolloException("Invalid payload")).build() } - val (payload, mergedFragmentIds) = if (responseMap.isDeferred()) { - incrementalResultsMerger.merge(responseMap) to incrementalResultsMerger.pendingResultIds - } else { - responseMap to null - } - val apolloResponse: ApolloResponse = payload.jsonReader().toApolloResponse( + val apolloResponse: ApolloResponse = responseMap.jsonReader().toApolloResponse( operation = request.operation, requestUuid = request.requestUuid, customScalarAdapters = requestCustomScalarAdapters, - deferredFragmentIdentifiers = mergedFragmentIds ) - if (!incrementalResultsMerger.hasNext) { - // Last incremental result: reset the incrementalResultsMerger for potential subsequent responses - incrementalResultsMerger.reset() - } - - return if (incrementalResultsMerger.isEmptyResponse) { - null - } else { - apolloResponse - } + return apolloResponse } } private class DefaultOperationListener( private val request: ApolloRequest, private val producerScope: ProducerScope>, - private val parser: SubscriptionParser + private val parser: SubscriptionParser, ) : OperationListener { override fun onResponse(response: ApolloJsonElement) { parser.parse(response)?.let { @@ -270,11 +253,6 @@ private class DefaultOperationListener( producerScope.close() } } - -private fun Map.isDeferred(): Boolean { - return keys.contains("hasNext") -} - /** * Closes the websocket connection if the transport is a [WebSocketNetworkTransport]. * @@ -289,7 +267,8 @@ private fun Map.isDeferred(): Boolean { */ @ApolloExperimental fun NetworkTransport.closeConnection(exception: ApolloException) { - val webSocketNetworkTransport = (this as? WebSocketNetworkTransport) ?: throw IllegalArgumentException("'$this' is not an instance of com.apollographql.apollo.websocket.WebSocketNetworkTransport") + val webSocketNetworkTransport = (this as? WebSocketNetworkTransport) + ?: throw IllegalArgumentException("'$this' is not an instance of com.apollographql.apollo.websocket.WebSocketNetworkTransport") webSocketNetworkTransport.closeConnection(exception) } @@ -301,7 +280,8 @@ fun NetworkTransport.closeConnection(exception: ApolloException) { */ @ApolloExperimental fun NetworkTransport.closeConnection() { - val webSocketNetworkTransport = (this as? WebSocketNetworkTransport) ?: throw IllegalArgumentException("'$this' is not an instance of com.apollographql.apollo.websocket.WebSocketNetworkTransport") + val webSocketNetworkTransport = (this as? WebSocketNetworkTransport) + ?: throw IllegalArgumentException("'$this' is not an instance of com.apollographql.apollo.websocket.WebSocketNetworkTransport") webSocketNetworkTransport.closeConnection(ApolloWebSocketForceCloseException) } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index 549638ed687..f432a47aeb4 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -10,8 +10,6 @@ import com.apollographql.apollo.api.toApolloResponse import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.SubscriptionOperationException -import com.apollographql.apollo.internal.IncrementalResultsMerger -import com.apollographql.apollo.internal.isIncremental import com.apollographql.apollo.internal.transformWhile import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.ws.internal.Command @@ -40,7 +38,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onSubscription @@ -61,7 +58,7 @@ private constructor( private val webSocketEngine: WebSocketEngine = DefaultWebSocketEngine(), private val idleTimeoutMillis: Long = 60_000, private val protocolFactory: WsProtocol.Factory = SubscriptionWsProtocol.Factory(), - private val reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)? + private val reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)?, ) : NetworkTransport { /** @@ -262,8 +259,6 @@ private constructor( override fun execute( request: ApolloRequest, ): Flow> { - val incrementalResultsMerger = IncrementalResultsMerger() - return events.onSubscription { messages.send(StartOperation(request)) }.filter { @@ -300,24 +295,13 @@ private constructor( }.map { response -> when (response) { is OperationResponse -> { - val responsePayload = response.payload val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters]!! - val (payload, mergedFragmentIds) = if (responsePayload.isIncremental()) { - incrementalResultsMerger.merge(responsePayload) to incrementalResultsMerger.pendingResultIds - } else { - responsePayload to null - } - val apolloResponse: ApolloResponse = payload.jsonReader().toApolloResponse( + val apolloResponse: ApolloResponse = response.payload.jsonReader().toApolloResponse( operation = request.operation, requestUuid = request.requestUuid, customScalarAdapters = requestCustomScalarAdapters, - deferredFragmentIdentifiers = mergedFragmentIds ) - if (!incrementalResultsMerger.hasNext) { - // Last incremental result: reset the incrementalResultsMerger for potential subsequent responses - incrementalResultsMerger.reset() - } apolloResponse } @@ -327,8 +311,6 @@ private constructor( // Cannot happen as these events are filtered out upstream is ConnectionReEstablished, is OperationComplete, is GeneralError -> error("Unexpected event $response") } - }.filterNot { - incrementalResultsMerger.isEmptyResponse }.onCompletion { messages.send(StopOperation(request)) } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt new file mode 100644 index 00000000000..e33a5e34f8f --- /dev/null +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt @@ -0,0 +1,747 @@ +@file:OptIn(ApolloInternal::class) + +package test.defer + +import com.apollographql.apollo.annotations.ApolloInternal +import com.apollographql.apollo.api.DeferredFragmentIdentifier +import com.apollographql.apollo.api.json.BufferedSourceJsonReader +import com.apollographql.apollo.api.json.readAny +import com.apollographql.apollo.internal.incremental.Defer20220824IncrementalResultsMerger +import okio.Buffer +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +private fun String.buffer() = Buffer().writeUtf8(this) + +@Suppress("UNCHECKED_CAST") +private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map + +class Defer20220824IncrementalResultsMergerTest { + @Test + fun mergeJsonSingleIncrementalItem() { + val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "hasNext": true, + "extensions": { + "incremental": [ + { + "duration": { + "amount": 100, + "unit": "ms" + } + } + ] + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "hasNext": true, + "extensions": { + "incremental": [ + { + "duration": { + "amount": 25, + "unit": "ms" + } + } + ] + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload4 = """ + { + "incremental": [ + { + "data": null, + "path": [ + "computers", + 0, + "screen" + ], + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ], + "label": "fragment:ComputerFields:0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "hasNext": true, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ] + } + """.trimIndent() + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload5 = """ + { + "incremental": [ + { + "data": { + "isColor": false + }, + "path": [ + "computers", + 1, + "screen" + ], + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ], + "label": "fragment:ComputerFields:0", + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } + } + ] + }, + "hasNext": true, + "extensions": { + "incremental": [ + { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + ] + }, + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ] + } + """.trimIndent() + incrementalResultsMerger.merge(payload5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + } + + @Test + fun mergeJsonMultipleIncrementalItems() { + val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload2_3 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + }, + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "hasNext": true, + "extensions": { + "incremental": [ + { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + { + "duration": { + "amount": 25, + "unit": "ms" + } + } + ] + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2_3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + + //language=JSON + val payload4_5 = """ + { + "incremental": [ + { + "data": null, + "path": [ + "computers", + 0, + "screen" + ], + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ], + "label": "fragment:ComputerFields:0" + }, + { + "data": { + "isColor": false + }, + "path": [ + "computers", + 1, + "screen" + ], + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ], + "label": "fragment:ComputerFields:0", + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } + } + ] + }, + "hasNext": true, + "extensions": { + "incremental": [ + { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + ] + }, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + }, + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ] + } + """.trimIndent() + incrementalResultsMerger.merge(payload4_5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers + ) + } + + @Test + fun emptyPayloads() { + val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) + + //language=JSON + val payload2 = """ + { + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) + + //language=JSON + val payload4 = """ + { + "hasNext": false + } + """.trimIndent() + incrementalResultsMerger.merge(payload4.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) + } +} diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt similarity index 78% rename from libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt rename to libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt index e743fa6f2ac..cf07c635dc3 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt @@ -6,7 +6,8 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.IncrementalResultIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.IncrementalResultsMerger +import com.apollographql.apollo.api.nonPending +import com.apollographql.apollo.internal.incremental.GraphQL17Alpha9IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -18,10 +19,10 @@ private fun String.buffer() = Buffer().writeUtf8(this) @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map -class IncrementalResultsMergerTest { +class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -78,12 +79,12 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0") - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -160,7 +161,7 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0") ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -242,7 +243,7 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -341,7 +342,7 @@ class IncrementalResultsMergerTest { IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -448,13 +449,13 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -519,13 +520,13 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2_3 = """ @@ -629,7 +630,7 @@ class IncrementalResultsMergerTest { IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -756,13 +757,13 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @Test fun emptyPayloads() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -851,7 +852,7 @@ class IncrementalResultsMergerTest { */ @Test fun june2023ExampleA() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -899,12 +900,12 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = null), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = null), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -961,7 +962,7 @@ class IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @@ -970,7 +971,7 @@ class IncrementalResultsMergerTest { */ @Test fun june2023ExampleA2() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1019,12 +1020,12 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = "D1"), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = "D1"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -1089,7 +1090,7 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("f2", "c", "f"), label = "D2"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -1140,7 +1141,7 @@ class IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @@ -1149,7 +1150,7 @@ class IncrementalResultsMergerTest { */ @Test fun june2023ExampleB1() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1197,13 +1198,13 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = "Blue"), - IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = "Blue"), + IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -1256,7 +1257,7 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf(), label = "Blue"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -1307,7 +1308,7 @@ class IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @@ -1316,7 +1317,7 @@ class IncrementalResultsMergerTest { */ @Test fun june2023ExampleB2() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1364,13 +1365,13 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = "Blue"), - IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = "Blue"), + IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -1429,54 +1430,55 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON val payload3 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - } - ], - "completed": [ - { - "id": "1" + { + "incremental": [ + { + "id": "1", + "data": { + "potentiallySlowFieldA": "potentiallySlowFieldA" } - ], - "hasNext": false - } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } """.trimIndent() + //language=JSON val mergedPayloads_1_2_3 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" } - """ + } + """.trimIndent() incrementalResultsMerger.merge(payload3.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } @@ -1485,7 +1487,7 @@ class IncrementalResultsMergerTest { */ @Test fun june2023ExampleD() { - val incrementalResultsMerger = IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1517,13 +1519,13 @@ class IncrementalResultsMergerTest { """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = null), - IncrementalResultIdentifier(path = listOf("me"), label = null), - ), - incrementalResultsMerger.pendingResultIds - ) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = null), + IncrementalResultIdentifier(path = listOf("me"), label = null), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) //language=JSON val payload2 = """ @@ -1619,7 +1621,7 @@ class IncrementalResultsMergerTest { setOf( IncrementalResultIdentifier(path = listOf(), label = null), ), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) //language=JSON @@ -1704,18 +1706,18 @@ class IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } - /** - * Example F from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleF() { - val incrementalResultsMerger = IncrementalResultsMerger() - //language=JSON - val payload1 = """ + /** + * Example F from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleF() { + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + //language=JSON + val payload1 = """ { "data": { "me": {} @@ -1732,25 +1734,25 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "me": {} } } """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( setOf( IncrementalResultIdentifier(path = listOf("me"), label = "B"), ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload2 = """ + //language=JSON + val payload2 = """ { "incremental": [ { @@ -1769,8 +1771,8 @@ class IncrementalResultsMergerTest { "hasNext": false } """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ + //language=JSON + val mergedPayloads_1_2 = """ { "data": { "me": { @@ -1780,22 +1782,22 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.pendingResultIds + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() ) } - /** - * Example G from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleG() { - val incrementalResultsMerger = IncrementalResultsMerger() - //language=JSON - val payload1 = """ + /** + * Example G from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleG() { + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + //language=JSON + val payload1 = """ { "data": { "me": { @@ -1827,8 +1829,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "me": { @@ -1843,18 +1845,18 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "Billing"), - IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("me"), label = "Billing"), + IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload2 = """ + //language=JSON + val payload2 = """ { "incremental": [ { @@ -1874,8 +1876,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ + //language=JSON + val mergedPayloads_1_2 = """ { "data": { "me": { @@ -1893,17 +1895,17 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload3 = """ + //language=JSON + val payload3 = """ { "incremental": [ { @@ -1925,8 +1927,8 @@ class IncrementalResultsMergerTest { "hasNext": false } """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ + //language=JSON + val mergedPayloads_1_2_3 = """ { "data": { "me": { @@ -1949,22 +1951,22 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.pendingResultIds - ) - } + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) + } - /** - * Example H from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleH() { - val incrementalResultsMerger = IncrementalResultsMerger() - //language=JSON - val payload1 = """ + /** + * Example H from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleH() { + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + //language=JSON + val payload1 = """ { "data": { "me": {} @@ -1986,26 +1988,26 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "me": {} } } """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf(), label = "A"), - IncrementalResultIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf(), label = "A"), + IncrementalResultIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload2 = """ + //language=JSON + val payload2 = """ { "incremental": [ { @@ -2039,8 +2041,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ + //language=JSON + val mergedPayloads_1_2 = """ { "data": { "me": { @@ -2053,17 +2055,17 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload3 = """ + //language=JSON + val payload3 = """ { "completed": [ { @@ -2089,8 +2091,8 @@ class IncrementalResultsMergerTest { "hasNext": false } """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ + //language=JSON + val mergedPayloads_1_2_3 = """ { "data": { "me": { @@ -2119,24 +2121,24 @@ class IncrementalResultsMergerTest { ] } """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.pendingResultIds - ) - } + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) + } - /** - * Example I from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) - */ - @Test - fun july2025ExampleI() { - val incrementalResultsMerger = IncrementalResultsMerger() - //language=JSON - val payload1 = """ + /** + * Example I from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleI() { + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + //language=JSON + val payload1 = """ { "data": { "person": { @@ -2171,8 +2173,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "person": { @@ -2189,18 +2191,18 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload2 = """ + //language=JSON + val payload2 = """ { "incremental": [ { @@ -2215,8 +2217,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ + //language=JSON + val mergedPayloads_1_2 = """ { "data": { "person": { @@ -2236,18 +2238,18 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload3 = """ + //language=JSON + val payload3 = """ { "completed": [ { @@ -2257,8 +2259,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ + //language=JSON + val mergedPayloads_1_2_3 = """ { "data": { "person": { @@ -2279,17 +2281,17 @@ class IncrementalResultsMergerTest { } """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload4 = """ + //language=JSON + val payload4 = """ { "incremental": [ { @@ -2309,8 +2311,8 @@ class IncrementalResultsMergerTest { "hasNext": false } """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4 = """ + //language=JSON + val mergedPayloads_1_2_3_4 = """ { "data": { "person": { @@ -2334,22 +2336,22 @@ class IncrementalResultsMergerTest { } """.trimIndent() - incrementalResultsMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.pendingResultIds - ) - } + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) + } - /** - * Example J from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) - */ - @Test - fun july2025ExampleJ() { - val incrementalResultsMerger = IncrementalResultsMerger() - //language=JSON - val payload1 = """ + /** + * Example J from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleJ() { + val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + //language=JSON + val payload1 = """ { "data": { "person": { @@ -2373,8 +2375,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ + //language=JSON + val mergedPayloads_1 = """ { "data": { "person": { @@ -2387,17 +2389,17 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload2 = """ + //language=JSON + val payload2 = """ { "incremental": [ { @@ -2412,8 +2414,8 @@ class IncrementalResultsMergerTest { "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ + //language=JSON + val mergedPayloads_1_2 = """ { "data": { "person": { @@ -2429,17 +2431,17 @@ class IncrementalResultsMergerTest { } } """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.pendingResultIds - ) + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) - //language=JSON - val payload3 = """ + //language=JSON + val payload3 = """ { "completed": [ { @@ -2464,8 +2466,8 @@ class IncrementalResultsMergerTest { "hasNext": false } """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ + //language=JSON + val mergedPayloads_1_2_3 = """ { "data": { "person": { @@ -2497,13 +2499,13 @@ class IncrementalResultsMergerTest { } """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.pendingResultIds - ) - } + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + ) + } } diff --git a/tests/defer/apollo-server/README.md b/tests/defer/apollo-server/README.md index ef149563b19..5111b87d42e 100644 --- a/tests/defer/apollo-server/README.md +++ b/tests/defer/apollo-server/README.md @@ -1,4 +1,4 @@ # Test server using Apollo Server, for `@defer` tests -- This uses graphql-js `17.0.0-alpha.7`, which implements the latest draft of the `@defer` incremental format (as of 2024-12-16). +- This uses graphql-js `17.0.0-alpha.9`, which implements the latest draft of the `@defer` incremental format (as of 2025-09-24). - Apollo Server `4.11.2` needs a patch (in `patches`) to surface this format in the responses. diff --git a/tests/defer/apollo-server/package.json b/tests/defer/apollo-server/package.json index 1b469e77c4e..57416828baf 100644 --- a/tests/defer/apollo-server/package.json +++ b/tests/defer/apollo-server/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@apollo/server": "4.11.2", - "graphql": "17.0.0-alpha.7", + "graphql": "17.0.0-alpha.9", "patch-package": "^8.0.0" }, "keywords": [], diff --git a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch index d6a742855b7..a516a8c8867 100644 --- a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch +++ b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch @@ -1,7 +1,32 @@ +diff --git a/node_modules/@apollo/server/dist/esm/ApolloServer.js b/node_modules/@apollo/server/dist/esm/ApolloServer.js +index 7665490..c32a28c 100644 +--- a/node_modules/@apollo/server/dist/esm/ApolloServer.js ++++ b/node_modules/@apollo/server/dist/esm/ApolloServer.js +@@ -631,7 +631,7 @@ export const MEDIA_TYPES = { + APPLICATION_JSON_GRAPHQL_CALLBACK: 'application/json; callbackSpec=1.0; charset=utf-8', + APPLICATION_GRAPHQL_RESPONSE_JSON: 'application/graphql-response+json; charset=utf-8', + MULTIPART_MIXED_NO_DEFER_SPEC: 'multipart/mixed', +- MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; deferSpec=20220824', ++ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalDeliverySpec=20230621', + TEXT_HTML: 'text/html', + }; + export function chooseContentTypeForSingleResultResponse(head) { diff --git a/node_modules/@apollo/server/dist/esm/runHttpQuery.js b/node_modules/@apollo/server/dist/esm/runHttpQuery.js -index 96ef0ab..0d341fa 100644 +index 96ef0ab..d816750 100644 --- a/node_modules/@apollo/server/dist/esm/runHttpQuery.js +++ b/node_modules/@apollo/server/dist/esm/runHttpQuery.js +@@ -161,9 +161,9 @@ export async function runHttpQuery({ server, httpRequest, contextValue, schemaDe + throw new BadRequestError('Apollo server received an operation that uses incremental delivery ' + + '(@defer or @stream), but the client does not accept multipart/mixed ' + + 'HTTP responses. To enable incremental delivery support, add the HTTP ' + +- "header 'Accept: multipart/mixed; deferSpec=20220824'.", { extensions: { http: { status: 406 } } }); ++ "header 'Accept: multipart/mixed; incrementalDeliverySpec=20230621'.", { extensions: { http: { status: 406 } } }); + } +- graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; deferSpec=20220824'); ++ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalDeliverySpec=20230621'); + return { + ...graphQLResponse.http, + body: { @@ -187,6 +187,7 @@ function orderExecutionResultFields(result) { } function orderInitialIncrementalExecutionResultFields(result) { diff --git a/tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt new file mode 100644 index 00000000000..be5cb5ac1e4 --- /dev/null +++ b/tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt @@ -0,0 +1,548 @@ +package test + +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.api.ApolloRequest +import com.apollographql.apollo.api.ApolloResponse +import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Operation +import com.apollographql.apollo.cache.normalized.ApolloStore +import com.apollographql.apollo.cache.normalized.FetchPolicy +import com.apollographql.apollo.cache.normalized.api.CacheHeaders +import com.apollographql.apollo.cache.normalized.api.MemoryCacheFactory +import com.apollographql.apollo.cache.normalized.apolloStore +import com.apollographql.apollo.cache.normalized.fetchPolicy +import com.apollographql.apollo.cache.normalized.optimisticUpdates +import com.apollographql.apollo.cache.normalized.store +import com.apollographql.apollo.exception.ApolloException +import com.apollographql.apollo.exception.ApolloHttpException +import com.apollographql.apollo.exception.ApolloNetworkException +import com.apollographql.apollo.exception.CacheMissException +import com.apollographql.apollo.network.NetworkTransport +import com.apollographql.apollo.testing.internal.runTest +import com.apollographql.mockserver.MockServer +import com.apollographql.mockserver.assertNoRequest +import com.apollographql.mockserver.awaitRequest +import com.apollographql.mockserver.enqueueError +import com.apollographql.mockserver.enqueueMultipart +import com.apollographql.mockserver.enqueueStrings +import com.benasher44.uuid.uuid4 +import defer.SimpleDeferQuery +import defer.WithFragmentSpreadsMutation +import defer.WithFragmentSpreadsQuery +import defer.fragment.ComputerFields +import defer.fragment.ScreenFields +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList +import okio.ByteString.Companion.encodeUtf8 +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.assertFailsWith +import kotlin.test.assertIs +import kotlin.test.assertTrue + +class Defer20220824NormalizedCacheTest { + private lateinit var mockServer: MockServer + private lateinit var apolloClient: ApolloClient + private lateinit var store: ApolloStore + + private suspend fun setUp() { + store = ApolloStore(MemoryCacheFactory()) + mockServer = MockServer() + apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store).build() + } + + private fun tearDown() { + mockServer.close() + apolloClient.close() + } + + @Test + fun cacheOnly() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheOnly).build() + + // Cache is empty + assertIs( + apolloClient.query(WithFragmentSpreadsQuery()).execute().exception + ) + + // Fill the cache by doing a network only request + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() + mockServer.awaitRequest() + + // Cache is not empty, so this doesn't go to the server + val cacheActual = apolloClient.query(WithFragmentSpreadsQuery()).execute().dataOrThrow() + mockServer.assertNoRequest() + + // We get the last/fully formed data + val cacheExpected = WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ) + assertEquals(cacheExpected, cacheActual) + } + + @Test + fun networkOnly() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.NetworkOnly).build() + + // Fill the cache by doing a first request + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() + mockServer.awaitRequest() + + // Cache is not empty, but NetworkOnly still goes to the server + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val networkActual = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + mockServer.awaitRequest() + + val networkExpected = listOf( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ), + ) + assertEquals(networkExpected, networkActual) + } + + @Test + fun cacheFirst() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + + // Cache is empty, so this goes to the server + val responses = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList() + assertTrue(responses[0].exception is CacheMissException) + val networkActual = responses.drop(1).map { it.dataOrThrow() } + mockServer.awaitRequest() + + val networkExpected = listOf( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ), + ) + assertEquals(networkExpected, networkActual) + + // Cache is not empty, so this doesn't go to the server + val cacheActual = apolloClient.query(WithFragmentSpreadsQuery()).execute().dataOrThrow() + assertFails { mockServer.takeRequest() } + + // We get the last/fully formed data + val cacheExpected = networkExpected.last() + assertEquals(cacheExpected, cacheActual) + } + + @Test + fun networkFirst() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.NetworkFirst).build() + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + + // Cache is empty, so this goes to the server + val networkActual = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + mockServer.awaitRequest() + + val networkExpected = listOf( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ), + ) + assertEquals(networkExpected, networkActual) + + mockServer.enqueueError(statusCode = 500) + // Network will fail, so we get the cached version + val cacheActual = apolloClient.query(WithFragmentSpreadsQuery()).execute().dataOrThrow() + + // We get the last/fully formed data + val cacheExpected = networkExpected.last() + assertEquals(cacheExpected, cacheActual) + } + + @Test + fun cacheAndNetwork() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheAndNetwork).build() + + val jsonList1 = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList1) + + // Cache is empty + val responses = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList() + assertTrue(responses[0].exception is CacheMissException) + val networkActual = responses.drop(1).map { it.dataOrThrow() } + mockServer.awaitRequest() + + val networkExpected = listOf( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ), + ) + assertEquals(networkExpected, networkActual) + + val jsonList2 = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList2) + + // Cache is not empty + val cacheAndNetworkActual = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + mockServer.awaitRequest() + + // We get a combination of the last/fully formed data from the cache + the new network data + val cacheAndNetworkExpected = listOf( + networkExpected.last(), + + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null)) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null) + ) + ) + ) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ) + ) + ), + ) + + assertEquals(cacheAndNetworkExpected, cacheAndNetworkActual) + } + + @Test + fun cacheFirstWithMissingFragmentDueToError() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + + // Cache is empty, so this goes to the server + val networkActual = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().drop(1) + mockServer.awaitRequest() + + val query = WithFragmentSpreadsQuery() + val uuid = uuid4() + + val networkExpected = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ) + .data( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ) + ) + .errors( + listOf( + Error.Builder(message = "Cannot resolve isColor") + .locations(listOf(Error.Location(1, 119))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ) + .build(), + ) + assertResponseListEquals(networkExpected, networkActual) + + mockServer.enqueueError(statusCode = 500) + // Because of the error the cache is missing some fields, so we get a cache miss, and fallback to the network (which also fails) + val exception = apolloClient.query(WithFragmentSpreadsQuery()).execute().exception + check(exception is CacheMissException) + assertIs(exception.suppressedExceptions.first()) + assertEquals("Object 'computers.0.screen' has no field named 'isColor'", exception.message) + mockServer.awaitRequest() + } + + @Test + fun networkFirstWithNetworkError() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = WithFragmentSpreadsQuery() + val uuid = uuid4() + val networkResponses = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ) + ).build(), + ) + + apolloClient = ApolloClient.Builder() + .store(store) + .fetchPolicy(FetchPolicy.NetworkFirst) + .networkTransport( + object : NetworkTransport { + @Suppress("UNCHECKED_CAST") + override fun execute(request: ApolloRequest): Flow> { + // Emit a few items then an exception + return flow { + for (networkResponse in networkResponses) { + emit(networkResponse as ApolloResponse) + } + delay(10) + emit(ApolloResponse.Builder(requestUuid = uuid, operation = query) + .exception(ApolloNetworkException("Network error")) + .isLast(true) + .build() as ApolloResponse + ) + } + } + + override fun dispose() {} + } + ) + .build() + + // - get the first few responses + // - an exception happens + // - fallback to the cache + // - because of the error the cache is missing some fields, so we get a cache miss + val actual = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList() + + assertResponseListEquals(networkResponses, actual.dropLast(2)) + val networkExceptionResponse = actual[actual.size - 2] + val cacheExceptionResponse = actual.last() + assertIs(networkExceptionResponse.exception) + assertIs(cacheExceptionResponse.exception) + assertEquals("Object 'computers.0.screen' has no field named 'isColor'", cacheExceptionResponse.exception!!.message) + } + + @Test + fun mutation() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val networkActual = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataOrThrow() } + mockServer.awaitRequest() + + val networkExpected = listOf( + WithFragmentSpreadsMutation.Data( + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null)) + ), + WithFragmentSpreadsMutation.Data( + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ), + WithFragmentSpreadsMutation.Data( + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ), + ) + assertEquals(networkExpected, networkActual) + + // Now cache is not empty + val cacheActual = apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.CacheOnly).execute().dataOrThrow() + + // We get the last/fully formed data + val cacheExpected = WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) + ) + assertEquals(cacheExpected, cacheActual) + } + + @Test + fun mutationWithOptimisticDataFails() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val responses = apolloClient.mutation(WithFragmentSpreadsMutation()).optimisticUpdates( + WithFragmentSpreadsMutation.Data( + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null)) + ) + ).toFlow() + + val exception = assertFailsWith { + responses.collect() + } + assertEquals("Apollo: optimistic updates can only be applied with one network response", exception.message) + } + + @Test + fun intermediatePayloadsAreCached() = runTest(before = { setUp() }, after = { tearDown() }) { + @Suppress("DEPRECATION") + if (com.apollographql.apollo.testing.platform() == com.apollographql.apollo.testing.Platform.Js) { + // TODO For now chunked is not supported on JS - remove this check when it is + return@runTest + } + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", + ) + val multipartBody = mockServer.enqueueMultipart("application/json") + multipartBody.enqueuePart(jsonList[0].encodeUtf8(), false) + val recordFields = apolloClient.query(SimpleDeferQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().map { + apolloClient.apolloStore.accessCache { it.loadRecord("computers.0", CacheHeaders.NONE)!!.fields }.also { + multipartBody.enqueuePart(jsonList[1].encodeUtf8(), true) + } + }.toList() + assertEquals(mapOf("__typename" to "Computer", "id" to "Computer1"), recordFields[0]) + assertEquals(mapOf("__typename" to "Computer", "id" to "Computer1", "cpu" to "386"), recordFields[1]) + } +} diff --git a/tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt b/tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt new file mode 100644 index 00000000000..1feaf99fa5f --- /dev/null +++ b/tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt @@ -0,0 +1,446 @@ +package test + +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.api.ApolloResponse +import com.apollographql.apollo.api.Error +import com.apollographql.apollo.autoPersistedQueryInfo +import com.apollographql.apollo.mpp.currentTimeMillis +import com.apollographql.apollo.testing.internal.runTest +import com.apollographql.mockserver.MockServer +import com.apollographql.mockserver.enqueueMultipart +import com.apollographql.mockserver.enqueueString +import com.apollographql.mockserver.enqueueStrings +import com.benasher44.uuid.uuid4 +import defer.SimpleDeferQuery +import defer.WithFragmentSpreadsQuery +import defer.WithInlineFragmentsQuery +import defer.fragment.ComputerFields +import defer.fragment.ScreenFields +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.last +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import okio.ByteString.Companion.encodeUtf8 +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class Defer20220824Test { + private lateinit var mockServer: MockServer + private lateinit var apolloClient: ApolloClient + + private suspend fun setUp() { + mockServer = MockServer() + apolloClient = ApolloClient.Builder() + .serverUrl(mockServer.url()) + .build() + } + + private fun tearDown() { + mockServer.close() + } + + @Test + fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + ) + + val expectedDataList = listOf( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null) + ) + ), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null) + ) + ), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualDataList = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"b"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"b"}],"hasNext":false}""", + ) + + val expectedDataList = listOf( + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", null), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", null) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", null) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", null) + ) + ), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", + WithInlineFragmentsQuery.OnScreen(false) + ) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", null) + ) + ), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", + WithInlineFragmentsQuery.OnScreen(false) + ) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", + WithInlineFragmentsQuery.OnScreen(true) + ) + ) + ), + ) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualDataList = apolloClient.query(WithInlineFragmentsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + ) + + val query = WithFragmentSpreadsQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ) + .data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ) + .errors( + listOf( + Error.Builder(message = "Cannot resolve isColor") + .locations(listOf(Error.Location(1, 119))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ) + .build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null) + ) + ), + ) + ) + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ) + ).build(), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun payloadsAreReceivedIncrementally() = runTest(before = { setUp() }, after = { tearDown() }) { + @Suppress("DEPRECATION") + if (com.apollographql.apollo.testing.platform() == com.apollographql.apollo.testing.Platform.Js) { + // TODO For now chunked is not supported on JS - remove this check when it is + return@runTest + } + val delayMillis = 200L + + val multipartBody = mockServer.enqueueMultipart("application/json") + + val syncChannel = Channel() + val job = launch { + apolloClient.query(WithFragmentSpreadsQuery()).toFlow().collect { + syncChannel.send(Unit) + } + } + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + ) + + jsonList.withIndex().forEach { (index, value) -> + multipartBody.enqueueDelay(delayMillis) + multipartBody.enqueuePart(value.encodeUtf8(), index == jsonList.lastIndex) + + val timeBeforeReceive = currentTimeMillis() + syncChannel.receive() + assertTrue(currentTimeMillis() - timeBeforeReceive >= delayMillis) + } + + job.cancel() + } + + @Test + fun emptyPayloadsAreIgnored() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonWithEmptyPayload = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":true}""", + """{"hasNext":false}""", + ) + val jsonWithoutEmptyPayload = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", + ) + + val expectedDataList = listOf( + SimpleDeferQuery.Data( + listOf(SimpleDeferQuery.Computer("Computer", "computer1", null)) + ), + SimpleDeferQuery.Data( + listOf(SimpleDeferQuery.Computer("Computer", "computer1", SimpleDeferQuery.OnComputer("386"))) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithEmptyPayload) + var actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithoutEmptyPayload) + actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithApqFound() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = ApolloClient.Builder() + .serverUrl(mockServer.url()) + .autoPersistedQueries() + .build() + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() + assertEquals(true, finalResponse.autoPersistedQueryInfo?.hit) + assertEquals( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + finalResponse.dataOrThrow() + ) + } + + @Test + fun deferWithApqNotFound() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = ApolloClient.Builder() + .serverUrl(mockServer.url()) + .autoPersistedQueries() + .build() + + mockServer.enqueueString("""{"errors":[{"message":"PersistedQueryNotFound"}]}""") + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() + assertEquals(false, finalResponse.autoPersistedQueryInfo?.hit) + assertEquals( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + finalResponse.dataOrThrow() + ) + } +} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt similarity index 86% rename from tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt index e6bced95233..0958e4f7835 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferNormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt @@ -19,6 +19,8 @@ import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.CacheMissException import com.apollographql.apollo.network.NetworkTransport +import com.apollographql.apollo.network.http.HttpNetworkTransport +import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.internal.runTest import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.assertNoRequest @@ -46,7 +48,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferNormalizedCacheTest { +class DeferGraphQL17Alpha9NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore @@ -54,7 +56,14 @@ class DeferNormalizedCacheTest { private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() - apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store).build() + apolloClient = ApolloClient.Builder() + .networkTransport( + HttpNetworkTransport.Builder() + .serverUrl(mockServer.url()) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .build() + ) + .store(store).build() } private fun tearDown() { @@ -87,17 +96,19 @@ class DeferNormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ) @@ -131,17 +142,19 @@ class DeferNormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) ) - ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ), @@ -174,17 +187,19 @@ class DeferNormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) ) - ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ), @@ -223,17 +238,19 @@ class DeferNormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) ) - ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ), @@ -270,9 +287,15 @@ class DeferNormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) ), ) assertEquals(networkExpected, networkActual) @@ -295,9 +318,15 @@ class DeferNormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null)) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ) + ) ), ) @@ -325,12 +354,13 @@ class DeferNormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ) - ) ).build(), @@ -340,15 +370,17 @@ class DeferNormalizedCacheTest { ).data( WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ) @@ -380,17 +412,26 @@ class DeferNormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - )).build(), + ).data( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ) + ).build(), ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - )).build(), + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ) + ).build(), ) apolloClient = ApolloClient.Builder() @@ -409,7 +450,8 @@ class DeferNormalizedCacheTest { emit(ApolloResponse.Builder(requestUuid = uuid, operation = query) .exception(ApolloNetworkException("Network error")) .isLast(true) - .build() as ApolloResponse) + .build() as ApolloResponse + ) } } @@ -450,18 +492,20 @@ class DeferNormalizedCacheTest { ) ), WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) ) - ) ), - WithFragmentSpreadsMutation.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) ) - ) ) ), ) @@ -473,17 +517,19 @@ class DeferNormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) ) - ) ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) - ) ), ) ) diff --git a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt similarity index 95% rename from tests/defer/src/commonTest/kotlin/test/DeferTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt index 9d7a80a4d00..423ab53cd02 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt @@ -6,7 +6,11 @@ import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.autoPersistedQueryInfo import com.apollographql.apollo.mpp.currentTimeMillis +import com.apollographql.apollo.network.http.HttpNetworkTransport +import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol +import com.apollographql.apollo.testing.Platform import com.apollographql.apollo.testing.internal.runTest +import com.apollographql.apollo.testing.platform import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.enqueueMultipart import com.apollographql.mockserver.enqueueString @@ -26,14 +30,19 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeferTest { +class DeferGraphQL17Alpha9Test { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private suspend fun setUp() { mockServer = MockServer() apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) + .networkTransport( + HttpNetworkTransport.Builder() + .serverUrl(mockServer.url()) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .build() + ) .build() } @@ -174,7 +183,7 @@ class DeferTest { @Test fun payloadsAreReceivedIncrementally() = runTest(before = { setUp() }, after = { tearDown() }) { @Suppress("DEPRECATION") - if (com.apollographql.apollo.testing.platform() == com.apollographql.apollo.testing.Platform.Js) { + if (platform() == Platform.Js) { // TODO For now chunked is not supported on JS - remove this check when it is return@runTest } @@ -244,8 +253,7 @@ class DeferTest { @Test fun deferWithApqFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) + apolloClient = apolloClient.newBuilder() .autoPersistedQueries() .build() @@ -279,8 +287,7 @@ class DeferTest { @Test fun deferWithApqNotFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) + apolloClient = apolloClient.newBuilder() .autoPersistedQueries() .build() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index 1746e941c95..09c954030d6 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -4,6 +4,8 @@ import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Optional +import com.apollographql.apollo.network.http.HttpNetworkTransport +import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.internal.runTest import com.benasher44.uuid.uuid4 import defer.CanDeferFragmentsOnTheTopLevelQueryFieldQuery @@ -44,7 +46,12 @@ class DeferWithApolloServerTest { private fun setUp() { apolloClient = ApolloClient.Builder() - .serverUrl("http://127.0.0.1:4000/") + .networkTransport( + HttpNetworkTransport.Builder() + .serverUrl("http://127.0.0.1:4000/") + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .build() + ) .build() } diff --git a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt index a300ee18e93..0eb43420170 100644 --- a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt +++ b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt @@ -72,8 +72,11 @@ class DeferJvmTest { } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental":[{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental":[{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) for ((index, json) in jsonList.withIndex()) { @@ -98,7 +101,9 @@ class DeferJvmTest { ) ), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", ScreenFields(true)) + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) ) ), ) From eda719547663f0175474f6ca387551b12c864d2d Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 25 Sep 2025 11:48:54 +0200 Subject: [PATCH 11/38] Simplify CI job --- .github/workflows/defer-integration-tests.yml | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/defer-integration-tests.yml b/.github/workflows/defer-integration-tests.yml index 313085f6379..16b84fdc9db 100644 --- a/.github/workflows/defer-integration-tests.yml +++ b/.github/workflows/defer-integration-tests.yml @@ -24,8 +24,6 @@ jobs: - run: | ./router --supergraph tests/defer/router/simple-supergraph.graphqls & - - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2 - - env: DEFER_WITH_ROUTER_TESTS: true run: | @@ -34,27 +32,15 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'apollographql/apollo-kotlin' steps: - - name: Checkout project - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 - - name: Install and run graph - working-directory: tests/defer/apollo-server/ + - working-directory: tests/defer/apollo-server/ run: | npm install --legacy-peer-deps npx patch-package APOLLO_PORT=4000 npm start & - - name: Setup Java - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 #v4.2.1 - with: - distribution: 'temurin' - java-version: 17 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@dbbdc275be76ac10734476cc723d82dfe7ec6eda #v3.4.2 - - - name: Run Apollo Kotlin @defer tests - env: + - env: DEFER_WITH_APOLLO_SERVER_TESTS: true run: | ./gradlew --no-daemon --console plain -p tests :defer:allTests From bb1a83ef06525ea15d7f1799720a8f9061019fef Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 25 Sep 2025 12:18:40 +0200 Subject: [PATCH 12/38] Do not expose accept headers in DefaultHttpRequestComposer's public API --- .../api/http/DefaultHttpRequestComposer.kt | 30 +++++-------------- .../network/http/HttpNetworkTransport.kt | 5 ++-- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index 75b990b7daf..19521d0faf6 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -40,15 +40,13 @@ import okio.buffer class DefaultHttpRequestComposer( private val serverUrl: String, private val enablePostCaching: Boolean, - private val acceptHeaderQueriesAndMutations: String = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824, - private val acceptHeaderSubscriptions: String = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0, + private val acceptHeaderQueriesAndMutations: String = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS, ) : HttpRequestComposer { constructor(serverUrl: String) : this( serverUrl = serverUrl, enablePostCaching = false, - acceptHeaderQueriesAndMutations = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824, - acceptHeaderSubscriptions = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0, + acceptHeaderQueriesAndMutations = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS, ) override fun compose(apolloRequest: ApolloRequest): HttpRequest { @@ -57,7 +55,7 @@ class DefaultHttpRequestComposer( val requestHeaders = mutableListOf().apply { if (apolloRequest.operation is Subscription<*>) { - add(HttpHeader(HEADER_ACCEPT_NAME, acceptHeaderSubscriptions)) + add(HttpHeader(HEADER_ACCEPT_NAME, HEADER_ACCEPT_VALUE_SUBSCRIPTIONS)) } else { add(HttpHeader(HEADER_ACCEPT_NAME, acceptHeaderQueriesAndMutations)) } @@ -127,25 +125,11 @@ class DefaultHttpRequestComposer( // and thus is safe to execute. // See https://www.apollographql.com/docs/apollo-server/security/cors/#preventing-cross-site-request-forgery-csrf // for details. - internal val HEADER_APOLLO_REQUIRE_PREFLIGHT = "Apollo-Require-Preflight" + private const val HEADER_APOLLO_REQUIRE_PREFLIGHT = "Apollo-Require-Preflight" - val HEADER_ACCEPT_NAME = "Accept" - - const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 = - "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - - // TODO To be agreed upon with the router and other clients - const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 = - "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" - - const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 = - "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" - - @Deprecated("Use HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 instead", ReplaceWith("HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0")) - val HEADER_ACCEPT_VALUE_MULTIPART = HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 - - @Deprecated("Use HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 instead", ReplaceWith("HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824")) - val HEADER_ACCEPT_VALUE_DEFER = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 + private const val HEADER_ACCEPT_NAME = "Accept" + private const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + private const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS = "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" private fun buildGetUrl( serverUrl: String, diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 919f31b0ab6..a113303ed48 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -427,7 +427,7 @@ private constructor( */ object Defer20220824 : IncrementalDeliveryProtocol { @ApolloInternal - override val acceptHeader: String = DefaultHttpRequestComposer.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 + override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" @ApolloInternal override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Defer20220824IncrementalResultsMerger() @@ -440,7 +440,8 @@ private constructor( */ object GraphQL17Alpha9 : IncrementalDeliveryProtocol { @ApolloInternal - override val acceptHeader: String = DefaultHttpRequestComposer.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 + // TODO To be agreed upon with the router and other clients + override val acceptHeader: String = "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" @ApolloInternal override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() From e6082496bb62fb80453f28519a04834aeb98d37e Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 25 Sep 2025 15:34:13 +0200 Subject: [PATCH 13/38] Make more symbols internal --- .../Defer20220824IncrementalResultsMerger.kt | 4 +- ...GraphQL17Alpha9IncrementalResultsMerger.kt | 4 +- .../IncrementalDeliveryProtocolImpl.kt | 42 +++++++++++++++++++ .../incremental/IncrementalResultsMerger.kt | 5 +-- .../network/http/HttpNetworkTransport.kt | 42 +++++++------------ 5 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt index e9db40042f9..760d5311655 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt @@ -1,6 +1,5 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.IncrementalResultIdentifier import com.apollographql.apollo.api.IncrementalResultIdentifiers import okio.BufferedSource @@ -8,9 +7,8 @@ import okio.BufferedSource /** * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.Defer20220824] protocol format. */ -@ApolloInternal @Suppress("UNCHECKED_CAST") -class Defer20220824IncrementalResultsMerger : IncrementalResultsMerger { +internal class Defer20220824IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt index 5d4b88b2dca..bb5c33e3e6a 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt @@ -1,6 +1,5 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.IncrementalResultIdentifier import com.apollographql.apollo.api.IncrementalResultIdentifiers import com.apollographql.apollo.api.pending @@ -9,9 +8,8 @@ import okio.BufferedSource /** * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9] protocol format. */ -@ApolloInternal @Suppress("UNCHECKED_CAST") -class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerger { +internal class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt new file mode 100644 index 00000000000..97e3ba66a95 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -0,0 +1,42 @@ +package com.apollographql.apollo.internal.incremental + +import com.apollographql.apollo.network.http.HttpNetworkTransport + +internal sealed interface IncrementalDeliveryProtocolImpl { + val acceptHeader: String + + fun newIncrementalResultsMerger(): IncrementalResultsMerger + + /** + * Format specified in this historical commit: + * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec + * + * Only `@defer` is supported with this format. + * + * This is the default. + */ + object Defer20220824 : IncrementalDeliveryProtocolImpl { + override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Defer20220824IncrementalResultsMerger() + } + + /** + * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. + * + * Both `@defer` and `@stream` are supported with this format. + */ + object GraphQL17Alpha9 : IncrementalDeliveryProtocolImpl { + // TODO To be agreed upon with the router and other clients + override val acceptHeader: String = + "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" + + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + } +} + +internal val HttpNetworkTransport.IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl + get() = when (this) { + HttpNetworkTransport.IncrementalDeliveryProtocol.Defer20220824 -> IncrementalDeliveryProtocolImpl.Defer20220824 + HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 + } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt index c2fa48e989f..fc9ecd23446 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt @@ -1,6 +1,5 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.IncrementalResultIdentifiers import okio.BufferedSource @@ -18,8 +17,8 @@ import okio.BufferedSource * `extensions` in incremental results (if present) are merged together in an array and then set to the `extensions` field of the [merged] * Map. */ -@ApolloInternal -interface IncrementalResultsMerger { + +internal sealed interface IncrementalResultsMerger { val merged: JsonMap val incrementalResultIdentifiers: IncrementalResultIdentifiers diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index a113303ed48..47e44f1a317 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -1,7 +1,6 @@ package com.apollographql.apollo.network.http import com.apollographql.apollo.annotations.ApolloDeprecatedSince -import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters @@ -22,9 +21,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.RouterError -import com.apollographql.apollo.internal.incremental.Defer20220824IncrementalResultsMerger -import com.apollographql.apollo.internal.incremental.GraphQL17Alpha9IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.IncrementalDeliveryProtocolImpl import com.apollographql.apollo.internal.incremental.IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.impl import com.apollographql.apollo.internal.isGraphQLResponse import com.apollographql.apollo.internal.isMultipart import com.apollographql.apollo.internal.multipartBodyFlow @@ -47,7 +46,7 @@ private constructor( private val engine: HttpEngine, val interceptors: List, private val exposeErrorBody: Boolean, - private val incrementalDeliveryProtocol: IncrementalDeliveryProtocol, + private val incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl, ) : NetworkTransport { private val engineInterceptor = EngineInterceptor() @@ -223,7 +222,7 @@ private constructor( } } else { if (incrementalResultsMerger == null) { - incrementalResultsMerger = incrementalDeliveryProtocol.newIncrementalResultsMerger() + incrementalResultsMerger = incrementalDeliveryProtocolImpl.newIncrementalResultsMerger() } val merged = incrementalResultsMerger.merge(part) val deferredFragmentIds = incrementalResultsMerger.incrementalResultIdentifiers @@ -358,6 +357,11 @@ private constructor( this.engine = httpEngine } + /** + * The incremental delivery protocol to use when using `@defer` and/or `@stream`. + * + * Default: [IncrementalDeliveryProtocol.Defer20220824] + */ fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { this.incrementalDeliveryProtocol = incrementalDeliveryProtocol } @@ -379,7 +383,7 @@ private constructor( ?: serverUrl?.let { DefaultHttpRequestComposer( serverUrl = it, - acceptHeaderQueriesAndMutations = incrementalDeliveryProtocol.acceptHeader, + acceptHeaderQueriesAndMutations = incrementalDeliveryProtocol.impl.acceptHeader, ) } ?: error("No HttpRequestComposer found. Use 'httpRequestComposer' or 'serverUrl'") @@ -393,7 +397,7 @@ private constructor( engine = engine ?: DefaultHttpEngine(), interceptors = interceptors, exposeErrorBody = exposeErrorBody, - incrementalDeliveryProtocol = incrementalDeliveryProtocol, + incrementalDeliveryProtocolImpl = incrementalDeliveryProtocol.impl, ) } } @@ -410,12 +414,7 @@ private constructor( /** * The protocol to use for incremental delivery (`@defer` and `@stream`). */ - sealed interface IncrementalDeliveryProtocol { - @ApolloInternal - val acceptHeader: String - - @ApolloInternal - fun newIncrementalResultsMerger(): IncrementalResultsMerger + enum class IncrementalDeliveryProtocol { /** * Format specified in this historical commit: @@ -425,26 +424,13 @@ private constructor( * * This is the default. */ - object Defer20220824 : IncrementalDeliveryProtocol { - @ApolloInternal - override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - - @ApolloInternal - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Defer20220824IncrementalResultsMerger() - } + Defer20220824, /** * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. * * Both `@defer` and `@stream` are supported with this format. */ - object GraphQL17Alpha9 : IncrementalDeliveryProtocol { - @ApolloInternal - // TODO To be agreed upon with the router and other clients - override val acceptHeader: String = "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" - - @ApolloInternal - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() - } + GraphQL17Alpha9 } } From 26399deb1c0d1cf521f80478ac45f9755d3d86d7 Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 25 Sep 2025 15:43:15 +0200 Subject: [PATCH 14/38] Mark IncrementalDeliveryProtocol @ApolloExperimental --- .../apollographql/apollo/network/http/HttpNetworkTransport.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 47e44f1a317..ac57be2df85 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -1,6 +1,7 @@ package com.apollographql.apollo.network.http import com.apollographql.apollo.annotations.ApolloDeprecatedSince +import com.apollographql.apollo.annotations.ApolloExperimental import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters @@ -362,6 +363,7 @@ private constructor( * * Default: [IncrementalDeliveryProtocol.Defer20220824] */ + @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { this.incrementalDeliveryProtocol = incrementalDeliveryProtocol } @@ -414,6 +416,7 @@ private constructor( /** * The protocol to use for incremental delivery (`@defer` and `@stream`). */ + @ApolloExperimental enum class IncrementalDeliveryProtocol { /** From 388c2cacf456ad7520fded72a1ba8b85a0026b0d Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 25 Sep 2025 16:20:19 +0200 Subject: [PATCH 15/38] Rename Defer20220824 protocol to GraphQL17Alpha2 fpr consistency --- .../com/apollographql/apollo/api/BooleanExpression.kt | 2 +- ...r.kt => GraphQL17Alpha2IncrementalResultsMerger.kt} | 4 ++-- .../incremental/IncrementalDeliveryProtocolImpl.kt | 4 ++-- .../apollo/network/http/HttpNetworkTransport.kt | 8 ++++---- ... => GraphQL17Alpha2IncrementalResultsMergerTest.kt} | 10 +++++----- ...t.kt => DeferGraphQL17Alpha2NormalizedCacheTest.kt} | 2 +- ...efer20220824Test.kt => DeferGraphQL17Alpha2Test.kt} | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/{Defer20220824IncrementalResultsMerger.kt => GraphQL17Alpha2IncrementalResultsMerger.kt} (94%) rename libraries/apollo-runtime/src/commonTest/kotlin/test/defer/{Defer20220824IncrementalResultsMergerTest.kt => GraphQL17Alpha2IncrementalResultsMergerTest.kt} (97%) rename tests/defer/src/commonTest/kotlin/test/{Defer20220824NormalizedCacheTest.kt => DeferGraphQL17Alpha2NormalizedCacheTest.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{Defer20220824Test.kt => DeferGraphQL17Alpha2Test.kt} (99%) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index 8c33d3e88ad..afb109b7335 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -95,7 +95,7 @@ private fun shouldParseFragment( // Modern protocol: parse fragments that are _not_ pending !deferredFragmentIdentifiers.contains(identifier) } else { - // Legacy 20220824 protocol: parse fragments that have been merged + // Legacy GraphQL17Alpha2 protocol: parse fragments that have been merged deferredFragmentIdentifiers.contains(identifier) } } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt similarity index 94% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt index 760d5311655..159823a6206 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Defer20220824IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt @@ -5,10 +5,10 @@ import com.apollographql.apollo.api.IncrementalResultIdentifiers import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.Defer20220824] protocol format. + * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2] protocol format. */ @Suppress("UNCHECKED_CAST") -internal class Defer20220824IncrementalResultsMerger : IncrementalResultsMerger { +internal class GraphQL17Alpha2IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index 97e3ba66a95..e70f297582d 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -18,7 +18,7 @@ internal sealed interface IncrementalDeliveryProtocolImpl { object Defer20220824 : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Defer20220824IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() } /** @@ -37,6 +37,6 @@ internal sealed interface IncrementalDeliveryProtocolImpl { internal val HttpNetworkTransport.IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - HttpNetworkTransport.IncrementalDeliveryProtocol.Defer20220824 -> IncrementalDeliveryProtocolImpl.Defer20220824 + HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.Defer20220824 HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index ac57be2df85..2a831ecb326 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -314,7 +314,7 @@ private constructor( private var engine: HttpEngine? = null private val interceptors: MutableList = mutableListOf() private var exposeErrorBody: Boolean = false - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.Defer20220824 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 private val headers: MutableList = mutableListOf() fun httpRequestComposer(httpRequestComposer: HttpRequestComposer) = apply { @@ -361,7 +361,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.Defer20220824] + * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { @@ -420,14 +420,14 @@ private constructor( enum class IncrementalDeliveryProtocol { /** - * Format specified in this historical commit: + * Newer format as implemented by graphql.js version `17.0.0-alpha.2` and specified in this historical commit: * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec * * Only `@defer` is supported with this format. * * This is the default. */ - Defer20220824, + GraphQL17Alpha2, /** * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt similarity index 97% rename from libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt rename to libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt index e33a5e34f8f..85d5d25b538 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/Defer20220824IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt @@ -6,7 +6,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.Defer20220824IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.GraphQL17Alpha2IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -18,10 +18,10 @@ private fun String.buffer() = Buffer().writeUtf8(this) @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map -class Defer20220824IncrementalResultsMergerTest { +class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -392,7 +392,7 @@ class Defer20220824IncrementalResultsMergerTest { @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -668,7 +668,7 @@ class Defer20220824IncrementalResultsMergerTest { @Test fun emptyPayloads() { - val incrementalResultsMerger = Defer20220824IncrementalResultsMerger() + val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() //language=JSON val payload1 = """ diff --git a/tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt index be5cb5ac1e4..515337cc662 100644 --- a/tests/defer/src/commonTest/kotlin/test/Defer20220824NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt @@ -45,7 +45,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class Defer20220824NormalizedCacheTest { +class DeferGraphQL17Alpha2NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore diff --git a/tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt rename to tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt index 1feaf99fa5f..c4c232b1a0f 100644 --- a/tests/defer/src/commonTest/kotlin/test/Defer20220824Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt @@ -25,7 +25,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class Defer20220824Test { +class DeferGraphQL17Alpha2Test { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient From 69ecb146420c719b1fcc93420c7c07fc9586de04 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 10:08:55 +0200 Subject: [PATCH 16/38] Keep symbols public but deprecated --- .../api/http/DefaultHttpRequestComposer.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index 19521d0faf6..f4ac4a661c7 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -54,11 +54,7 @@ class DefaultHttpRequestComposer( val customScalarAdapters = apolloRequest.executionContext[CustomScalarAdapters] ?: CustomScalarAdapters.Empty val requestHeaders = mutableListOf().apply { - if (apolloRequest.operation is Subscription<*>) { - add(HttpHeader(HEADER_ACCEPT_NAME, HEADER_ACCEPT_VALUE_SUBSCRIPTIONS)) - } else { - add(HttpHeader(HEADER_ACCEPT_NAME, acceptHeaderQueriesAndMutations)) - } + add(HttpHeader("Accept", if (apolloRequest.operation is Subscription<*>) HEADER_ACCEPT_VALUE_SUBSCRIPTIONS else acceptHeaderQueriesAndMutations)) if (apolloRequest.httpHeaders != null) { addAll(apolloRequest.httpHeaders) } @@ -127,9 +123,22 @@ class DefaultHttpRequestComposer( // for details. private const val HEADER_APOLLO_REQUIRE_PREFLIGHT = "Apollo-Require-Preflight" - private const val HEADER_ACCEPT_NAME = "Accept" - private const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - private const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS = "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + @Deprecated("This was made public by mistake and will be removed in a future version, please use your own constants instead") + @ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v5_0_0) + val HEADER_ACCEPT_NAME = "Accept" + + @Deprecated("This was made public by mistake and will be removed in a future version, please use your own constants instead") + @ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v5_0_0) + val HEADER_ACCEPT_VALUE_DEFER = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + + @Deprecated("This was made public by mistake and will be removed in a future version, please use your own constants instead") + @ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v5_0_0) + val HEADER_ACCEPT_VALUE_MULTIPART = "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + + private const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS = + "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + private const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS = + "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" private fun buildGetUrl( serverUrl: String, From 4d9b9f2f9133da6104a9d756ec43dd88498df910 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 11:38:13 +0200 Subject: [PATCH 17/38] Let HttpNetworkTransport handle the Accept header --- .../api/http/DefaultHttpRequestComposer.kt | 24 +++++++++---------- .../network/http/HttpNetworkTransport.kt | 18 +++++++++----- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index f4ac4a661c7..c455add9721 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -40,24 +40,29 @@ import okio.buffer class DefaultHttpRequestComposer( private val serverUrl: String, private val enablePostCaching: Boolean, - private val acceptHeaderQueriesAndMutations: String = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS, ) : HttpRequestComposer { - constructor(serverUrl: String) : this( - serverUrl = serverUrl, - enablePostCaching = false, - acceptHeaderQueriesAndMutations = HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS, - ) + constructor(serverUrl: String) : this(serverUrl, false) override fun compose(apolloRequest: ApolloRequest): HttpRequest { val operation = apolloRequest.operation val customScalarAdapters = apolloRequest.executionContext[CustomScalarAdapters] ?: CustomScalarAdapters.Empty val requestHeaders = mutableListOf().apply { - add(HttpHeader("Accept", if (apolloRequest.operation is Subscription<*>) HEADER_ACCEPT_VALUE_SUBSCRIPTIONS else acceptHeaderQueriesAndMutations)) if (apolloRequest.httpHeaders != null) { addAll(apolloRequest.httpHeaders) } + if (get("accept") == null) { + add( + HttpHeader("accept", + if (apolloRequest.operation is Subscription<*>) { + "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + } else { + "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + } + ) + ) + } } val sendApqExtensions = apolloRequest.sendApqExtensions ?: false @@ -135,11 +140,6 @@ class DefaultHttpRequestComposer( @ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v5_0_0) val HEADER_ACCEPT_VALUE_MULTIPART = "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" - private const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS = - "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - private const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS = - "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" - private fun buildGetUrl( serverUrl: String, operation: Operation, diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 2a831ecb326..ba28a000a8a 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -55,6 +55,17 @@ private constructor( request: ApolloRequest, ): Flow> { val customScalarAdapters = request.executionContext[CustomScalarAdapters]!! + + val request = if (request.httpHeaders.orEmpty().none { it.name.lowercase() == "accept" }) { + val accept = if (request.operation is Subscription<*>) { + "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" + } else { + incrementalDeliveryProtocolImpl.acceptHeader + } + request.newBuilder().addHttpHeader("accept", accept).build() + } else { + request + } val httpRequest = httpRequestComposer.compose(request) return execute(request, httpRequest, customScalarAdapters) @@ -382,12 +393,7 @@ private constructor( "It is an error to set both 'httpRequestComposer' and 'serverUrl'" } val composer = httpRequestComposer - ?: serverUrl?.let { - DefaultHttpRequestComposer( - serverUrl = it, - acceptHeaderQueriesAndMutations = incrementalDeliveryProtocol.impl.acceptHeader, - ) - } + ?: serverUrl?.let { DefaultHttpRequestComposer(it) } ?: error("No HttpRequestComposer found. Use 'httpRequestComposer' or 'serverUrl'") if (headers.isNotEmpty()) { From 77c52fbd4bd4d6160b21639001a7f1a3b14dd9dd Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 11:40:33 +0200 Subject: [PATCH 18/38] Rename Defer20220824 protocol to GraphQL17Alpha2 for consistency --- .../IncrementalDeliveryProtocolImpl.kt | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index e70f297582d..4a2aa2ca0f8 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -7,25 +7,12 @@ internal sealed interface IncrementalDeliveryProtocolImpl { fun newIncrementalResultsMerger(): IncrementalResultsMerger - /** - * Format specified in this historical commit: - * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec - * - * Only `@defer` is supported with this format. - * - * This is the default. - */ - object Defer20220824 : IncrementalDeliveryProtocolImpl { + object GraphQL17Alpha2 : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() } - /** - * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. - * - * Both `@defer` and `@stream` are supported with this format. - */ object GraphQL17Alpha9 : IncrementalDeliveryProtocolImpl { // TODO To be agreed upon with the router and other clients override val acceptHeader: String = @@ -37,6 +24,6 @@ internal sealed interface IncrementalDeliveryProtocolImpl { internal val HttpNetworkTransport.IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.Defer20220824 + HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha2 HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 } From 5630434a84781ea78b50ed0ed51ddcfcdaa3e8ec Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 14:29:25 +0200 Subject: [PATCH 19/38] Revert typealiases and use DeferredFragmentIdentifier again --- libraries/apollo-api/api/apollo-api.api | 5 + .../apollo/api/BooleanExpression.kt | 16 +- .../apollo/api/CustomScalarAdapters.kt | 8 +- .../apollo/api/DeferredFragmentIdentifier.kt | 19 +++ .../apollographql/apollo/api/Executables.kt | 6 +- .../apollo/api/IncrementalResultIdentifier.kt | 41 ----- .../apollographql/apollo/api/Operations.kt | 8 +- .../apollo/api/internal/ResponseParser.kt | 4 +- ...GraphQL17Alpha2IncrementalResultsMerger.kt | 11 +- ...GraphQL17Alpha9IncrementalResultsMerger.kt | 10 +- .../incremental/IncrementalResultsMerger.kt | 7 +- .../apollo/internal/incremental/JsonMap.kt | 1 - .../network/http/HttpNetworkTransport.kt | 4 +- ...hQL17Alpha2IncrementalResultsMergerTest.kt | 16 +- ...hQL17Alpha9IncrementalResultsMergerTest.kt | 159 +++++++++--------- ...DeferGraphQL17Alpha2NormalizedCacheTest.kt | 108 +++--------- .../kotlin/test/DeferGraphQL17Alpha2Test.kt | 110 +++--------- .../src/jvmTest/kotlin/test/DeferJvmTest.kt | 10 +- 18 files changed, 197 insertions(+), 346 deletions(-) create mode 100644 libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt delete mode 100644 libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt diff --git a/libraries/apollo-api/api/apollo-api.api b/libraries/apollo-api/api/apollo-api.api index 4c7c43fb3f3..1936bf55553 100644 --- a/libraries/apollo-api/api/apollo-api.api +++ b/libraries/apollo-api/api/apollo-api.api @@ -531,6 +531,7 @@ public final class com/apollographql/apollo/api/DefaultUploadKt { } public final class com/apollographql/apollo/api/DeferredFragmentIdentifier { + public static final field Companion Lcom/apollographql/apollo/api/DeferredFragmentIdentifier$Companion; public fun (Ljava/util/List;Ljava/lang/String;)V public final fun component1 ()Ljava/util/List; public final fun component2 ()Ljava/lang/String; @@ -543,6 +544,10 @@ public final class com/apollographql/apollo/api/DeferredFragmentIdentifier { public fun toString ()Ljava/lang/String; } +public final class com/apollographql/apollo/api/DeferredFragmentIdentifier$Companion { + public final fun getPending ()Lcom/apollographql/apollo/api/DeferredFragmentIdentifier; +} + public final class com/apollographql/apollo/api/EnumType : com/apollographql/apollo/api/CompiledNamedType { public fun (Ljava/lang/String;Ljava/util/List;)V public final fun getValues ()Ljava/util/List; diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index afb109b7335..c9a1cd0a5b7 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -67,7 +67,7 @@ internal fun BooleanExpression.evaluate(block: (T) -> Boolean): Boo fun BooleanExpression.evaluate( variables: Set?, typename: String?, - deferredFragmentIdentifiers: IncrementalResultIdentifiers?, + deferredFragmentIdentifiers: Set?, path: List?, ): Boolean { // Remove "data" from the path @@ -75,22 +75,18 @@ fun BooleanExpression.evaluate( return evaluate { when (it) { is BVariable -> !(variables?.contains(it.name) ?: false) - is BLabel -> shouldParseFragment(deferredFragmentIdentifiers = deferredFragmentIdentifiers, path = croppedPath!!, label = it.label) + is BLabel -> shouldParseFragment(deferredFragmentIdentifiers, croppedPath!!, it.label) is BPossibleTypes -> it.possibleTypes.contains(typename) } } } -private fun shouldParseFragment( - deferredFragmentIdentifiers: IncrementalResultIdentifiers?, - path: List, - label: String?, -): Boolean { +private fun shouldParseFragment(deferredFragmentIdentifiers: Set?, path: List, label: String?): Boolean { if (deferredFragmentIdentifiers == null) { // By default, parse all deferred fragments - this is the case when parsing from the normalized cache. return true } - val identifier = IncrementalResultIdentifier(path, label) + val identifier = DeferredFragmentIdentifier(path, label) return if (deferredFragmentIdentifiers.isPending()) { // Modern protocol: parse fragments that are _not_ pending !deferredFragmentIdentifiers.contains(identifier) @@ -100,6 +96,10 @@ private fun shouldParseFragment( } } +private fun Set.isPending(): Boolean { + return any { it === DeferredFragmentIdentifier.Pending } +} + /** * A generic term in a [BooleanExpression] */ diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt index 324d58c95a6..a684756cf4b 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt @@ -19,10 +19,10 @@ class CustomScalarAdapters private constructor( @JvmField val falseVariables: Set?, /** - * Incremental result identifiers used to determine whether the parser must parse deferred fragments + * Identifiers used to determine whether the parser must parse deferred fragments */ @JvmField - val deferredFragmentIdentifiers: IncrementalResultIdentifiers?, + val deferredFragmentIdentifiers: Set?, /** * Errors to use with @catch */ @@ -131,14 +131,14 @@ class CustomScalarAdapters private constructor( class Builder { private val adaptersMap: MutableMap> = mutableMapOf() private var falseVariables: Set? = null - private var deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null + private var deferredFragmentIdentifiers: Set? = null private var errors: List? = null fun falseVariables(falseVariables: Set?) = apply { this.falseVariables = falseVariables } - fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: IncrementalResultIdentifiers?) = apply { + fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set?) = apply { this.deferredFragmentIdentifiers = deferredFragmentIdentifiers } diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt new file mode 100644 index 00000000000..a71efb5328c --- /dev/null +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/DeferredFragmentIdentifier.kt @@ -0,0 +1,19 @@ +package com.apollographql.apollo.api + +import com.apollographql.apollo.annotations.ApolloInternal + +data class DeferredFragmentIdentifier( + /** + * Path of the fragment in the overall JSON response. The elements can either be Strings (names) or Integers (array indices). + */ + val path: List, + val label: String?, +) { + companion object { + /** + * Special identifier to signal that the identifiers are pending, as in the modern version of the protocol. + */ + @ApolloInternal + val Pending = DeferredFragmentIdentifier(emptyList(), "__pending") + } +} diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt index 0cd35e4c604..28d0fe1c090 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt @@ -71,8 +71,8 @@ fun Executable.parseData( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, falseVariables: Set? = null, - deferredFragmentIds: IncrementalResultIdentifiers? = null, - errors: List? = null, + deferredFragmentIds: Set? = null, + errors: List? = null ): D? { val customScalarAdapters1 = customScalarAdapters.newBuilder() .falseVariables(falseVariables) @@ -89,4 +89,4 @@ fun Executable.composeData( value: D ) { adapter().toJson(jsonWriter, customScalarAdapters, value) -} +} \ No newline at end of file diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt deleted file mode 100644 index b2455047c37..00000000000 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/IncrementalResultIdentifier.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.apollographql.apollo.api - -import com.apollographql.apollo.annotations.ApolloInternal - -data class DeferredFragmentIdentifier( - /** - * Path of the fragment in the overall JSON response. The elements can either be Strings (names) or Integers (array indices). - */ - val path: List, - val label: String?, -) { - internal companion object { - /** - * Special identifier to signal that the identifiers are pending, as in the modern version of the protocol. - */ - internal val Pending = DeferredFragmentIdentifier(emptyList(), "__pending") - } -} - -/** - * Identifies an incremental result. - * [DeferredFragmentIdentifier] is kept to preserve the API/ABI, but this alias is more descriptive of its purpose. - */ -typealias IncrementalResultIdentifier = DeferredFragmentIdentifier - -typealias IncrementalResultIdentifiers = Set - -@ApolloInternal -fun IncrementalResultIdentifiers.isPending(): Boolean { - return any { it === DeferredFragmentIdentifier.Pending } -} - -@ApolloInternal -fun IncrementalResultIdentifiers.pending(): IncrementalResultIdentifiers { - return this + DeferredFragmentIdentifier.Pending -} - -@ApolloInternal -fun IncrementalResultIdentifiers.nonPending(): IncrementalResultIdentifiers { - return filter { it !== DeferredFragmentIdentifier.Pending }.toSet() -} diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt index b9e4414818c..9dc586f8147 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt @@ -70,7 +70,7 @@ fun Operation.composeJsonRequest( fun Operation.parseJsonResponse( jsonReader: JsonReader, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return jsonReader.use { ResponseParser.parse( @@ -103,7 +103,7 @@ fun Operation.parseResponse( jsonReader: JsonReader, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return try { ResponseParser.parse( @@ -177,7 +177,7 @@ fun JsonReader.toApolloResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return use { try { @@ -213,7 +213,7 @@ fun JsonReader.parseResponse( operation: Operation, requestUuid: Uuid? = null, customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, - deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null, + deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return try { ResponseParser.parse( diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt index 0c92b233578..f60fa9d5bcc 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/internal/ResponseParser.kt @@ -3,8 +3,8 @@ package com.apollographql.apollo.api.internal import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters +import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.Error -import com.apollographql.apollo.api.IncrementalResultIdentifiers import com.apollographql.apollo.api.Operation import com.apollographql.apollo.api.falseVariables import com.apollographql.apollo.api.json.JsonReader @@ -24,7 +24,7 @@ internal object ResponseParser { operation: Operation, requestUuid: Uuid?, customScalarAdapters: CustomScalarAdapters, - deferredFragmentIds: IncrementalResultIdentifiers?, + deferredFragmentIds: Set?, ): ApolloResponse { jsonReader.beginObject() diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt index 159823a6206..70e4a7ab924 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt @@ -1,7 +1,6 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.api.IncrementalResultIdentifier -import com.apollographql.apollo.api.IncrementalResultIdentifiers +import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** @@ -12,12 +11,12 @@ internal class GraphQL17Alpha2IncrementalResultsMerger : IncrementalResultsMerge private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged - private val _incrementalResultIds = mutableSetOf() + private val _deferredFragmentIdentifiers = mutableSetOf() /** * For this protocol, this represents the set of fragment ids that are already merged. */ - override val incrementalResultIdentifiers: IncrementalResultIdentifiers = _incrementalResultIds + override val deferredFragmentIdentifiers: Set = _deferredFragmentIdentifiers override var hasNext: Boolean = true private set @@ -77,13 +76,13 @@ internal class GraphQL17Alpha2IncrementalResultsMerger : IncrementalResultsMerge val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap deepMergeObject(nodeToMergeInto, data) - _incrementalResultIds += IncrementalResultIdentifier(path = path, label = incrementalResult["label"] as String?) + _deferredFragmentIdentifiers += DeferredFragmentIdentifier(path = path, label = incrementalResult["label"] as String?) } } override fun reset() { _merged.clear() - _incrementalResultIds.clear() + _deferredFragmentIdentifiers.clear() hasNext = true isEmptyResponse = false } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt index bb5c33e3e6a..f6ef485146b 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt @@ -1,8 +1,6 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.api.IncrementalResultIdentifier -import com.apollographql.apollo.api.IncrementalResultIdentifiers -import com.apollographql.apollo.api.pending +import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** @@ -16,12 +14,12 @@ internal class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerge /** * Map of identifiers to their corresponding IncrementalResultIdentifier, found in `pending`. */ - private val _pendingResultIds = mutableMapOf() + private val _pendingResultIds = mutableMapOf() /** * For this protocol, this represents the set of ids that are pending. */ - override val incrementalResultIdentifiers: IncrementalResultIdentifiers get() = _pendingResultIds.values.toSet().pending() + override val deferredFragmentIdentifiers: Set get() = _pendingResultIds.values.toSet() + DeferredFragmentIdentifier.Pending override var hasNext: Boolean = true private set @@ -74,7 +72,7 @@ internal class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerge val id = pendingResult["id"] as String val path = pendingResult["path"] as List val label = pendingResult["label"] as String? - _pendingResultIds[id] = IncrementalResultIdentifier(path = path, label = label) + _pendingResultIds[id] = DeferredFragmentIdentifier(path = path, label = label) } } } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt index fc9ecd23446..5577849e335 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalResultsMerger.kt @@ -1,12 +1,12 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.api.IncrementalResultIdentifiers +import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** * Utility for merging GraphQL incremental results received in multiple chunks when using the `@defer` and/or `@stream` directives. * - * Each call to [merge] will merge the given results into the [merged] Map, and will also update [incrementalResultIdentifiers] with the + * Each call to [merge] will merge the given results into the [merged] Map, and will also update [deferredFragmentIdentifiers] with the * value of their `path` and `label` fields. * * The fields in `data` are merged into the node found in [merged] at the path known by looking at the `id` field. For the first call to @@ -17,11 +17,10 @@ import okio.BufferedSource * `extensions` in incremental results (if present) are merged together in an array and then set to the `extensions` field of the [merged] * Map. */ - internal sealed interface IncrementalResultsMerger { val merged: JsonMap - val incrementalResultIdentifiers: IncrementalResultIdentifiers + val deferredFragmentIdentifiers: Set val hasNext: Boolean diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt index a7aea249326..3d9d62ac17e 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/JsonMap.kt @@ -9,7 +9,6 @@ import okio.BufferedSource internal typealias JsonMap = Map internal typealias MutableJsonMap = MutableMap - /** * Find the node in the [map] at the given [path]. * @param path The path to the node to find, as a list of either `String` (name of field in object) or `Int` (index of element in array). diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index ba28a000a8a..1da82bcbd42 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -237,7 +237,7 @@ private constructor( incrementalResultsMerger = incrementalDeliveryProtocolImpl.newIncrementalResultsMerger() } val merged = incrementalResultsMerger.merge(part) - val deferredFragmentIds = incrementalResultsMerger.incrementalResultIdentifiers + val deferredFragmentIdentifiers = incrementalResultsMerger.deferredFragmentIdentifiers val isLast = !incrementalResultsMerger.hasNext if (incrementalResultsMerger.isEmptyResponse) { @@ -246,7 +246,7 @@ private constructor( merged.jsonReader().toApolloResponse( operation = operation, customScalarAdapters = customScalarAdapters, - deferredFragmentIdentifiers = deferredFragmentIds + deferredFragmentIdentifiers = deferredFragmentIdentifiers ).newBuilder().isLast(isLast).build() } } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt index 85d5d25b538..b5226574b78 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt @@ -49,7 +49,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -121,7 +121,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { setOf( DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -198,7 +198,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -287,7 +287,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -386,7 +386,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) } @@ -420,7 +420,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -522,7 +522,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -662,7 +662,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers + incrementalResultsMerger.deferredFragmentIdentifiers ) } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt index cf07c635dc3..796f3d039e0 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt @@ -3,10 +3,9 @@ package test.defer import com.apollographql.apollo.annotations.ApolloInternal -import com.apollographql.apollo.api.IncrementalResultIdentifier +import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.api.nonPending import com.apollographql.apollo.internal.incremental.GraphQL17Alpha9IncrementalResultsMerger import okio.Buffer import kotlin.test.Test @@ -81,9 +80,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -159,9 +158,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0") + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0") ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -241,9 +240,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -339,10 +338,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -447,9 +446,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -522,10 +521,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - IncrementalResultIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -627,10 +626,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - IncrementalResultIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -755,9 +754,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -902,9 +901,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf(), label = null), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -962,7 +961,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1022,9 +1021,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = "D1"), + DeferredFragmentIdentifier(path = listOf(), label = "D1"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1088,9 +1087,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("f2", "c", "f"), label = "D2"), + DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1141,7 +1140,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1200,10 +1199,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = "Blue"), - IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1255,9 +1254,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1308,7 +1307,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1367,10 +1366,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = "Blue"), - IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1428,9 +1427,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("a", "b"), label = "Red"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1478,7 +1477,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1521,10 +1520,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = null), - IncrementalResultIdentifier(path = listOf("me"), label = null), + DeferredFragmentIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf("me"), label = null), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1619,9 +1618,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf(), label = null), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1706,7 +1705,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1746,9 +1745,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "B"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1786,7 +1785,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -1849,10 +1848,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "Billing"), - IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), + DeferredFragmentIdentifier(path = listOf("me"), label = "Billing"), + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1899,9 +1898,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "Prev"), + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -1955,7 +1954,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -2000,10 +1999,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf(), label = "A"), - IncrementalResultIdentifier(path = listOf("me"), label = "B"), + DeferredFragmentIdentifier(path = listOf(), label = "A"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2059,9 +2058,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "B"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2125,9 +2124,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("me"), label = "B"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -2195,10 +2194,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2242,10 +2241,10 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2285,9 +2284,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2340,7 +2339,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) assertEquals( setOf(), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } @@ -2393,9 +2392,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2435,9 +2434,9 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) //language=JSON @@ -2503,9 +2502,13 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - IncrementalResultIdentifier(path = listOf("person", "films"), label = "filmsStream"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), ), - incrementalResultsMerger.incrementalResultIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() ) } } + +private fun Set.nonPending(): Set { + return filter { it !== DeferredFragmentIdentifier.Pending }.toSet() +} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt index 515337cc662..9ca3d8da8b2 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt @@ -88,11 +88,7 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { val cacheExpected = WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ) assertEquals(cacheExpected, cacheActual) } @@ -122,19 +118,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -163,19 +152,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -210,19 +192,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -259,19 +234,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -296,19 +264,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null) - ) - ) - ) + ComputerFields.Screen("Screen", "800x600", null)))) ), WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ) - ) + ScreenFields(true))))) ), ) @@ -339,20 +300,15 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { uuid, ).data(WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ) - ).build(), + )).build(), ApolloResponse.Builder( query, uuid, ).data(WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) - ) - ).build(), + ComputerFields.Screen("Screen", "640x480", null)))) + )).build(), ApolloResponse.Builder( query, @@ -361,10 +317,7 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { .data( WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ) ) .errors( @@ -398,20 +351,15 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { uuid, ).data(WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ) - ).build(), + )).build(), ApolloResponse.Builder( query, uuid, ).data(WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) - ) - ).build(), + ComputerFields.Screen("Screen", "640x480", null)))) + )).build(), ) apolloClient = ApolloClient.Builder() @@ -430,8 +378,7 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { emit(ApolloResponse.Builder(requestUuid = uuid, operation = query) .exception(ApolloNetworkException("Network error")) .isLast(true) - .build() as ApolloResponse - ) + .build() as ApolloResponse) } } @@ -471,19 +418,12 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { ), WithFragmentSpreadsMutation.Data( listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) + ComputerFields.Screen("Screen", "640x480", null)))) ), WithFragmentSpreadsMutation.Data( listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -495,11 +435,7 @@ class DeferGraphQL17Alpha2NormalizedCacheTest { val cacheExpected = WithFragmentSpreadsQuery.Data( listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + ScreenFields(false))))) ) assertEquals(cacheExpected, cacheActual) } diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt index c4c232b1a0f..155b785f0fa 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt @@ -60,52 +60,35 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) ), WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null) - ) - ), + ComputerFields.Screen("Screen", "800x600", null))), ) ), WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null) - ) - ), + ComputerFields.Screen("Screen", "800x600", null))), ) ), WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), ) @@ -135,52 +118,35 @@ class DeferGraphQL17Alpha2Test { WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null) - ) - ), + WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), ) ), WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null) - ) - ), + WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null) - ) - ), + WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), ) ), WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false) - ) - ) - ), + WithInlineFragmentsQuery.OnScreen(false)))), WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null) - ) - ), + WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), ) ), WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false) - ) - ) - ), + WithInlineFragmentsQuery.OnScreen(false)))), WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, WithInlineFragmentsQuery.Screen("Screen", "800x600", - WithInlineFragmentsQuery.OnScreen(true) - ) - ) - ), + WithInlineFragmentsQuery.OnScreen(true)))), ) ), ) @@ -212,8 +178,7 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) - ) - ).build(), + )).build(), ApolloResponse.Builder( query, @@ -222,9 +187,7 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) ) @@ -238,9 +201,7 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) ) @@ -262,13 +223,9 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null) - ) - ), + ComputerFields.Screen("Screen", "800x600", null))), ) ) ).build(), @@ -280,15 +237,10 @@ class DeferGraphQL17Alpha2Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ) ).build(), @@ -389,16 +341,10 @@ class DeferGraphQL17Alpha2Test { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), finalResponse.dataOrThrow() @@ -428,16 +374,10 @@ class DeferGraphQL17Alpha2Test { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), finalResponse.dataOrThrow() diff --git a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt index 0eb43420170..f102bb3238f 100644 --- a/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt +++ b/tests/defer/src/jvmTest/kotlin/test/DeferJvmTest.kt @@ -96,16 +96,10 @@ class DeferJvmTest { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ) From 5249d9ca382fa79716e34bd6d7b7dd03b87f75d2 Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 15:30:07 +0200 Subject: [PATCH 20/38] Revert handling incremental results in WebSocket --- ...GraphQL17Alpha2IncrementalResultsMerger.kt | 2 +- ...GraphQL17Alpha9IncrementalResultsMerger.kt | 2 +- .../IncrementalDeliveryProtocolImpl.kt | 8 +-- .../network/IncrementalDeliveryProtocol.kt | 27 ++++++++++ .../network/http/HttpNetworkTransport.kt | 25 +-------- .../websocket/WebSocketNetworkTransport.kt | 51 ++++++++++++++++--- .../network/ws/WebSocketNetworkTransport.kt | 41 ++++++++++++++- ...DeferGraphQL17Alpha9NormalizedCacheTest.kt | 2 +- .../kotlin/test/DeferGraphQL17Alpha9Test.kt | 2 +- .../kotlin/test/DeferWithApolloServerTest.kt | 2 +- 10 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt index 70e4a7ab924..b741a6df394 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt @@ -4,7 +4,7 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.GraphQL17Alpha2] protocol format. */ @Suppress("UNCHECKED_CAST") internal class GraphQL17Alpha2IncrementalResultsMerger : IncrementalResultsMerger { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt index f6ef485146b..07952b1d227 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt @@ -4,7 +4,7 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.GraphQL17Alpha9] protocol format. */ @Suppress("UNCHECKED_CAST") internal class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerger { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index 4a2aa2ca0f8..3bc8da45bfa 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -1,6 +1,6 @@ package com.apollographql.apollo.internal.incremental -import com.apollographql.apollo.network.http.HttpNetworkTransport +import com.apollographql.apollo.network.IncrementalDeliveryProtocol internal sealed interface IncrementalDeliveryProtocolImpl { val acceptHeader: String @@ -22,8 +22,8 @@ internal sealed interface IncrementalDeliveryProtocolImpl { } } -internal val HttpNetworkTransport.IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl +internal val IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha2 - HttpNetworkTransport.IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 + IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha2 + IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt new file mode 100644 index 00000000000..f01d205a1b6 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt @@ -0,0 +1,27 @@ +package com.apollographql.apollo.network + +import com.apollographql.apollo.annotations.ApolloExperimental + +/** + * The protocol to use for incremental delivery (`@defer` and `@stream`). + */ +@ApolloExperimental +enum class IncrementalDeliveryProtocol { + + /** + * Newer format as implemented by graphql.js version `17.0.0-alpha.2` and specified in this historical commit: + * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec + * + * Only `@defer` is supported with this format. + * + * This is the default. + */ + GraphQL17Alpha2, + + /** + * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. + * + * Both `@defer` and `@stream` are supported with this format. + */ + GraphQL17Alpha9 +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 1da82bcbd42..aa558a16ecf 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -29,6 +29,7 @@ import com.apollographql.apollo.internal.isGraphQLResponse import com.apollographql.apollo.internal.isMultipart import com.apollographql.apollo.internal.multipartBodyFlow import com.apollographql.apollo.mpp.currentTimeMillis +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport import com.benasher44.uuid.Uuid import com.benasher44.uuid.uuid4 @@ -418,28 +419,4 @@ private constructor( return chain.proceed(request.newBuilder().addHeaders(headers).build()) } } - - /** - * The protocol to use for incremental delivery (`@defer` and `@stream`). - */ - @ApolloExperimental - enum class IncrementalDeliveryProtocol { - - /** - * Newer format as implemented by graphql.js version `17.0.0-alpha.2` and specified in this historical commit: - * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec - * - * Only `@defer` is supported with this format. - * - * This is the default. - */ - GraphQL17Alpha2, - - /** - * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. - * - * Both `@defer` and `@stream` are supported with this format. - */ - GraphQL17Alpha9 - } } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index 631d40e6dfc..907fa2bc949 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -12,6 +12,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloWebSocketForceCloseException import com.apollographql.apollo.exception.DefaultApolloException import com.apollographql.apollo.exception.SubscriptionOperationException +import com.apollographql.apollo.internal.incremental.IncrementalDeliveryProtocolImpl +import com.apollographql.apollo.internal.incremental.impl +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.websocket.internal.OperationListener import com.apollographql.apollo.network.websocket.internal.WebSocketPool @@ -113,6 +116,7 @@ class WebSocketNetworkTransport private constructor( private var pingInterval: Duration? = null private var idleTimeout: Duration? = null private var parserFactory: SubscriptionParserFactory? = null + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 /** * @param serverUrl a server url that is called every time a WebSocket @@ -176,6 +180,15 @@ class WebSocketNetworkTransport private constructor( this.parserFactory = parserFactory } + /** + * The incremental delivery protocol to use when using `@defer` and/or `@stream`. + * + * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] + */ + @ApolloExperimental + fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { + this.incrementalDeliveryProtocol = incrementalDeliveryProtocol + } /** * Builds the [WebSocketNetworkTransport] @@ -188,19 +201,25 @@ class WebSocketNetworkTransport private constructor( wsProtocol = wsProtocol ?: GraphQLWsProtocol { null }, pingInterval = pingInterval, connectionAcknowledgeTimeout = connectionAcknowledgeTimeout ?: 10.seconds, - parserFactory = parserFactory ?: DefaultSubscriptionParserFactory + parserFactory = parserFactory ?: DefaultSubscriptionParserFactory(incrementalDeliveryProtocol.impl), ) } } } -private object DefaultSubscriptionParserFactory : SubscriptionParserFactory { +private class DefaultSubscriptionParserFactory( + private val incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl, +) : SubscriptionParserFactory { override fun createParser(request: ApolloRequest): SubscriptionParser { - return DefaultSubscriptionParser(request) + return DefaultSubscriptionParser(incrementalDeliveryProtocolImpl, request) } } -private class DefaultSubscriptionParser(private val request: ApolloRequest) : SubscriptionParser { +private class DefaultSubscriptionParser( + incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl, + private val request: ApolloRequest, +) : SubscriptionParser { + private val incrementalResultsMerger = incrementalDeliveryProtocolImpl.newIncrementalResultsMerger() private val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters] ?: CustomScalarAdapters.Empty @Suppress("NAME_SHADOWING") @@ -212,13 +231,28 @@ private class DefaultSubscriptionParser(private val request: .exception(DefaultApolloException("Invalid payload")).build() } - val apolloResponse: ApolloResponse = responseMap.jsonReader().toApolloResponse( + val (payload, deferredFragmentIdentifiers) = if (responseMap.isDeferred()) { + incrementalResultsMerger.merge(responseMap) to incrementalResultsMerger.deferredFragmentIdentifiers + } else { + responseMap to null + } + val apolloResponse: ApolloResponse = payload.jsonReader().toApolloResponse( operation = request.operation, requestUuid = request.requestUuid, customScalarAdapters = requestCustomScalarAdapters, + deferredFragmentIdentifiers = deferredFragmentIdentifiers, ) - return apolloResponse + if (!incrementalResultsMerger.hasNext) { + // Last deferred payload: reset the incrementalResultsMerger for potential subsequent responses + incrementalResultsMerger.reset() + } + + return if (incrementalResultsMerger.isEmptyResponse) { + null + } else { + apolloResponse + } } } @@ -253,6 +287,11 @@ private class DefaultOperationListener( producerScope.close() } } + +private fun Map.isDeferred(): Boolean { + return keys.contains("hasNext") +} + /** * Closes the websocket connection if the transport is a [WebSocketNetworkTransport]. * diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index f432a47aeb4..f9244718878 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -1,5 +1,6 @@ package com.apollographql.apollo.network.ws +import com.apollographql.apollo.annotations.ApolloExperimental import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.CustomScalarAdapters @@ -10,7 +11,10 @@ import com.apollographql.apollo.api.toApolloResponse import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.SubscriptionOperationException +import com.apollographql.apollo.internal.incremental.IncrementalDeliveryProtocolImpl +import com.apollographql.apollo.internal.incremental.impl import com.apollographql.apollo.internal.transformWhile +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.ws.internal.Command import com.apollographql.apollo.network.ws.internal.ConnectionReEstablished @@ -38,6 +42,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onSubscription @@ -59,6 +64,7 @@ private constructor( private val idleTimeoutMillis: Long = 60_000, private val protocolFactory: WsProtocol.Factory = SubscriptionWsProtocol.Factory(), private val reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)?, + private val incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl, ) : NetworkTransport { /** @@ -259,6 +265,8 @@ private constructor( override fun execute( request: ApolloRequest, ): Flow> { + val incrementalResultsMerger = incrementalDeliveryProtocolImpl.newIncrementalResultsMerger() + return events.onSubscription { messages.send(StartOperation(request)) }.filter { @@ -295,13 +303,24 @@ private constructor( }.map { response -> when (response) { is OperationResponse -> { + val responsePayload = response.payload val requestCustomScalarAdapters = request.executionContext[CustomScalarAdapters]!! - val apolloResponse: ApolloResponse = response.payload.jsonReader().toApolloResponse( + val (payload, deferredFragmentIdentifiers) = if (responsePayload.isDeferred()) { + incrementalResultsMerger.merge(responsePayload) to incrementalResultsMerger.deferredFragmentIdentifiers + } else { + responsePayload to null + } + val apolloResponse: ApolloResponse = payload.jsonReader().toApolloResponse( operation = request.operation, requestUuid = request.requestUuid, customScalarAdapters = requestCustomScalarAdapters, + deferredFragmentIdentifiers = deferredFragmentIdentifiers ) + if (!incrementalResultsMerger.hasNext) { + // Last deferred payload: reset the incrementalResultsMerger for potential subsequent responses + incrementalResultsMerger.reset() + } apolloResponse } @@ -311,6 +330,8 @@ private constructor( // Cannot happen as these events are filtered out upstream is ConnectionReEstablished, is OperationComplete, is GeneralError -> error("Unexpected event $response") } + }.filterNot { + incrementalResultsMerger.isEmptyResponse }.onCompletion { messages.send(StopOperation(request)) } @@ -353,6 +374,7 @@ private constructor( private var idleTimeoutMillis: Long? = null private var protocolFactory: WsProtocol.Factory? = null private var reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)? = null + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 /** * Configure the server URL. @@ -419,6 +441,16 @@ private constructor( this.reopenWhen = reopenWhen } + /** + * The incremental delivery protocol to use when using `@defer` and/or `@stream`. + * + * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] + */ + @ApolloExperimental + fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { + this.incrementalDeliveryProtocol = incrementalDeliveryProtocol + } + fun build(): WebSocketNetworkTransport { return WebSocketNetworkTransport( serverUrl = serverUrl ?: error("No serverUrl specified"), @@ -426,7 +458,8 @@ private constructor( webSocketEngine = webSocketEngine ?: DefaultWebSocketEngine(), idleTimeoutMillis = idleTimeoutMillis ?: 60_000, protocolFactory = protocolFactory ?: SubscriptionWsProtocol.Factory(), - reopenWhen = reopenWhen + reopenWhen = reopenWhen, + incrementalDeliveryProtocolImpl = incrementalDeliveryProtocol.impl, ) } } @@ -441,3 +474,7 @@ fun NetworkTransport.closeConnection(reason: Throwable) { (this as? WebSocketNetworkTransport ?: throw IllegalArgumentException("'$this' is not an instance of com.apollographql.apollo.ws.WebSocketNetworkTransport")).closeConnection(reason) } + +private fun Map.isDeferred(): Boolean { + return keys.contains("hasNext") +} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt index 0958e4f7835..992fd4eb1f8 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt @@ -20,7 +20,7 @@ import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.CacheMissException import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.http.HttpNetworkTransport -import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.internal.runTest import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.assertNoRequest diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt index 423ab53cd02..f841ab6e188 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt @@ -7,7 +7,7 @@ import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.autoPersistedQueryInfo import com.apollographql.apollo.mpp.currentTimeMillis import com.apollographql.apollo.network.http.HttpNetworkTransport -import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.Platform import com.apollographql.apollo.testing.internal.runTest import com.apollographql.apollo.testing.platform diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index 09c954030d6..9f74798de9e 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -5,7 +5,7 @@ import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Optional import com.apollographql.apollo.network.http.HttpNetworkTransport -import com.apollographql.apollo.network.http.HttpNetworkTransport.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.internal.runTest import com.benasher44.uuid.uuid4 import defer.CanDeferFragmentsOnTheTopLevelQueryFieldQuery From eb20af64da57f0d2037ac6962c23667eb2c03f3c Mon Sep 17 00:00:00 2001 From: BoD Date: Fri, 26 Sep 2025 15:48:49 +0200 Subject: [PATCH 21/38] Default to `application/graphql-response+json, application/json` in DefaultHttpRequestComposer --- .../apollographql/apollo/api/http/DefaultHttpRequestComposer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index c455add9721..5d6c20816de 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -58,7 +58,7 @@ class DefaultHttpRequestComposer( if (apolloRequest.operation is Subscription<*>) { "multipart/mixed;subscriptionSpec=1.0, application/graphql-response+json, application/json" } else { - "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" + "application/graphql-response+json, application/json" } ) ) From 940f00f4bc895a2f04788e5b15841234636d3018 Mon Sep 17 00:00:00 2001 From: BoD Date: Wed, 15 Oct 2025 11:14:57 +0200 Subject: [PATCH 22/38] Update package-lock.json --- kotlin-js-store/package-lock.json | 2084 +++++++---- kotlin-js-store/wasm/package-lock.json | 52 +- tests/kotlin-js-store/package-lock.json | 3252 ++++++++++++++---- tests/kotlin-js-store/wasm/package-lock.json | 1 - 4 files changed, 4175 insertions(+), 1214 deletions(-) diff --git a/kotlin-js-store/package-lock.json b/kotlin-js-store/package-lock.json index 6f260b6068a..d4a401027c6 100644 --- a/kotlin-js-store/package-lock.json +++ b/kotlin-js-store/package-lock.json @@ -40,9 +40,8 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -55,11 +54,83 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -78,22 +149,17 @@ } }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "5.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -194,50 +260,39 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/async": { - "version": "0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", - "integrity": "sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==", - "engines": { - "node": "*" - } + "version": "0.1.22" }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.1", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/browser-stdout": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/buffer-from": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/camelcase": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -247,9 +302,8 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -263,9 +317,8 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -273,26 +326,10 @@ "node": ">=8" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -302,69 +339,10 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -374,14 +352,12 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/commander": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "license": "MIT", "dependencies": { "graceful-readlink": ">= 1.0.0" }, @@ -391,9 +367,8 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -404,10 +379,9 @@ } }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.3.7", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -422,9 +396,8 @@ }, "node_modules/decamelize": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -432,41 +405,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "version": "8.0.0", + "dev": true, + "license": "MIT" }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -476,9 +436,8 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -492,18 +451,16 @@ }, "node_modules/flat": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/foreground-child": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -517,24 +474,21 @@ }, "node_modules/format-util": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", - "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -550,33 +504,43 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/graceful-readlink": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" + "license": "MIT" }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -586,27 +550,24 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -616,15 +577,13 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -637,9 +596,8 @@ }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -647,24 +605,14 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, "node_modules/ktor-ktor-client-core": { "resolved": "packages_imported/ktor-ktor-client-core/3.2.3", "link": true }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -677,9 +625,8 @@ }, "node_modules/log-symbols": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -693,90 +640,31 @@ }, "node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/mdns2": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/mdns2/-/mdns2-2.1.4.tgz", - "integrity": "sha512-aStBO1nRHcaTEU6+Ic9YS8+F6AhnG893VTBSS86P/H+jxsCWBk3//eQAejNOg8D9kFjF+dMA89w3jHtgaEGbuQ==", - "hasInstallScript": true - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "version": "2.1.4" }, "node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/mout": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", - "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" + "version": "0.2.0" }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/needle": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "license": "MIT", "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -804,23 +692,18 @@ }, "node_modules/network/node_modules/async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + "license": "MIT" }, "node_modules/node-uuid": { "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", - "deprecated": "Use uuid module instead", "bin": { "uuid": "bin/uuid" } }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -833,9 +716,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -848,33 +730,29 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true + "dev": true, + "license": "BlueOak-1.0.0" }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -888,45 +766,27 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -941,32 +801,29 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "license": "MIT" }, "node_modules/sax": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "license": "ISC" }, "node_modules/serialize-javascript": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -976,18 +833,16 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -997,46 +852,39 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "4.2.3", "dev": true, + "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1046,26 +894,10 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1073,27 +905,11 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1101,20 +917,10 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/strip-json-comments": { + "version": "3.1.1", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -1124,9 +930,8 @@ }, "node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1139,9 +944,8 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -1154,8 +958,7 @@ }, "node_modules/wmic": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/wmic/-/wmic-1.1.1.tgz", - "integrity": "sha512-6lbonssALks49dX9bJTE8i54OTjbbLfd3IraFfG1ZR1ZrEbEynCt471IX5SfslZaFwISJKdUFHjOWHk0Brs5eg==", + "license": "MIT", "dependencies": { "async": "^3.2.0", "iconv-lite": "^0.5.0" @@ -1163,13 +966,11 @@ }, "node_modules/wmic/node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + "license": "MIT" }, "node_modules/wmic/node_modules/iconv-lite": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -1177,24 +978,17 @@ "node": ">=0.10.0" } }, - "node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "7.0.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -1203,9 +997,8 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1218,63 +1011,9 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/ws": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -1293,18 +1032,16 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -1320,18 +1057,16 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yargs-unparser": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -1342,52 +1077,10 @@ "node": ">=10" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1397,13 +1090,16 @@ }, "node_modules/zmq": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/zmq/-/zmq-2.4.0.tgz", - "integrity": "sha512-M7o1HPC+4gb3qL1+pddKJiu/uBu9un+r8c1nnDLS9HZKvMhIDyhGLFtmMbx9EnpP29KjNshrR27RyWBfhbNSRQ==", - "hasInstallScript": true, "engines": { "node": ">=0.7.9" } }, + "packages_imported/kotlin-node/22.5.5-pre.844": { + "name": "kotlin-node", + "version": "22.5.5-pre.844", + "extraneous": true, + "devDependencies": {} + }, "packages_imported/ktor-ktor-client-core/3.2.3": { "name": "ktor-ktor-client-core", "version": "3.2.3", @@ -1412,6 +1108,15 @@ }, "devDependencies": {} }, + "packages_imported/ktor-ktor-client-ktor-client-core/3.1.2": { + "name": "ktor-ktor-client-ktor-client-core", + "version": "3.1.2", + "extraneous": true, + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, "packages/apollo-kotlin-apollo-annotations": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} @@ -1424,65 +1129,118 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-api": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-api-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "packages/apollo-kotlin-apollo-annotations-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "packages/apollo-kotlin-apollo-ast": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} + "packages/apollo-kotlin-apollo-annotations-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } }, - "packages/apollo-kotlin-apollo-ast-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "packages/apollo-kotlin-apollo-annotations-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" } }, - "packages/apollo-kotlin-apollo-engine-tests": { - "version": "5.0.0-alpha.3-SNAPSHOT", + "packages/apollo-kotlin-apollo-annotations-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", "dependencies": { - "ws": "8.18.0" + "brace-expansion": "^2.0.1" }, - "devDependencies": {} + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "packages/apollo-kotlin-apollo-engine-tests-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", + "packages/apollo-kotlin-apollo-annotations-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", "dependencies": { - "ws": "8.18.0" + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "packages/apollo-kotlin-apollo-execution": { + "packages/apollo-kotlin-apollo-annotations-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-annotations-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-annotations-wasm-js": { "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, "devDependencies": {} }, - "packages/apollo-kotlin-apollo-execution-test": { + "packages/apollo-kotlin-apollo-annotations-wasm-js-test": { "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "typescript": "5.5.4" } }, - "packages/apollo-kotlin-apollo-mpp-utils": { + "packages/apollo-kotlin-apollo-api": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} }, - "packages/apollo-kotlin-apollo-mpp-utils-test": { + "packages/apollo-kotlin-apollo-api-test": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": { "kotlin-web-helpers": "2.1.0", @@ -1490,57 +1248,983 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-normalized-cache": { - "version": "5.0.0-alpha.3-SNAPSHOT", + "packages/apollo-kotlin-apollo-api-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", "dependencies": { - "ws": "8.18.0" + "readdirp": "^4.0.1" }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-normalized-cache-api": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "packages/apollo-kotlin-apollo-api-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" } }, - "packages/apollo-kotlin-apollo-normalized-cache-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", + "packages/apollo-kotlin-apollo-api-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "format-util": "^1.0.5" } }, - "packages/apollo-kotlin-apollo-runtime": { - "version": "5.0.0-alpha.3-SNAPSHOT", + "packages/apollo-kotlin-apollo-api-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", "dependencies": { - "ws": "8.18.0" + "brace-expansion": "^2.0.1" }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-runtime-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" + "engines": { + "node": ">=16 || 14 >=14.17" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/apollo-kotlin-apollo-testing-support": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { + "packages/apollo-kotlin-apollo-api-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-api-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-api-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-api-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-api-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-ast": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-ast-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-ast-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-ast-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-ast-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-engine-tests": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-engine-tests-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-engine-tests-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-engine-tests-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-execution": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-execution-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-execution-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-execution-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-execution-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-mpp-utils-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-mpp-utils-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-mpp-utils-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-normalized-cache-api": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-normalized-cache-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-normalized-cache-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-runtime": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-runtime-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-runtime-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-runtime-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-runtime-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, + "packages/apollo-kotlin-apollo-testing-support": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { "ws": "8.18.0" }, "devDependencies": {} @@ -1557,6 +2241,113 @@ "source-map-support": "0.5.21" } }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-testing-support-internal-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-testing-support-internal-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": { + "typescript": "5.5.4" + } + }, "packages/apollo-kotlin-apollo-testing-support-test": { "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { @@ -1567,6 +2358,121 @@ "mocha": "11.7.1", "source-map-support": "0.5.21" } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/mocha": { + "version": "11.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-kotlin-apollo-testing-support-test/node_modules/workerpool": { + "version": "9.3.3", + "dev": true, + "license": "Apache-2.0" + }, + "packages/apollo-kotlin-apollo-testing-support-wasm-js": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-testing-support-wasm-js-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": { + "typescript": "5.5.4" + } } } } diff --git a/kotlin-js-store/wasm/package-lock.json b/kotlin-js-store/wasm/package-lock.json index f13a574789a..5a7363b8f05 100644 --- a/kotlin-js-store/wasm/package-lock.json +++ b/kotlin-js-store/wasm/package-lock.json @@ -138,17 +138,11 @@ "link": true }, "node_modules/async": { - "version": "0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", - "integrity": "sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==", - "engines": { - "node": "*" - } + "version": "0.1.22" }, "node_modules/commander": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "license": "MIT", "dependencies": { "graceful-readlink": ">= 1.0.0" }, @@ -158,13 +152,11 @@ }, "node_modules/graceful-readlink": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" + "license": "MIT" }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -173,20 +165,14 @@ } }, "node_modules/mdns2": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/mdns2/-/mdns2-2.1.4.tgz", - "integrity": "sha512-aStBO1nRHcaTEU6+Ic9YS8+F6AhnG893VTBSS86P/H+jxsCWBk3//eQAejNOg8D9kFjF+dMA89w3jHtgaEGbuQ==", - "hasInstallScript": true + "version": "2.1.4" }, "node_modules/mout": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", - "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" + "version": "0.2.0" }, "node_modules/needle": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "license": "MIT", "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -214,32 +200,25 @@ }, "node_modules/network/node_modules/async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + "license": "MIT" }, "node_modules/node-uuid": { "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", - "deprecated": "Use uuid module instead", "bin": { "uuid": "bin/uuid" } }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "license": "MIT" }, "node_modules/sax": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "license": "ISC" }, "node_modules/wmic": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/wmic/-/wmic-1.1.1.tgz", - "integrity": "sha512-6lbonssALks49dX9bJTE8i54OTjbbLfd3IraFfG1ZR1ZrEbEynCt471IX5SfslZaFwISJKdUFHjOWHk0Brs5eg==", + "license": "MIT", "dependencies": { "async": "^3.2.0", "iconv-lite": "^0.5.0" @@ -247,13 +226,11 @@ }, "node_modules/wmic/node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + "license": "MIT" }, "node_modules/wmic/node_modules/iconv-lite": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -263,9 +240,6 @@ }, "node_modules/zmq": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/zmq/-/zmq-2.4.0.tgz", - "integrity": "sha512-M7o1HPC+4gb3qL1+pddKJiu/uBu9un+r8c1nnDLS9HZKvMhIDyhGLFtmMbx9EnpP29KjNshrR27RyWBfhbNSRQ==", - "hasInstallScript": true, "engines": { "node": ">=0.7.9" } diff --git a/tests/kotlin-js-store/package-lock.json b/tests/kotlin-js-store/package-lock.json index aa091e43a65..a065252aa1e 100644 --- a/tests/kotlin-js-store/package-lock.json +++ b/tests/kotlin-js-store/package-lock.json @@ -49,15 +49,6 @@ "node": ">=0.1.90" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", - "dev": true, - "engines": { - "node": ">=14.17.0" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -76,9 +67,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "engines": { "node": ">=12" @@ -88,9 +79,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "engines": { "node": ">=12" @@ -123,9 +114,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -424,50 +415,6 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -517,6 +464,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-import-phases": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", @@ -530,15 +487,16 @@ } }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { "type": "github", @@ -562,16 +520,36 @@ } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.3" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, "peerDependencies": { - "ajv": "^8.8.2" + "ajv": "^6.9.1" } }, "node_modules/ansi-regex": { @@ -753,24 +731,27 @@ } }, "node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/iconv-lite": { @@ -785,6 +766,18 @@ "node": ">=0.10.0" } }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1151,12 +1144,22 @@ } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/di": { @@ -1165,15 +1168,6 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -1488,6 +1482,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -1610,6 +1611,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/format-util": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", @@ -1825,19 +1838,28 @@ } }, "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "depd": "~1.1.2", + "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", + "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" } }, "node_modules/http-proxy": { @@ -2102,10 +2124,11 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true }, "node_modules/jsonfile": { "version": "4.0.0", @@ -2231,15 +2254,6 @@ "node": ">=0.10.0" } }, - "node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -2436,230 +2450,59 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, + "node_modules/mout": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", + "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" }, "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "needle": "bin/needle" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">= 4.4.x" } }, - "node_modules/mocha/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/network": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/network/-/network-0.7.0.tgz", + "integrity": "sha512-AquYHEZFrPi1WPaMg+21iTKN7aMERP70frgK41lbTt/9tTQcwgaOtlqHRmVbxJjWYWJy033jUbA8xXhFArXodw==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "async": "^1.5.2", + "commander": "2.9.0", + "needle": "^3.0.0", + "wmic": "^1.0.1" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/mout": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", - "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/needle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", - "dependencies": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/network": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/network/-/network-0.7.0.tgz", - "integrity": "sha512-AquYHEZFrPi1WPaMg+21iTKN7aMERP70frgK41lbTt/9tTQcwgaOtlqHRmVbxJjWYWJy033jUbA8xXhFArXodw==", - "dependencies": { - "async": "^1.5.2", - "commander": "2.9.0", - "needle": "^3.0.0", - "wmic": "^1.0.1" - }, - "bin": { - "network": "bin/network" + "bin": { + "network": "bin/network" } }, "node_modules/network/node_modules/async": { @@ -2700,6 +2543,18 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2922,10 +2777,13 @@ } }, "node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { "node": ">=0.6" }, @@ -2952,13 +2810,13 @@ } }, "node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "http-errors": "1.8.1", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -3137,15 +2995,15 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "peer": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { "node": ">= 10.13.0" @@ -3203,16 +3061,76 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/socket.io": { @@ -3594,6 +3512,59 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -3723,13 +3694,33 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "engines": { - "node": ">= 0.4.0" + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" } }, "node_modules/vary": { @@ -3764,22 +3755,21 @@ } }, "node_modules/webpack": { - "version": "5.100.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", - "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.2", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -3789,11 +3779,11 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.1", - "webpack-sources": "^3.3.3" + "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" @@ -3811,71 +3801,6 @@ } } }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-cli/node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -3937,12 +3862,6 @@ "node": ">=0.10.0" } }, - "node_modules/workerpool": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", - "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4088,12 +4007,6 @@ "extraneous": true, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-api/5.0.0-alpha.2-SNAPSHOT": { - "name": "apollo-kotlin-apollo-api", - "version": "5.0.0-alpha.2-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-api/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-api", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4127,16 +4040,6 @@ }, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-normalized-cache/5.0.0-alpha.2-SNAPSHOT": { - "name": "apollo-kotlin-apollo-normalized-cache", - "version": "5.0.0-alpha.2-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-normalized-cache/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-normalized-cache", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4156,6 +4059,12 @@ }, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-runtime-wasm-js/5.0.0-alpha.3-SNAPSHOT": { + "name": "apollo-kotlin-apollo-runtime-wasm-js", + "version": "5.0.0-alpha.3-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.0-SNAPSHOT": { "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.0-SNAPSHOT", @@ -4178,16 +4087,6 @@ }, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.2-SNAPSHOT": { - "name": "apollo-kotlin-apollo-runtime", - "version": "5.0.0-alpha.2-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4219,12 +4118,6 @@ "extraneous": true, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-testing-support-internal/5.0.0-alpha.2-SNAPSHOT": { - "name": "apollo-kotlin-apollo-testing-support-internal", - "version": "5.0.0-alpha.2-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-testing-support-internal/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-testing-support-internal", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4258,16 +4151,6 @@ }, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-testing-support/5.0.0-alpha.2-SNAPSHOT": { - "name": "apollo-kotlin-apollo-testing-support", - "version": "5.0.0-alpha.2-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-testing-support/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-testing-support", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4327,104 +4210,2162 @@ "webpack-cli": "6.0.1" } }, - "packages/apollo-tests-defer": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-defer-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "packages/apollo-tests-browser-tests-test/node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "engines": { + "node": ">=14.17.0" } }, - "packages/apollo-tests-gzip": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" + "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "engines": { + "node": ">=18.12.0" }, - "devDependencies": {} + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } }, - "packages/apollo-tests-gzip-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" + "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "engines": { + "node": ">=18.12.0" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, - "packages/apollo-tests-integration-tests": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" + "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "engines": { + "node": ">=18.12.0" }, - "devDependencies": {} - }, - "packages/apollo-tests-integration-tests-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "packages/apollo-tests-js": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-browser-tests-test/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "devDependencies": {} + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "packages/apollo-tests-js-test": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-browser-tests-test/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "fast-deep-equal": "^3.1.3" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "packages/apollo-tests-jsexport": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-browser-tests-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "packages/apollo-tests-browser-tests-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-browser-tests-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/webpack": { + "version": "5.100.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", + "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.2", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-browser-tests-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-browser-tests-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-browser-tests-wasm-js": { + "version": "0.0.0-unspecified", + "extraneous": true, + "devDependencies": { + "kotlin-web-helpers": "2.0.0", + "source-map-loader": "5.0.0", + "typescript": "5.5.4", + "webpack": "5.94.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.2" + } + }, + "packages/apollo-tests-browser-tests-wasm-js-test": { + "version": "0.0.0-unspecified", + "extraneous": true, + "devDependencies": { + "karma": "6.4.4", + "karma-chrome-launcher": "3.2.0", + "karma-mocha": "2.0.1", + "karma-sourcemap-loader": "0.4.0", + "karma-webpack": "5.0.1", + "kotlin-web-helpers": "2.0.0", + "mocha": "10.7.3", + "source-map-loader": "5.0.0", + "typescript": "5.5.4", + "webpack": "5.94.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.2" + } + }, + "packages/apollo-tests-defer": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-defer-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-defer-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-defer-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-defer-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-defer-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-defer-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-defer-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-defer-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-defer-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-defer-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-defer-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-defer-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-defer-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-defer-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-defer-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-gzip": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-gzip-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-gzip-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-gzip-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-gzip-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-gzip-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-gzip-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-gzip-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-gzip-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-gzip-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-gzip-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-gzip-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-gzip-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-gzip-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-gzip-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-gzip-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-integration-tests": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-integration-tests-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-integration-tests-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-integration-tests-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-integration-tests-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-js": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-js-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-js-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-js-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-js-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-js-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-js-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-js-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-js-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-js-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-js-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-js-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-js-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-js-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-js-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-js-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-jsexport": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} }, "packages/apollo-tests-jsexport-test": { "version": "0.0.0-unspecified", "dependencies": { - "ws": "8.18.0" + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-jsexport-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-jsexport-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-jsexport-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-jsexport-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-operation-based": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-models-operation-based-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-models-operation-based-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-models-operation-based-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-operation-based-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-response-based": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-models-response-based-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-models-response-based-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-models-response-based-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-models-response-based-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-multipart": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": {} + }, + "packages/apollo-tests-multipart-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" + } + }, + "packages/apollo-tests-multipart-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-multipart-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-multipart-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-multipart-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "packages/apollo-tests-multipart-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-multipart-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-multipart-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-multipart-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/apollo-tests-multipart-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-multipart-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-multipart-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-multipart-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-multipart-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "engines": { + "node": ">=12" } }, - "packages/apollo-tests-models-operation-based": { + "packages/apollo-tests-multipart-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-websockets": { "version": "0.0.0-unspecified", "dependencies": { "ws": "8.18.0" }, "devDependencies": {} }, - "packages/apollo-tests-models-operation-based-test": { + "packages/apollo-tests-websockets-test": { "version": "0.0.0-unspecified", "dependencies": { "ws": "8.18.0" @@ -4435,58 +6376,199 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-tests-models-response-based": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "balanced-match": "^1.0.0" + } + }, + "packages/apollo-tests-websockets-test/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" }, - "devDependencies": {} + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "packages/apollo-tests-models-response-based-test": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "engines": { + "node": ">=12" } }, - "packages/apollo-tests-multipart": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "ms": "^2.1.3" }, - "devDependencies": {} + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - "packages/apollo-tests-multipart-test": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "packages/apollo-tests-websockets-test/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "packages/apollo-tests-websockets": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "format-util": "^1.0.5" + } + }, + "packages/apollo-tests-websockets-test/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, - "devDependencies": {} + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "packages/apollo-tests-websockets-test": { - "version": "0.0.0-unspecified", + "packages/apollo-tests-websockets-test/node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, "dependencies": { - "ws": "8.18.0" + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/apollo-tests-websockets-test/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "packages/apollo-tests-websockets-test/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "packages/apollo-tests-websockets-test/node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "packages/apollo-tests-websockets-test/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "packages/apollo-tests-websockets-test/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" } } } diff --git a/tests/kotlin-js-store/wasm/package-lock.json b/tests/kotlin-js-store/wasm/package-lock.json index 3c23e2d6f98..744ea710ef6 100644 --- a/tests/kotlin-js-store/wasm/package-lock.json +++ b/tests/kotlin-js-store/wasm/package-lock.json @@ -182,7 +182,6 @@ "devDependencies": {} }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.3-SNAPSHOT": { - "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} }, From 8e6c7f54ea67e3cc8c3a37af5556d5316577fab6 Mon Sep 17 00:00:00 2001 From: BoD Date: Wed, 15 Oct 2025 15:08:37 +0200 Subject: [PATCH 23/38] Rename incremental delivery protocol enum/classes to DraftInitial and Draft0_1. --- ...kt => Draft0_1IncrementalResultsMerger.kt} | 4 +-- ...> DraftInitialIncrementalResultsMerger.kt} | 4 +-- .../IncrementalDeliveryProtocolImpl.kt | 12 ++++---- .../network/IncrementalDeliveryProtocol.kt | 12 ++++---- .../network/http/HttpNetworkTransport.kt | 4 +-- .../websocket/WebSocketNetworkTransport.kt | 4 +-- .../network/ws/WebSocketNetworkTransport.kt | 4 +-- ...hQL17Alpha2IncrementalResultsMergerTest.kt | 8 +++--- ...hQL17Alpha9IncrementalResultsMergerTest.kt | 28 +++++++++---------- ...kt => DeferDraft0_1NormalizedCacheTest.kt} | 6 ++-- ...QL17Alpha9Test.kt => DeferDraft0_1Test.kt} | 6 ++-- ...> DeferDraftInitialNormalizedCacheTest.kt} | 2 +- ...Alpha2Test.kt => DeferDraftInitialTest.kt} | 2 +- .../kotlin/test/DeferWithApolloServerTest.kt | 4 +-- 14 files changed, 50 insertions(+), 50 deletions(-) rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/{GraphQL17Alpha9IncrementalResultsMerger.kt => Draft0_1IncrementalResultsMerger.kt} (97%) rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/{GraphQL17Alpha2IncrementalResultsMerger.kt => DraftInitialIncrementalResultsMerger.kt} (95%) rename tests/defer/src/commonTest/kotlin/test/{DeferGraphQL17Alpha9NormalizedCacheTest.kt => DeferDraft0_1NormalizedCacheTest.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferGraphQL17Alpha9Test.kt => DeferDraft0_1Test.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferGraphQL17Alpha2NormalizedCacheTest.kt => DeferDraftInitialNormalizedCacheTest.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferGraphQL17Alpha2Test.kt => DeferDraftInitialTest.kt} (99%) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt similarity index 97% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt index 07952b1d227..a21f202c318 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha9IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt @@ -4,10 +4,10 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.GraphQL17Alpha9] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.Draft0_1] protocol format. */ @Suppress("UNCHECKED_CAST") -internal class GraphQL17Alpha9IncrementalResultsMerger : IncrementalResultsMerger { +internal class Draft0_1IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt similarity index 95% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt index b741a6df394..d3c9d45c230 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/GraphQL17Alpha2IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt @@ -4,10 +4,10 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.GraphQL17Alpha2] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.DraftInitial] protocol format. */ @Suppress("UNCHECKED_CAST") -internal class GraphQL17Alpha2IncrementalResultsMerger : IncrementalResultsMerger { +internal class DraftInitialIncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index 3bc8da45bfa..3712e050532 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -7,23 +7,23 @@ internal sealed interface IncrementalDeliveryProtocolImpl { fun newIncrementalResultsMerger(): IncrementalResultsMerger - object GraphQL17Alpha2 : IncrementalDeliveryProtocolImpl { + object DraftInitial : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = DraftInitialIncrementalResultsMerger() } - object GraphQL17Alpha9 : IncrementalDeliveryProtocolImpl { + object Draft0_1 : IncrementalDeliveryProtocolImpl { // TODO To be agreed upon with the router and other clients override val acceptHeader: String = "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Draft0_1IncrementalResultsMerger() } } internal val IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - IncrementalDeliveryProtocol.GraphQL17Alpha2 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha2 - IncrementalDeliveryProtocol.GraphQL17Alpha9 -> IncrementalDeliveryProtocolImpl.GraphQL17Alpha9 + IncrementalDeliveryProtocol.DraftInitial -> IncrementalDeliveryProtocolImpl.DraftInitial + IncrementalDeliveryProtocol.Draft0_1 -> IncrementalDeliveryProtocolImpl.Draft0_1 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt index f01d205a1b6..5b6d2c8b30f 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt @@ -7,21 +7,21 @@ import com.apollographql.apollo.annotations.ApolloExperimental */ @ApolloExperimental enum class IncrementalDeliveryProtocol { - /** - * Newer format as implemented by graphql.js version `17.0.0-alpha.2` and specified in this historical commit: - * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec + * Initial draft format as specified in this historical commit: + * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec, + * and implemented by `graphql.js` version `17.0.0-alpha.2` * * Only `@defer` is supported with this format. * * This is the default. */ - GraphQL17Alpha2, + DraftInitial, /** - * Newer format as implemented by graphql.js version `17.0.0-alpha.9`. + * Draft format as specified by https://specs.apollo.dev/incremental/v0.1/, and implemented by `graphql.js` version `17.0.0-alpha.9`. * * Both `@defer` and `@stream` are supported with this format. */ - GraphQL17Alpha9 + Draft0_1 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index aa558a16ecf..029d9515a4c 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -326,7 +326,7 @@ private constructor( private var engine: HttpEngine? = null private val interceptors: MutableList = mutableListOf() private var exposeErrorBody: Boolean = false - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial private val headers: MutableList = mutableListOf() fun httpRequestComposer(httpRequestComposer: HttpRequestComposer) = apply { @@ -373,7 +373,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] + * Default: [IncrementalDeliveryProtocol.DraftInitial] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index 907fa2bc949..c4b697a05a8 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -116,7 +116,7 @@ class WebSocketNetworkTransport private constructor( private var pingInterval: Duration? = null private var idleTimeout: Duration? = null private var parserFactory: SubscriptionParserFactory? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial /** * @param serverUrl a server url that is called every time a WebSocket @@ -183,7 +183,7 @@ class WebSocketNetworkTransport private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] + * Default: [IncrementalDeliveryProtocol.DraftInitial] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index f9244718878..685c87c107f 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -374,7 +374,7 @@ private constructor( private var idleTimeoutMillis: Long? = null private var protocolFactory: WsProtocol.Factory? = null private var reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.GraphQL17Alpha2 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial /** * Configure the server URL. @@ -444,7 +444,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.GraphQL17Alpha2] + * Default: [IncrementalDeliveryProtocol.DraftInitial] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt index b5226574b78..33f00f1a07a 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt @@ -6,7 +6,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.GraphQL17Alpha2IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.DraftInitialIncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -21,7 +21,7 @@ private fun jsonToMap(json: String): Map = BufferedSourceJsonReade class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() + val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() //language=JSON val payload1 = """ @@ -392,7 +392,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() + val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() //language=JSON val payload1 = """ @@ -668,7 +668,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun emptyPayloads() { - val incrementalResultsMerger = GraphQL17Alpha2IncrementalResultsMerger() + val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() //language=JSON val payload1 = """ diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt index 796f3d039e0..93a87f99d86 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt @@ -6,7 +6,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.GraphQL17Alpha9IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.Draft0_1IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -21,7 +21,7 @@ private fun jsonToMap(json: String): Map = BufferedSourceJsonReade class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -454,7 +454,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -762,7 +762,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun emptyPayloads() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -851,7 +851,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleA() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -970,7 +970,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleA2() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1149,7 +1149,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleB1() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1316,7 +1316,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleB2() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1486,7 +1486,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleD() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1714,7 +1714,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleF() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1794,7 +1794,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleG() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1963,7 +1963,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleH() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2135,7 +2135,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun july2025ExampleI() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2348,7 +2348,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun july2025ExampleJ() { - val incrementalResultsMerger = GraphQL17Alpha9IncrementalResultsMerger() + val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt index 992fd4eb1f8..c0329681598 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt @@ -18,9 +18,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.CacheMissException +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport import com.apollographql.apollo.network.http.HttpNetworkTransport -import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.testing.internal.runTest import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.assertNoRequest @@ -48,7 +48,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferGraphQL17Alpha9NormalizedCacheTest { +class DeferDraft0_1NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore @@ -60,7 +60,7 @@ class DeferGraphQL17Alpha9NormalizedCacheTest { .networkTransport( HttpNetworkTransport.Builder() .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) .build() ) .store(store).build() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt rename to tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt index f841ab6e188..e778de69a5e 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha9Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt @@ -6,8 +6,8 @@ import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.autoPersistedQueryInfo import com.apollographql.apollo.mpp.currentTimeMillis -import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.network.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.testing.Platform import com.apollographql.apollo.testing.internal.runTest import com.apollographql.apollo.testing.platform @@ -30,7 +30,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeferGraphQL17Alpha9Test { +class DeferDraft0_1Test { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient @@ -40,7 +40,7 @@ class DeferGraphQL17Alpha9Test { .networkTransport( HttpNetworkTransport.Builder() .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) .build() ) .build() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt index 9ca3d8da8b2..72818d9cf4f 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt @@ -45,7 +45,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferGraphQL17Alpha2NormalizedCacheTest { +class DeferDraftInitialNormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore diff --git a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt rename to tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt index 155b785f0fa..e7198702b57 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferGraphQL17Alpha2Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt @@ -25,7 +25,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeferGraphQL17Alpha2Test { +class DeferDraftInitialTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index 9f74798de9e..64b951aecfa 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -4,8 +4,8 @@ import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Optional -import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.network.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.testing.internal.runTest import com.benasher44.uuid.uuid4 import defer.CanDeferFragmentsOnTheTopLevelQueryFieldQuery @@ -49,7 +49,7 @@ class DeferWithApolloServerTest { .networkTransport( HttpNetworkTransport.Builder() .serverUrl("http://127.0.0.1:4000/") - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.GraphQL17Alpha9) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) .build() ) .build() From 8a775163eea656f88c655594286ef945762ef46f Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 16 Oct 2025 11:30:44 +0200 Subject: [PATCH 24/38] Update API dump --- libraries/apollo-api/api/apollo-api.klib.api | 6 +++++- .../apollo-runtime/api/android/apollo-runtime.api | 15 +++++++++++++-- .../apollo-runtime/api/apollo-runtime.klib.api | 14 ++++++++++++++ .../apollo-runtime/api/jvm/apollo-runtime.api | 15 +++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/libraries/apollo-api/api/apollo-api.klib.api b/libraries/apollo-api/api/apollo-api.klib.api index 19dce8acfcf..a3c44454685 100644 --- a/libraries/apollo-api/api/apollo-api.klib.api +++ b/libraries/apollo-api/api/apollo-api.klib.api @@ -953,7 +953,6 @@ final class com.apollographql.apollo.api/CustomScalarAdapters : com.apollographq final fun deferredFragmentIdentifiers(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.deferredFragmentIdentifiers|deferredFragmentIdentifiers(kotlin.collections.Set?){}[0] final fun errors(kotlin.collections/List?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.errors|errors(kotlin.collections.List?){}[0] final fun falseVariables(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.falseVariables|falseVariables(kotlin.collections.Set?){}[0] - final fun pendingResultIds(kotlin.collections/Set?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.pendingResultIds|pendingResultIds(kotlin.collections.Set?){}[0] } final object Key : com.apollographql.apollo.api/ExecutionContext.Key { // com.apollographql.apollo.api/CustomScalarAdapters.Key|null[0] @@ -1010,6 +1009,11 @@ final class com.apollographql.apollo.api/DeferredFragmentIdentifier { // com.apo final fun equals(kotlin/Any?): kotlin/Boolean // com.apollographql.apollo.api/DeferredFragmentIdentifier.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // com.apollographql.apollo.api/DeferredFragmentIdentifier.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // com.apollographql.apollo.api/DeferredFragmentIdentifier.toString|toString(){}[0] + + final object Companion { // com.apollographql.apollo.api/DeferredFragmentIdentifier.Companion|null[0] + final val Pending // com.apollographql.apollo.api/DeferredFragmentIdentifier.Companion.Pending|{}Pending[0] + final fun (): com.apollographql.apollo.api/DeferredFragmentIdentifier // com.apollographql.apollo.api/DeferredFragmentIdentifier.Companion.Pending.|(){}[0] + } } final class com.apollographql.apollo.api/EnumType : com.apollographql.apollo.api/CompiledNamedType { // com.apollographql.apollo.api/EnumType|null[0] diff --git a/libraries/apollo-runtime/api/android/apollo-runtime.api b/libraries/apollo-runtime/api/android/apollo-runtime.api index 311d1fedc20..fa27c2e3802 100644 --- a/libraries/apollo-runtime/api/android/apollo-runtime.api +++ b/libraries/apollo-runtime/api/android/apollo-runtime.api @@ -215,6 +215,14 @@ public final class com/apollographql/apollo/interceptor/RetryOnErrorInterceptorK public static final fun RetryOnErrorInterceptor (Lcom/apollographql/apollo/network/NetworkMonitor;)Lcom/apollographql/apollo/interceptor/ApolloInterceptor; } +public final class com/apollographql/apollo/network/IncrementalDeliveryProtocol : java/lang/Enum { + public static final field Draft0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field DraftInitial Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static fun values ()[Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; +} + public abstract interface class com/apollographql/apollo/network/NetworkMonitor : java/io/Closeable { public abstract fun isOnline ()Lkotlinx/coroutines/flow/StateFlow; } @@ -326,7 +334,7 @@ public abstract interface class com/apollographql/apollo/network/http/HttpInterc } public final class com/apollographql/apollo/network/http/HttpNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; public final fun getInterceptors ()Ljava/util/List; @@ -342,6 +350,7 @@ public final class com/apollographql/apollo/network/http/HttpNetworkTransport$Bu public final fun httpEngine (Lcom/apollographql/apollo/network/http/HttpEngine;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun httpHeaders (Ljava/util/List;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun httpRequestComposer (Lcom/apollographql/apollo/api/http/HttpRequestComposer;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun interceptors (Ljava/util/List;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; } @@ -522,6 +531,7 @@ public final class com/apollographql/apollo/network/websocket/WebSocketNetworkTr public final fun build ()Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport; public final fun connectionAcknowledgeTimeout-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun idleTimeout-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun parserFactory (Lcom/apollographql/apollo/network/websocket/SubscriptionParserFactory;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun pingInterval-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; @@ -650,7 +660,7 @@ public final class com/apollographql/apollo/network/ws/WebSocketEngineKt { } public final class com/apollographql/apollo/network/ws/WebSocketNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/util/List;Lcom/apollographql/apollo/network/ws/WebSocketEngine;JLcom/apollographql/apollo/network/ws/WsProtocol$Factory;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/util/List;Lcom/apollographql/apollo/network/ws/WebSocketEngine;JLcom/apollographql/apollo/network/ws/WsProtocol$Factory;Lkotlin/jvm/functions/Function3;Lcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun closeConnection (Ljava/lang/Throwable;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; @@ -664,6 +674,7 @@ public final class com/apollographql/apollo/network/ws/WebSocketNetworkTransport public final fun build ()Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport; public final fun headers (Ljava/util/List;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun idleTimeoutMillis (J)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun protocol (Lcom/apollographql/apollo/network/ws/WsProtocol$Factory;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun reopenWhen (Lkotlin/jvm/functions/Function3;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; diff --git a/libraries/apollo-runtime/api/apollo-runtime.klib.api b/libraries/apollo-runtime/api/apollo-runtime.klib.api index 152240b6148..cac9e5f2a98 100644 --- a/libraries/apollo-runtime/api/apollo-runtime.klib.api +++ b/libraries/apollo-runtime/api/apollo-runtime.klib.api @@ -21,6 +21,17 @@ final enum class com.apollographql.apollo.network.ws/WsFrameType : kotlin/Enum // com.apollographql.apollo.network.ws/WsFrameType.values|values#static(){}[0] } +final enum class com.apollographql.apollo.network/IncrementalDeliveryProtocol : kotlin/Enum { // com.apollographql.apollo.network/IncrementalDeliveryProtocol|null[0] + enum entry Draft0_1 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.Draft0_1|null[0] + enum entry DraftInitial // com.apollographql.apollo.network/IncrementalDeliveryProtocol.DraftInitial|null[0] + + final val entries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): com.apollographql.apollo.network/IncrementalDeliveryProtocol // com.apollographql.apollo.network/IncrementalDeliveryProtocol.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // com.apollographql.apollo.network/IncrementalDeliveryProtocol.values|values#static(){}[0] +} + abstract interface <#A: com.apollographql.apollo.api/Operation.Data> com.apollographql.apollo.network.websocket/SubscriptionParser { // com.apollographql.apollo.network.websocket/SubscriptionParser|null[0] abstract fun parse(kotlin/Any?): com.apollographql.apollo.api/ApolloResponse<#A>? // com.apollographql.apollo.network.websocket/SubscriptionParser.parse|parse(kotlin.Any?){}[0] } @@ -283,6 +294,7 @@ final class com.apollographql.apollo.network.http/HttpNetworkTransport : com.apo final fun httpEngine(com.apollographql.apollo.network.http/HttpEngine): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.httpEngine|httpEngine(com.apollographql.apollo.network.http.HttpEngine){}[0] final fun httpHeaders(kotlin.collections/List): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.httpHeaders|httpHeaders(kotlin.collections.List){}[0] final fun httpRequestComposer(com.apollographql.apollo.api.http/HttpRequestComposer): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.httpRequestComposer|httpRequestComposer(com.apollographql.apollo.api.http.HttpRequestComposer){}[0] + final fun incrementalDeliveryProtocol(com.apollographql.apollo.network/IncrementalDeliveryProtocol): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.incrementalDeliveryProtocol|incrementalDeliveryProtocol(com.apollographql.apollo.network.IncrementalDeliveryProtocol){}[0] final fun interceptors(kotlin.collections/List): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.interceptors|interceptors(kotlin.collections.List){}[0] final fun serverUrl(kotlin/String): com.apollographql.apollo.network.http/HttpNetworkTransport.Builder // com.apollographql.apollo.network.http/HttpNetworkTransport.Builder.serverUrl|serverUrl(kotlin.String){}[0] } @@ -430,6 +442,7 @@ final class com.apollographql.apollo.network.websocket/WebSocketNetworkTransport final fun build(): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.build|build(){}[0] final fun connectionAcknowledgeTimeout(kotlin.time/Duration?): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.connectionAcknowledgeTimeout|connectionAcknowledgeTimeout(kotlin.time.Duration?){}[0] final fun idleTimeout(kotlin.time/Duration?): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.idleTimeout|idleTimeout(kotlin.time.Duration?){}[0] + final fun incrementalDeliveryProtocol(com.apollographql.apollo.network/IncrementalDeliveryProtocol): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.incrementalDeliveryProtocol|incrementalDeliveryProtocol(com.apollographql.apollo.network.IncrementalDeliveryProtocol){}[0] final fun parserFactory(com.apollographql.apollo.network.websocket/SubscriptionParserFactory?): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.parserFactory|parserFactory(com.apollographql.apollo.network.websocket.SubscriptionParserFactory?){}[0] final fun pingInterval(kotlin.time/Duration?): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.pingInterval|pingInterval(kotlin.time.Duration?){}[0] final fun serverUrl(kotlin/String?): com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.websocket/WebSocketNetworkTransport.Builder.serverUrl|serverUrl(kotlin.String?){}[0] @@ -532,6 +545,7 @@ final class com.apollographql.apollo.network.ws/WebSocketNetworkTransport : com. final fun build(): com.apollographql.apollo.network.ws/WebSocketNetworkTransport // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.build|build(){}[0] final fun headers(kotlin.collections/List): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.headers|headers(kotlin.collections.List){}[0] final fun idleTimeoutMillis(kotlin/Long): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.idleTimeoutMillis|idleTimeoutMillis(kotlin.Long){}[0] + final fun incrementalDeliveryProtocol(com.apollographql.apollo.network/IncrementalDeliveryProtocol): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.incrementalDeliveryProtocol|incrementalDeliveryProtocol(com.apollographql.apollo.network.IncrementalDeliveryProtocol){}[0] final fun protocol(com.apollographql.apollo.network.ws/WsProtocol.Factory): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.protocol|protocol(com.apollographql.apollo.network.ws.WsProtocol.Factory){}[0] final fun reopenWhen(kotlin.coroutines/SuspendFunction2?): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.reopenWhen|reopenWhen(kotlin.coroutines.SuspendFunction2?){}[0] final fun serverUrl(kotlin.coroutines/SuspendFunction0?): com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder // com.apollographql.apollo.network.ws/WebSocketNetworkTransport.Builder.serverUrl|serverUrl(kotlin.coroutines.SuspendFunction0?){}[0] diff --git a/libraries/apollo-runtime/api/jvm/apollo-runtime.api b/libraries/apollo-runtime/api/jvm/apollo-runtime.api index a104e2ae811..0b7e0ffc502 100644 --- a/libraries/apollo-runtime/api/jvm/apollo-runtime.api +++ b/libraries/apollo-runtime/api/jvm/apollo-runtime.api @@ -215,6 +215,14 @@ public final class com/apollographql/apollo/interceptor/RetryOnErrorInterceptorK public static final fun RetryOnErrorInterceptor (Lcom/apollographql/apollo/network/NetworkMonitor;)Lcom/apollographql/apollo/interceptor/ApolloInterceptor; } +public final class com/apollographql/apollo/network/IncrementalDeliveryProtocol : java/lang/Enum { + public static final field Draft0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field DraftInitial Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static fun values ()[Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; +} + public abstract interface class com/apollographql/apollo/network/NetworkMonitor : java/io/Closeable { public abstract fun isOnline ()Lkotlinx/coroutines/flow/StateFlow; } @@ -322,7 +330,7 @@ public abstract interface class com/apollographql/apollo/network/http/HttpInterc } public final class com/apollographql/apollo/network/http/HttpNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; public final fun getInterceptors ()Ljava/util/List; @@ -338,6 +346,7 @@ public final class com/apollographql/apollo/network/http/HttpNetworkTransport$Bu public final fun httpEngine (Lcom/apollographql/apollo/network/http/HttpEngine;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun httpHeaders (Ljava/util/List;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun httpRequestComposer (Lcom/apollographql/apollo/api/http/HttpRequestComposer;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun interceptors (Ljava/util/List;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/http/HttpNetworkTransport$Builder; } @@ -518,6 +527,7 @@ public final class com/apollographql/apollo/network/websocket/WebSocketNetworkTr public final fun build ()Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport; public final fun connectionAcknowledgeTimeout-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun idleTimeout-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun parserFactory (Lcom/apollographql/apollo/network/websocket/SubscriptionParserFactory;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun pingInterval-BwNAW2A (Lkotlin/time/Duration;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/websocket/WebSocketNetworkTransport$Builder; @@ -646,7 +656,7 @@ public final class com/apollographql/apollo/network/ws/WebSocketEngineKt { } public final class com/apollographql/apollo/network/ws/WebSocketNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/util/List;Lcom/apollographql/apollo/network/ws/WebSocketEngine;JLcom/apollographql/apollo/network/ws/WsProtocol$Factory;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lkotlin/jvm/functions/Function1;Ljava/util/List;Lcom/apollographql/apollo/network/ws/WebSocketEngine;JLcom/apollographql/apollo/network/ws/WsProtocol$Factory;Lkotlin/jvm/functions/Function3;Lcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun closeConnection (Ljava/lang/Throwable;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; @@ -660,6 +670,7 @@ public final class com/apollographql/apollo/network/ws/WebSocketNetworkTransport public final fun build ()Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport; public final fun headers (Ljava/util/List;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun idleTimeoutMillis (J)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; + public final fun incrementalDeliveryProtocol (Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun protocol (Lcom/apollographql/apollo/network/ws/WsProtocol$Factory;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun reopenWhen (Lkotlin/jvm/functions/Function3;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; public final fun serverUrl (Ljava/lang/String;)Lcom/apollographql/apollo/network/ws/WebSocketNetworkTransport$Builder; From 44f84c4e2441a4ef998c94595d6d4255f113b4c3 Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 16 Oct 2025 11:31:10 +0200 Subject: [PATCH 25/38] Update package-lock.json --- kotlin-js-store/package-lock.json | 2080 ++++------- kotlin-js-store/wasm/package-lock.json | 52 +- tests/kotlin-js-store/package-lock.json | 3292 ++++-------------- tests/kotlin-js-store/wasm/package-lock.json | 1 + 4 files changed, 1232 insertions(+), 4193 deletions(-) diff --git a/kotlin-js-store/package-lock.json b/kotlin-js-store/package-lock.json index d4a401027c6..6f260b6068a 100644 --- a/kotlin-js-store/package-lock.json +++ b/kotlin-js-store/package-lock.json @@ -40,8 +40,9 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -54,83 +55,11 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -149,17 +78,22 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -260,39 +194,50 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/async": { - "version": "0.1.22" + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==", + "engines": { + "node": "*" + } }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/brace-expansion": { - "version": "2.0.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/browser-stdout": { "version": "1.3.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, "node_modules/buffer-from": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -302,8 +247,9 @@ }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -317,8 +263,9 @@ }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -326,10 +273,26 @@ "node": ">=8" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -339,10 +302,69 @@ "node": ">=12" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -352,12 +374,14 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/commander": { "version": "2.9.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", "dependencies": { "graceful-readlink": ">= 1.0.0" }, @@ -367,8 +391,9 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -379,9 +404,10 @@ } }, "node_modules/debug": { - "version": "4.3.7", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -396,8 +422,9 @@ }, "node_modules/decamelize": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -405,28 +432,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/escalade": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -436,8 +476,9 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -451,16 +492,18 @@ }, "node_modules/flat": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/foreground-child": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -474,21 +517,24 @@ }, "node_modules/format-util": { "version": "1.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", + "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==", + "dev": true }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/glob": { "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -504,43 +550,33 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/graceful-readlink": { "version": "1.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/iconv-lite": { "version": "0.6.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -550,24 +586,27 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -577,13 +616,15 @@ }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/jackspeak": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -596,8 +637,9 @@ }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -605,14 +647,24 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, "node_modules/ktor-ktor-client-core": { "resolved": "packages_imported/ktor-ktor-client-core/3.2.3", "link": true }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -625,8 +677,9 @@ }, "node_modules/log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -640,31 +693,90 @@ }, "node_modules/lru-cache": { "version": "10.4.3", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/mdns2": { - "version": "2.1.4" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdns2/-/mdns2-2.1.4.tgz", + "integrity": "sha512-aStBO1nRHcaTEU6+Ic9YS8+F6AhnG893VTBSS86P/H+jxsCWBk3//eQAejNOg8D9kFjF+dMA89w3jHtgaEGbuQ==", + "hasInstallScript": true + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/minipass": { "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/mout": { - "version": "0.2.0" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", + "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" }, "node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/needle": { "version": "3.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -692,18 +804,23 @@ }, "node_modules/network/node_modules/async": { "version": "1.5.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" }, "node_modules/node-uuid": { "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", + "deprecated": "Use uuid module instead", "bin": { "uuid": "bin/uuid" } }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -716,8 +833,9 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -730,29 +848,33 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "dev": true, - "license": "BlueOak-1.0.0" + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-key": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -766,27 +888,45 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true }, "node_modules/randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -801,29 +941,32 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safer-buffer": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { "version": "1.4.1", - "license": "ISC" + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/serialize-javascript": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/shebang-command": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -833,16 +976,18 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" }, @@ -852,39 +997,46 @@ }, "node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/string-width": { - "version": "4.2.3", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -894,10 +1046,26 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -905,11 +1073,27 @@ "node": ">=8" } }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -917,21 +1101,32 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" - }, + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -944,8 +1139,9 @@ }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -958,7 +1154,8 @@ }, "node_modules/wmic": { "version": "1.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wmic/-/wmic-1.1.1.tgz", + "integrity": "sha512-6lbonssALks49dX9bJTE8i54OTjbbLfd3IraFfG1ZR1ZrEbEynCt471IX5SfslZaFwISJKdUFHjOWHk0Brs5eg==", "dependencies": { "async": "^3.2.0", "iconv-lite": "^0.5.0" @@ -966,11 +1163,13 @@ }, "node_modules/wmic/node_modules/async": { "version": "3.2.6", - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/wmic/node_modules/iconv-lite": { "version": "0.5.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -978,17 +1177,24 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, "node_modules/wrap-ansi": { - "version": "7.0.0", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -997,8 +1203,9 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -1011,9 +1218,63 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -1032,16 +1293,18 @@ }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -1057,16 +1320,18 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yargs-unparser": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -1077,10 +1342,52 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1090,16 +1397,13 @@ }, "node_modules/zmq": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/zmq/-/zmq-2.4.0.tgz", + "integrity": "sha512-M7o1HPC+4gb3qL1+pddKJiu/uBu9un+r8c1nnDLS9HZKvMhIDyhGLFtmMbx9EnpP29KjNshrR27RyWBfhbNSRQ==", + "hasInstallScript": true, "engines": { "node": ">=0.7.9" } }, - "packages_imported/kotlin-node/22.5.5-pre.844": { - "name": "kotlin-node", - "version": "22.5.5-pre.844", - "extraneous": true, - "devDependencies": {} - }, "packages_imported/ktor-ktor-client-core/3.2.3": { "name": "ktor-ktor-client-core", "version": "3.2.3", @@ -1108,15 +1412,6 @@ }, "devDependencies": {} }, - "packages_imported/ktor-ktor-client-ktor-client-core/3.1.2": { - "name": "ktor-ktor-client-ktor-client-core", - "version": "3.1.2", - "extraneous": true, - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, "packages/apollo-kotlin-apollo-annotations": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} @@ -1129,118 +1424,65 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } + "packages/apollo-kotlin-apollo-api": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" + "packages/apollo-kotlin-apollo-api-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } + "packages/apollo-kotlin-apollo-ast": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "packages/apollo-kotlin-apollo-ast-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", + "packages/apollo-kotlin-apollo-engine-tests": { + "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "ws": "8.18.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } + "devDependencies": {} }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" + "packages/apollo-kotlin-apollo-engine-tests-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-annotations-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-annotations-wasm-js": { + "packages/apollo-kotlin-apollo-execution": { "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, "devDependencies": {} }, - "packages/apollo-kotlin-apollo-annotations-wasm-js-test": { + "packages/apollo-kotlin-apollo-execution-test": { "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, "devDependencies": { - "typescript": "5.5.4" + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-api": { + "packages/apollo-kotlin-apollo-mpp-utils": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} }, - "packages/apollo-kotlin-apollo-api-test": { + "packages/apollo-kotlin-apollo-mpp-utils-test": { "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": { "kotlin-web-helpers": "2.1.0", @@ -1248,978 +1490,52 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-api-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", + "packages/apollo-kotlin-apollo-normalized-cache": { + "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" + "ws": "8.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" - } + "devDependencies": {} }, - "packages/apollo-kotlin-apollo-api-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" + "packages/apollo-kotlin-apollo-normalized-cache-api": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-normalized-cache-api-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-api-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", + "packages/apollo-kotlin-apollo-normalized-cache-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { - "format-util": "^1.0.5" + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-api-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", + "packages/apollo-kotlin-apollo-runtime": { + "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { - "brace-expansion": "^2.0.1" + "ws": "8.18.0" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "devDependencies": {} + }, + "packages/apollo-kotlin-apollo-runtime-test": { + "version": "5.0.0-alpha.3-SNAPSHOT", + "dependencies": { + "ws": "8.18.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-api-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-api-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-api-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-api-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-api-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-ast": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-ast-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-ast-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-ast-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-ast-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-engine-tests": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-engine-tests-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-engine-tests-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-engine-tests-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-engine-tests-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-execution": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-execution-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-execution-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-execution-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-execution-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-mpp-utils-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-mpp-utils-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-mpp-utils-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-mpp-utils-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-normalized-cache-api": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-normalized-cache-api-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-normalized-cache-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-normalized-cache-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-normalized-cache-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": { - "typescript": "5.5.4" - } - }, - "packages/apollo-kotlin-apollo-runtime": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-runtime-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-runtime-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-runtime-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-runtime-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, "packages/apollo-kotlin-apollo-testing-support": { @@ -2241,113 +1557,6 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-testing-support-internal-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-testing-support-internal-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-testing-support-internal-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": { - "typescript": "5.5.4" - } - }, "packages/apollo-kotlin-apollo-testing-support-test": { "version": "5.0.0-alpha.3-SNAPSHOT", "dependencies": { @@ -2358,121 +1567,6 @@ "mocha": "11.7.1", "source-map-support": "0.5.21" } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/chokidar": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/diff": { - "version": "7.0.0", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/mocha": { - "version": "11.7.1", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/readdirp": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-kotlin-apollo-testing-support-test/node_modules/workerpool": { - "version": "9.3.3", - "dev": true, - "license": "Apache-2.0" - }, - "packages/apollo-kotlin-apollo-testing-support-wasm-js": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-kotlin-apollo-testing-support-wasm-js-test": { - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "dependencies": { - "format-util": "^1.0.5", - "ws": "8.18.0" - }, - "devDependencies": { - "typescript": "5.5.4" - } } } } diff --git a/kotlin-js-store/wasm/package-lock.json b/kotlin-js-store/wasm/package-lock.json index 5a7363b8f05..f13a574789a 100644 --- a/kotlin-js-store/wasm/package-lock.json +++ b/kotlin-js-store/wasm/package-lock.json @@ -138,11 +138,17 @@ "link": true }, "node_modules/async": { - "version": "0.1.22" + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==", + "engines": { + "node": "*" + } }, "node_modules/commander": { "version": "2.9.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", "dependencies": { "graceful-readlink": ">= 1.0.0" }, @@ -152,11 +158,13 @@ }, "node_modules/graceful-readlink": { "version": "1.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" }, "node_modules/iconv-lite": { "version": "0.6.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -165,14 +173,20 @@ } }, "node_modules/mdns2": { - "version": "2.1.4" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdns2/-/mdns2-2.1.4.tgz", + "integrity": "sha512-aStBO1nRHcaTEU6+Ic9YS8+F6AhnG893VTBSS86P/H+jxsCWBk3//eQAejNOg8D9kFjF+dMA89w3jHtgaEGbuQ==", + "hasInstallScript": true }, "node_modules/mout": { - "version": "0.2.0" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", + "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" }, "node_modules/needle": { "version": "3.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -200,25 +214,32 @@ }, "node_modules/network/node_modules/async": { "version": "1.5.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" }, "node_modules/node-uuid": { "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", + "deprecated": "Use uuid module instead", "bin": { "uuid": "bin/uuid" } }, "node_modules/safer-buffer": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { "version": "1.4.1", - "license": "ISC" + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/wmic": { "version": "1.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wmic/-/wmic-1.1.1.tgz", + "integrity": "sha512-6lbonssALks49dX9bJTE8i54OTjbbLfd3IraFfG1ZR1ZrEbEynCt471IX5SfslZaFwISJKdUFHjOWHk0Brs5eg==", "dependencies": { "async": "^3.2.0", "iconv-lite": "^0.5.0" @@ -226,11 +247,13 @@ }, "node_modules/wmic/node_modules/async": { "version": "3.2.6", - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/wmic/node_modules/iconv-lite": { "version": "0.5.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -240,6 +263,9 @@ }, "node_modules/zmq": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/zmq/-/zmq-2.4.0.tgz", + "integrity": "sha512-M7o1HPC+4gb3qL1+pddKJiu/uBu9un+r8c1nnDLS9HZKvMhIDyhGLFtmMbx9EnpP29KjNshrR27RyWBfhbNSRQ==", + "hasInstallScript": true, "engines": { "node": ">=0.7.9" } diff --git a/tests/kotlin-js-store/package-lock.json b/tests/kotlin-js-store/package-lock.json index a065252aa1e..aa091e43a65 100644 --- a/tests/kotlin-js-store/package-lock.json +++ b/tests/kotlin-js-store/package-lock.json @@ -49,6 +49,15 @@ "node": ">=0.1.90" } }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "engines": { + "node": ">=14.17.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -67,9 +76,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -79,9 +88,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { "node": ">=12" @@ -114,9 +123,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -415,6 +424,50 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -464,16 +517,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-import-phases": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", @@ -487,16 +530,15 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -520,36 +562,16 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, "peerDependencies": { - "ajv": "^6.9.1" + "ajv": "^8.8.2" } }, "node_modules/ansi-regex": { @@ -731,27 +753,24 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.5", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", + "depd": "~1.1.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.8" } }, "node_modules/body-parser/node_modules/iconv-lite": { @@ -766,18 +785,6 @@ "node": ">=0.10.0" } }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1144,22 +1151,12 @@ } }, "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">= 0.6" } }, "node_modules/di": { @@ -1168,6 +1165,15 @@ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -1482,13 +1488,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -1611,18 +1610,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/format-util": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", @@ -1838,28 +1825,19 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "dependencies": { - "depd": "2.0.0", + "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/http-proxy": { @@ -2124,11 +2102,10 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "node_modules/jsonfile": { "version": "4.0.0", @@ -2254,6 +2231,15 @@ "node": ">=0.10.0" } }, + "node_modules/kotlin-web-helpers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", + "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", + "dev": true, + "dependencies": { + "format-util": "^1.0.5" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -2450,71 +2436,242 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mout": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", - "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/needle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "node_modules/mocha": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", + "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", + "dev": true, "dependencies": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "bin": { - "needle": "bin/needle" + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 4.4.x" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "engines": { - "node": ">= 0.6" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/network": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/network/-/network-0.7.0.tgz", - "integrity": "sha512-AquYHEZFrPi1WPaMg+21iTKN7aMERP70frgK41lbTt/9tTQcwgaOtlqHRmVbxJjWYWJy033jUbA8xXhFArXodw==", + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, "dependencies": { - "async": "^1.5.2", - "commander": "2.9.0", - "needle": "^3.0.0", - "wmic": "^1.0.1" + "readdirp": "^4.0.1" }, - "bin": { - "network": "bin/network" + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/network/node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/mout": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-0.2.0.tgz", + "integrity": "sha512-Y21WNNZuxo5EACa3ybj71s7hy1Qolw1xJ+aJuUPq6SmTZVjfo7MmYq2Q1YsanPV94rFYGf744udOIf7HPLBZKQ==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/network": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/network/-/network-0.7.0.tgz", + "integrity": "sha512-AquYHEZFrPi1WPaMg+21iTKN7aMERP70frgK41lbTt/9tTQcwgaOtlqHRmVbxJjWYWJy033jUbA8xXhFArXodw==", + "dependencies": { + "async": "^1.5.2", + "commander": "2.9.0", + "needle": "^3.0.0", + "wmic": "^1.0.1" + }, + "bin": { + "network": "bin/network" + } + }, + "node_modules/network/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true }, "node_modules/node-uuid": { "version": "1.4.8", @@ -2543,18 +2700,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2777,13 +2922,10 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, "engines": { "node": ">=0.6" }, @@ -2810,13 +2952,13 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", "dev": true, "dependencies": { "bytes": "3.1.2", - "http-errors": "2.0.0", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -2995,15 +3137,15 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, - "peer": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -3061,76 +3203,16 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/socket.io": { @@ -3512,81 +3594,28 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=14.14" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" + "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" @@ -3694,26 +3723,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3755,21 +3764,22 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", - "dev": true, - "peer": true, - "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "version": "5.100.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", + "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -3779,11 +3789,11 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -3801,6 +3811,71 @@ } } }, + "node_modules/webpack-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-cli/node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -3862,6 +3937,12 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", + "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4007,6 +4088,12 @@ "extraneous": true, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-api/5.0.0-alpha.2-SNAPSHOT": { + "name": "apollo-kotlin-apollo-api", + "version": "5.0.0-alpha.2-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-api/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-api", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4040,6 +4127,16 @@ }, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-normalized-cache/5.0.0-alpha.2-SNAPSHOT": { + "name": "apollo-kotlin-apollo-normalized-cache", + "version": "5.0.0-alpha.2-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-normalized-cache/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-normalized-cache", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4059,12 +4156,6 @@ }, "devDependencies": {} }, - "packages_imported/apollo-kotlin-apollo-runtime-wasm-js/5.0.0-alpha.3-SNAPSHOT": { - "name": "apollo-kotlin-apollo-runtime-wasm-js", - "version": "5.0.0-alpha.3-SNAPSHOT", - "extraneous": true, - "devDependencies": {} - }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.0-SNAPSHOT": { "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.0-SNAPSHOT", @@ -4087,6 +4178,16 @@ }, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.2-SNAPSHOT": { + "name": "apollo-kotlin-apollo-runtime", + "version": "5.0.0-alpha.2-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4118,6 +4219,12 @@ "extraneous": true, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-testing-support-internal/5.0.0-alpha.2-SNAPSHOT": { + "name": "apollo-kotlin-apollo-testing-support-internal", + "version": "5.0.0-alpha.2-SNAPSHOT", + "extraneous": true, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-testing-support-internal/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-testing-support-internal", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4151,6 +4258,16 @@ }, "devDependencies": {} }, + "packages_imported/apollo-kotlin-apollo-testing-support/5.0.0-alpha.2-SNAPSHOT": { + "name": "apollo-kotlin-apollo-testing-support", + "version": "5.0.0-alpha.2-SNAPSHOT", + "extraneous": true, + "dependencies": { + "format-util": "^1.0.5", + "ws": "8.18.0" + }, + "devDependencies": {} + }, "packages_imported/apollo-kotlin-apollo-testing-support/5.0.0-alpha.3-SNAPSHOT": { "name": "apollo-kotlin-apollo-testing-support", "version": "5.0.0-alpha.3-SNAPSHOT", @@ -4210,2162 +4327,68 @@ "webpack-cli": "6.0.1" } }, - "packages/apollo-tests-browser-tests-test/node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", - "dev": true, - "engines": { - "node": ">=14.17.0" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "engines": { - "node": ">=18.12.0" + "packages/apollo-tests-defer": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } + "devDependencies": {} }, - "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "engines": { - "node": ">=18.12.0" + "packages/apollo-tests-defer-test": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-browser-tests-test/node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" + "packages/apollo-tests-gzip": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } + "devDependencies": {} }, - "packages/apollo-tests-browser-tests-test/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, + "packages/apollo-tests-gzip-test": { + "version": "0.0.0-unspecified", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "ws": "8.18.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-browser-tests-test/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "packages/apollo-tests-browser-tests-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-browser-tests-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/webpack": { - "version": "5.100.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", - "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.2", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.3.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-browser-tests-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-browser-tests-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-browser-tests-wasm-js": { - "version": "0.0.0-unspecified", - "extraneous": true, - "devDependencies": { - "kotlin-web-helpers": "2.0.0", - "source-map-loader": "5.0.0", - "typescript": "5.5.4", - "webpack": "5.94.0", - "webpack-cli": "5.1.4", - "webpack-dev-server": "4.15.2" - } - }, - "packages/apollo-tests-browser-tests-wasm-js-test": { - "version": "0.0.0-unspecified", - "extraneous": true, - "devDependencies": { - "karma": "6.4.4", - "karma-chrome-launcher": "3.2.0", - "karma-mocha": "2.0.1", - "karma-sourcemap-loader": "0.4.0", - "karma-webpack": "5.0.1", - "kotlin-web-helpers": "2.0.0", - "mocha": "10.7.3", - "source-map-loader": "5.0.0", - "typescript": "5.5.4", - "webpack": "5.94.0", - "webpack-cli": "5.1.4", - "webpack-dev-server": "4.15.2" - } - }, - "packages/apollo-tests-defer": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-defer-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-defer-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-defer-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-defer-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-defer-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-defer-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-defer-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-defer-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-defer-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-defer-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-defer-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-defer-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-defer-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-defer-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-defer-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-gzip": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-gzip-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-gzip-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-gzip-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-gzip-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-gzip-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-gzip-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-gzip-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-gzip-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-gzip-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-gzip-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-gzip-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-gzip-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-gzip-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-gzip-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-gzip-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-integration-tests": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-integration-tests-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-integration-tests-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-integration-tests-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-integration-tests-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-js": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-js-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-js-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-js-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-js-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-js-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-js-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-js-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-js-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-js-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-js-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-js-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-js-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-js-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-js-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-js-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-jsexport": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-jsexport-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-jsexport-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-jsexport-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-jsexport-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-jsexport-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-operation-based": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-models-operation-based-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-models-operation-based-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-models-operation-based-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-operation-based-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-response-based": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-models-response-based-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-models-response-based-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true - }, - "packages/apollo-tests-models-response-based-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-models-response-based-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-multipart": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": {} - }, - "packages/apollo-tests-multipart-test": { - "version": "0.0.0-unspecified", - "dependencies": { - "ws": "8.18.0" - }, - "devDependencies": { - "kotlin-web-helpers": "2.1.0", - "mocha": "11.7.1", - "source-map-support": "0.5.21" - } - }, - "packages/apollo-tests-multipart-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/apollo-tests-multipart-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-multipart-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-multipart-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-multipart-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/apollo-tests-multipart-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-multipart-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, - "dependencies": { - "format-util": "^1.0.5" - } - }, - "packages/apollo-tests-multipart-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/apollo-tests-multipart-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "packages/apollo-tests-multipart-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-multipart-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" + "packages/apollo-tests-integration-tests": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-multipart-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true + "devDependencies": {} }, - "packages/apollo-tests-multipart-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, + "packages/apollo-tests-integration-tests-test": { + "version": "0.0.0-unspecified", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "ws": "8.18.0" }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-multipart-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets": { + "packages/apollo-tests-js": { "version": "0.0.0-unspecified", "dependencies": { "ws": "8.18.0" }, "devDependencies": {} }, - "packages/apollo-tests-websockets-test": { + "packages/apollo-tests-js-test": { "version": "0.0.0-unspecified", "dependencies": { "ws": "8.18.0" @@ -6376,199 +4399,94 @@ "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets-test/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "packages/apollo-tests-jsexport": { + "version": "0.0.0-unspecified", "dependencies": { - "balanced-match": "^1.0.0" - } + "ws": "8.18.0" + }, + "devDependencies": {} }, - "packages/apollo-tests-websockets-test/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "packages/apollo-tests-jsexport-test": { + "version": "0.0.0-unspecified", "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" + "ws": "8.18.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets-test/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, + "packages/apollo-tests-models-operation-based": { + "version": "0.0.0-unspecified", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "ws": "8.18.0" }, - "engines": { - "node": ">=12" - } + "devDependencies": {} }, - "packages/apollo-tests-websockets-test/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "packages/apollo-tests-models-operation-based-test": { + "version": "0.0.0-unspecified", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "ws": "8.18.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "packages/apollo-tests-websockets-test/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "engines": { - "node": ">=0.3.1" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets-test/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "packages/apollo-tests-models-response-based": { + "version": "0.0.0-unspecified", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "ws": "8.18.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "devDependencies": {} }, - "packages/apollo-tests-websockets-test/node_modules/kotlin-web-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kotlin-web-helpers/-/kotlin-web-helpers-2.1.0.tgz", - "integrity": "sha512-NAJhiNB84tnvJ5EQx7iER3GWw7rsTZkX9HVHZpe7E3dDBD/dhTzqgSwNU3MfQjniy2rB04bP24WM9Z32ntUWRg==", - "dev": true, + "packages/apollo-tests-models-response-based-test": { + "version": "0.0.0-unspecified", "dependencies": { - "format-util": "^1.0.5" + "ws": "8.18.0" + }, + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets-test/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "packages/apollo-tests-multipart": { + "version": "0.0.0-unspecified", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "ws": "8.18.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "devDependencies": {} }, - "packages/apollo-tests-websockets-test/node_modules/mocha": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.1.tgz", - "integrity": "sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==", - "dev": true, + "packages/apollo-tests-multipart-test": { + "version": "0.0.0-unspecified", "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "ws": "8.18.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } }, - "packages/apollo-tests-websockets-test/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "packages/apollo-tests-websockets-test/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "engines": { - "node": ">= 14.18.0" + "packages/apollo-tests-websockets": { + "version": "0.0.0-unspecified", + "dependencies": { + "ws": "8.18.0" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "packages/apollo-tests-websockets-test/node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true + "devDependencies": {} }, - "packages/apollo-tests-websockets-test/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, + "packages/apollo-tests-websockets-test": { + "version": "0.0.0-unspecified", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "ws": "8.18.0" }, - "engines": { - "node": ">=12" - } - }, - "packages/apollo-tests-websockets-test/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "devDependencies": { + "kotlin-web-helpers": "2.1.0", + "mocha": "11.7.1", + "source-map-support": "0.5.21" } } } diff --git a/tests/kotlin-js-store/wasm/package-lock.json b/tests/kotlin-js-store/wasm/package-lock.json index 744ea710ef6..3c23e2d6f98 100644 --- a/tests/kotlin-js-store/wasm/package-lock.json +++ b/tests/kotlin-js-store/wasm/package-lock.json @@ -182,6 +182,7 @@ "devDependencies": {} }, "packages_imported/apollo-kotlin-apollo-runtime/5.0.0-alpha.3-SNAPSHOT": { + "name": "apollo-kotlin-apollo-runtime", "version": "5.0.0-alpha.3-SNAPSHOT", "devDependencies": {} }, From 40a9de75203fd80ffc106e6f7aa1c6bf178de4f5 Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 16 Oct 2025 11:31:54 +0200 Subject: [PATCH 26/38] Update the accept header with the value agreed upon --- .../incremental/IncrementalDeliveryProtocolImpl.kt | 4 +--- .../apollo-server/patches/@apollo+server+4.11.2.patch | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index 3712e050532..6d791359a3c 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -14,9 +14,7 @@ internal sealed interface IncrementalDeliveryProtocolImpl { } object Draft0_1 : IncrementalDeliveryProtocolImpl { - // TODO To be agreed upon with the router and other clients - override val acceptHeader: String = - "multipart/mixed;incrementalDeliverySpec=20230621, application/graphql-response+json, application/json" + override val acceptHeader: String = "multipart/mixed;incrementalSpec=v0.1, application/graphql-response+json, application/json" override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Draft0_1IncrementalResultsMerger() } diff --git a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch index a516a8c8867..74a83c2afd1 100644 --- a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch +++ b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@apollo/server/dist/esm/ApolloServer.js b/node_modules/@apollo/server/dist/esm/ApolloServer.js -index 7665490..c32a28c 100644 +index 7665490..1e8bb12 100644 --- a/node_modules/@apollo/server/dist/esm/ApolloServer.js +++ b/node_modules/@apollo/server/dist/esm/ApolloServer.js @@ -631,7 +631,7 @@ export const MEDIA_TYPES = { @@ -7,12 +7,12 @@ index 7665490..c32a28c 100644 APPLICATION_GRAPHQL_RESPONSE_JSON: 'application/graphql-response+json; charset=utf-8', MULTIPART_MIXED_NO_DEFER_SPEC: 'multipart/mixed', - MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; deferSpec=20220824', -+ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalDeliverySpec=20230621', ++ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalSpec=v0.1', TEXT_HTML: 'text/html', }; export function chooseContentTypeForSingleResultResponse(head) { diff --git a/node_modules/@apollo/server/dist/esm/runHttpQuery.js b/node_modules/@apollo/server/dist/esm/runHttpQuery.js -index 96ef0ab..d816750 100644 +index 96ef0ab..f35174b 100644 --- a/node_modules/@apollo/server/dist/esm/runHttpQuery.js +++ b/node_modules/@apollo/server/dist/esm/runHttpQuery.js @@ -161,9 +161,9 @@ export async function runHttpQuery({ server, httpRequest, contextValue, schemaDe @@ -20,10 +20,10 @@ index 96ef0ab..d816750 100644 '(@defer or @stream), but the client does not accept multipart/mixed ' + 'HTTP responses. To enable incremental delivery support, add the HTTP ' + - "header 'Accept: multipart/mixed; deferSpec=20220824'.", { extensions: { http: { status: 406 } } }); -+ "header 'Accept: multipart/mixed; incrementalDeliverySpec=20230621'.", { extensions: { http: { status: 406 } } }); ++ "header 'Accept: multipart/mixed; incrementalSpec=v0.1'.", { extensions: { http: { status: 406 } } }); } - graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; deferSpec=20220824'); -+ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalDeliverySpec=20230621'); ++ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalSpec=v0.1'); return { ...graphQLResponse.http, body: { From 13d7de02f07b70d7867cff396b84abed126390c2 Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 16 Oct 2025 11:45:13 +0200 Subject: [PATCH 27/38] Clarify comment --- .../kotlin/com/apollographql/apollo/api/BooleanExpression.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index c9a1cd0a5b7..2cbe23e14d4 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -88,10 +88,10 @@ private fun shouldParseFragment(deferredFragmentIdentifiers: Set Date: Thu, 16 Oct 2025 12:17:52 +0200 Subject: [PATCH 28/38] Rename incremental delivery protocol enums to V0_0 and V0_1 --- .../apollo/api/BooleanExpression.kt | 2 +- .../api/android/apollo-runtime.api | 4 +-- .../api/apollo-runtime.klib.api | 4 +-- .../apollo-runtime/api/jvm/apollo-runtime.api | 4 +-- .../IncrementalDeliveryProtocolImpl.kt | 12 ++++---- ...ger.kt => V0_0IncrementalResultsMerger.kt} | 4 +-- ...ger.kt => V0_1IncrementalResultsMerger.kt} | 4 +-- .../network/IncrementalDeliveryProtocol.kt | 10 +++---- .../network/http/HttpNetworkTransport.kt | 4 +-- .../websocket/WebSocketNetworkTransport.kt | 4 +-- .../network/ws/WebSocketNetworkTransport.kt | 4 +-- ...kt => V0_0IncrementalResultsMergerTest.kt} | 10 +++---- ...kt => V0_1IncrementalResultsMergerTest.kt} | 30 +++++++++---------- ...est.kt => DeferV0_0NormalizedCacheTest.kt} | 2 +- ...erDraftInitialTest.kt => DeferV0_0Test.kt} | 2 +- ...est.kt => DeferV0_1NormalizedCacheTest.kt} | 4 +-- ...{DeferDraft0_1Test.kt => DeferV0_1Test.kt} | 4 +-- .../kotlin/test/DeferWithApolloServerTest.kt | 2 +- 18 files changed, 55 insertions(+), 55 deletions(-) rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/{DraftInitialIncrementalResultsMerger.kt => V0_0IncrementalResultsMerger.kt} (95%) rename libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/{Draft0_1IncrementalResultsMerger.kt => V0_1IncrementalResultsMerger.kt} (97%) rename libraries/apollo-runtime/src/commonTest/kotlin/test/defer/{GraphQL17Alpha2IncrementalResultsMergerTest.kt => V0_0IncrementalResultsMergerTest.kt} (97%) rename libraries/apollo-runtime/src/commonTest/kotlin/test/defer/{GraphQL17Alpha9IncrementalResultsMergerTest.kt => V0_1IncrementalResultsMergerTest.kt} (98%) rename tests/defer/src/commonTest/kotlin/test/{DeferDraftInitialNormalizedCacheTest.kt => DeferV0_0NormalizedCacheTest.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferDraftInitialTest.kt => DeferV0_0Test.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferDraft0_1NormalizedCacheTest.kt => DeferV0_1NormalizedCacheTest.kt} (99%) rename tests/defer/src/commonTest/kotlin/test/{DeferDraft0_1Test.kt => DeferV0_1Test.kt} (99%) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index 2cbe23e14d4..6bd3cbfe456 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -91,7 +91,7 @@ private fun shouldParseFragment(deferredFragmentIdentifiers: Set { // com.apollographql.apollo.network/IncrementalDeliveryProtocol|null[0] - enum entry Draft0_1 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.Draft0_1|null[0] - enum entry DraftInitial // com.apollographql.apollo.network/IncrementalDeliveryProtocol.DraftInitial|null[0] + enum entry V0_0 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.V0_0|null[0] + enum entry V0_1 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.V0_1|null[0] final val entries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries.|#static(){}[0] diff --git a/libraries/apollo-runtime/api/jvm/apollo-runtime.api b/libraries/apollo-runtime/api/jvm/apollo-runtime.api index 0b7e0ffc502..4eed1e8386e 100644 --- a/libraries/apollo-runtime/api/jvm/apollo-runtime.api +++ b/libraries/apollo-runtime/api/jvm/apollo-runtime.api @@ -216,8 +216,8 @@ public final class com/apollographql/apollo/interceptor/RetryOnErrorInterceptorK } public final class com/apollographql/apollo/network/IncrementalDeliveryProtocol : java/lang/Enum { - public static final field Draft0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; - public static final field DraftInitial Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field V0_0 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field V0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun values ()[Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index 6d791359a3c..df5424bdfe3 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -7,21 +7,21 @@ internal sealed interface IncrementalDeliveryProtocolImpl { fun newIncrementalResultsMerger(): IncrementalResultsMerger - object DraftInitial : IncrementalDeliveryProtocolImpl { + object V0_0 : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = DraftInitialIncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_0IncrementalResultsMerger() } - object Draft0_1 : IncrementalDeliveryProtocolImpl { + object V0_1 : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;incrementalSpec=v0.1, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = Draft0_1IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_1IncrementalResultsMerger() } } internal val IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - IncrementalDeliveryProtocol.DraftInitial -> IncrementalDeliveryProtocolImpl.DraftInitial - IncrementalDeliveryProtocol.Draft0_1 -> IncrementalDeliveryProtocolImpl.Draft0_1 + IncrementalDeliveryProtocol.V0_0 -> IncrementalDeliveryProtocolImpl.V0_0 + IncrementalDeliveryProtocol.V0_1 -> IncrementalDeliveryProtocolImpl.V0_1 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt similarity index 95% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt index d3c9d45c230..fc4c193772a 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/DraftInitialIncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt @@ -4,10 +4,10 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.DraftInitial] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.V0_0] protocol format. */ @Suppress("UNCHECKED_CAST") -internal class DraftInitialIncrementalResultsMerger : IncrementalResultsMerger { +internal class V0_0IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt similarity index 97% rename from libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt rename to libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt index a21f202c318..ec676d8f0e1 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/Draft0_1IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt @@ -4,10 +4,10 @@ import com.apollographql.apollo.api.DeferredFragmentIdentifier import okio.BufferedSource /** - * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.Draft0_1] protocol format. + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.V0_1] protocol format. */ @Suppress("UNCHECKED_CAST") -internal class Draft0_1IncrementalResultsMerger : IncrementalResultsMerger { +internal class V0_1IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt index 5b6d2c8b30f..261db36e7e3 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt @@ -8,20 +8,20 @@ import com.apollographql.apollo.annotations.ApolloExperimental @ApolloExperimental enum class IncrementalDeliveryProtocol { /** - * Initial draft format as specified in this historical commit: + * First draft format as specified in this historical commit: * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec, - * and implemented by `graphql.js` version `17.0.0-alpha.2` + * and implemented by `graphql.js` version `17.0.0-alpha.2`, also referred to as `20220824`. * * Only `@defer` is supported with this format. * * This is the default. */ - DraftInitial, + V0_0, /** - * Draft format as specified by https://specs.apollo.dev/incremental/v0.1/, and implemented by `graphql.js` version `17.0.0-alpha.9`. + * Draft v0.1 format as specified by https://specs.apollo.dev/incremental/v0.1/, and implemented by `graphql.js` version `17.0.0-alpha.9`. * * Both `@defer` and `@stream` are supported with this format. */ - Draft0_1 + V0_1, } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index 029d9515a4c..e3f68466f4d 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -326,7 +326,7 @@ private constructor( private var engine: HttpEngine? = null private val interceptors: MutableList = mutableListOf() private var exposeErrorBody: Boolean = false - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 private val headers: MutableList = mutableListOf() fun httpRequestComposer(httpRequestComposer: HttpRequestComposer) = apply { @@ -373,7 +373,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.DraftInitial] + * Default: [IncrementalDeliveryProtocol.V0_0] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index c4b697a05a8..e80da5c5dc7 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -116,7 +116,7 @@ class WebSocketNetworkTransport private constructor( private var pingInterval: Duration? = null private var idleTimeout: Duration? = null private var parserFactory: SubscriptionParserFactory? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 /** * @param serverUrl a server url that is called every time a WebSocket @@ -183,7 +183,7 @@ class WebSocketNetworkTransport private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.DraftInitial] + * Default: [IncrementalDeliveryProtocol.V0_0] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index 685c87c107f..264b597fb62 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -374,7 +374,7 @@ private constructor( private var idleTimeoutMillis: Long? = null private var protocolFactory: WsProtocol.Factory? = null private var reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.DraftInitial + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 /** * Configure the server URL. @@ -444,7 +444,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.DraftInitial] + * Default: [IncrementalDeliveryProtocol.V0_0] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt similarity index 97% rename from libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt rename to libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt index 33f00f1a07a..a871de914c8 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha2IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt @@ -6,7 +6,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.DraftInitialIncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.V0_0IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -18,10 +18,10 @@ private fun String.buffer() = Buffer().writeUtf8(this) @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map -class GraphQL17Alpha2IncrementalResultsMergerTest { +class V0_0IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() + val incrementalResultsMerger = V0_0IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -392,7 +392,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() + val incrementalResultsMerger = V0_0IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -668,7 +668,7 @@ class GraphQL17Alpha2IncrementalResultsMergerTest { @Test fun emptyPayloads() { - val incrementalResultsMerger = DraftInitialIncrementalResultsMerger() + val incrementalResultsMerger = V0_0IncrementalResultsMerger() //language=JSON val payload1 = """ diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt similarity index 98% rename from libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt rename to libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt index 93a87f99d86..82ce73b0231 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/GraphQL17Alpha9IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt @@ -6,7 +6,7 @@ import com.apollographql.apollo.annotations.ApolloInternal import com.apollographql.apollo.api.DeferredFragmentIdentifier import com.apollographql.apollo.api.json.BufferedSourceJsonReader import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.Draft0_1IncrementalResultsMerger +import com.apollographql.apollo.internal.incremental.V0_1IncrementalResultsMerger import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -18,10 +18,10 @@ private fun String.buffer() = Buffer().writeUtf8(this) @Suppress("UNCHECKED_CAST") private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map -class GraphQL17Alpha9IncrementalResultsMergerTest { +class V0_1IncrementalResultsMergerTest { @Test fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -454,7 +454,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -762,7 +762,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { @Test fun emptyPayloads() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ @@ -851,7 +851,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleA() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -970,7 +970,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleA2() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1149,7 +1149,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleB1() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1316,7 +1316,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleB2() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1486,7 +1486,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleD() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1714,7 +1714,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleF() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1794,7 +1794,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleG() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -1963,7 +1963,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun june2023ExampleH() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2135,7 +2135,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun july2025ExampleI() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { @@ -2348,7 +2348,7 @@ class GraphQL17Alpha9IncrementalResultsMergerTest { */ @Test fun july2025ExampleJ() { - val incrementalResultsMerger = Draft0_1IncrementalResultsMerger() + val incrementalResultsMerger = V0_1IncrementalResultsMerger() //language=JSON val payload1 = """ { diff --git a/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt index 72818d9cf4f..40bf823c7b9 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialNormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt @@ -45,7 +45,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferDraftInitialNormalizedCacheTest { +class DeferV0_0NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore diff --git a/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt index e7198702b57..95d9ba77b19 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferDraftInitialTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt @@ -25,7 +25,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeferDraftInitialTest { +class DeferV0_0Test { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient diff --git a/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt index c0329681598..de36ee6613a 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt @@ -48,7 +48,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferDraft0_1NormalizedCacheTest { +class DeferV0_1NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore @@ -60,7 +60,7 @@ class DeferDraft0_1NormalizedCacheTest { .networkTransport( HttpNetworkTransport.Builder() .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) .build() ) .store(store).build() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt similarity index 99% rename from tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt rename to tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt index e778de69a5e..46da4af0484 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferDraft0_1Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt @@ -30,7 +30,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeferDraft0_1Test { +class DeferV0_1Test { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient @@ -40,7 +40,7 @@ class DeferDraft0_1Test { .networkTransport( HttpNetworkTransport.Builder() .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) .build() ) .build() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index 64b951aecfa..301cac4654b 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -49,7 +49,7 @@ class DeferWithApolloServerTest { .networkTransport( HttpNetworkTransport.Builder() .serverUrl("http://127.0.0.1:4000/") - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.Draft0_1) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) .build() ) .build() From 97177bd0dee89ac629660e940b22d5f55a785f99 Mon Sep 17 00:00:00 2001 From: BoD Date: Thu, 16 Oct 2025 14:54:04 +0200 Subject: [PATCH 29/38] Fix API Dump after rebase --- libraries/apollo-api/api/apollo-api.api | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/apollo-api/api/apollo-api.api b/libraries/apollo-api/api/apollo-api.api index 1936bf55553..52981a466ac 100644 --- a/libraries/apollo-api/api/apollo-api.api +++ b/libraries/apollo-api/api/apollo-api.api @@ -426,7 +426,6 @@ public final class com/apollographql/apollo/api/CustomScalarAdapters$Builder { public final fun deferredFragmentIdentifiers (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; public final fun errors (Ljava/util/List;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; public final fun falseVariables (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; - public final fun pendingResultIds (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder; } public final class com/apollographql/apollo/api/CustomScalarAdapters$Key : com/apollographql/apollo/api/ExecutionContext$Key { From 97f29fb4fdb23f7553cceaa6c209276edfea65ae Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 20 Oct 2025 11:22:40 +0200 Subject: [PATCH 30/38] Rename v0.0 -> v0.1 and v0.1 -> v0.2 --- .../IncrementalDeliveryProtocolImpl.kt | 12 +- .../V0_0IncrementalResultsMerger.kt | 89 - .../V0_1IncrementalResultsMerger.kt | 108 +- .../V0_2IncrementalResultsMerger.kt | 129 + .../network/IncrementalDeliveryProtocol.kt | 12 +- .../network/http/HttpNetworkTransport.kt | 4 +- .../websocket/WebSocketNetworkTransport.kt | 4 +- .../network/ws/WebSocketNetworkTransport.kt | 4 +- .../defer/V0_0IncrementalResultsMergerTest.kt | 747 ----- .../defer/V0_1IncrementalResultsMergerTest.kt | 2191 ++------------ .../defer/V0_2IncrementalResultsMergerTest.kt | 2514 +++++++++++++++++ .../patches/@apollo+server+4.11.2.patch | 10 +- .../commonTest/kotlin/test/DeferV0_0Test.kt | 386 --- .../test/DeferV0_1NormalizedCacheTest.kt | 323 +-- .../commonTest/kotlin/test/DeferV0_1Test.kt | 236 +- ...est.kt => DeferV0_2NormalizedCacheTest.kt} | 328 ++- .../commonTest/kotlin/test/DeferV0_2Test.kt | 325 +++ .../kotlin/test/DeferWithApolloServerTest.kt | 5 +- 18 files changed, 3718 insertions(+), 3709 deletions(-) delete mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt create mode 100644 libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_2IncrementalResultsMerger.kt delete mode 100644 libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt create mode 100644 libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_2IncrementalResultsMergerTest.kt delete mode 100644 tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt rename tests/defer/src/commonTest/kotlin/test/{DeferV0_0NormalizedCacheTest.kt => DeferV0_2NormalizedCacheTest.kt} (52%) create mode 100644 tests/defer/src/commonTest/kotlin/test/DeferV0_2Test.kt diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt index df5424bdfe3..9cc6e0759b5 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl.kt @@ -7,21 +7,21 @@ internal sealed interface IncrementalDeliveryProtocolImpl { fun newIncrementalResultsMerger(): IncrementalResultsMerger - object V0_0 : IncrementalDeliveryProtocolImpl { + object V0_1 : IncrementalDeliveryProtocolImpl { override val acceptHeader: String = "multipart/mixed;deferSpec=20220824, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_0IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_1IncrementalResultsMerger() } - object V0_1 : IncrementalDeliveryProtocolImpl { - override val acceptHeader: String = "multipart/mixed;incrementalSpec=v0.1, application/graphql-response+json, application/json" + object V0_2 : IncrementalDeliveryProtocolImpl { + override val acceptHeader: String = "multipart/mixed;incrementalSpec=v0.2, application/graphql-response+json, application/json" - override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_1IncrementalResultsMerger() + override fun newIncrementalResultsMerger(): IncrementalResultsMerger = V0_2IncrementalResultsMerger() } } internal val IncrementalDeliveryProtocol.impl: IncrementalDeliveryProtocolImpl get() = when (this) { - IncrementalDeliveryProtocol.V0_0 -> IncrementalDeliveryProtocolImpl.V0_0 IncrementalDeliveryProtocol.V0_1 -> IncrementalDeliveryProtocolImpl.V0_1 + IncrementalDeliveryProtocol.V0_2 -> IncrementalDeliveryProtocolImpl.V0_2 } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt deleted file mode 100644 index fc4c193772a..00000000000 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_0IncrementalResultsMerger.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.apollographql.apollo.internal.incremental - -import com.apollographql.apollo.api.DeferredFragmentIdentifier -import okio.BufferedSource - -/** - * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.V0_0] protocol format. - */ -@Suppress("UNCHECKED_CAST") -internal class V0_0IncrementalResultsMerger : IncrementalResultsMerger { - private val _merged: MutableJsonMap = mutableMapOf() - override val merged: JsonMap = _merged - - private val _deferredFragmentIdentifiers = mutableSetOf() - - /** - * For this protocol, this represents the set of fragment ids that are already merged. - */ - override val deferredFragmentIdentifiers: Set = _deferredFragmentIdentifiers - - override var hasNext: Boolean = true - private set - - override var isEmptyResponse: Boolean = false - private set - - override fun merge(part: BufferedSource): JsonMap { - return merge(part.toJsonMap()) - } - - override fun merge(part: JsonMap): JsonMap { - if (merged.isEmpty()) { - // Initial part, no merging needed - _merged += part - return merged - } - - val incremental = part["incremental"] as? List - if (incremental == null) { - isEmptyResponse = true - } else { - isEmptyResponse = false - val mergedErrors = mutableListOf() - val mergedExtensions = mutableListOf() - for (incrementalResult in incremental) { - incrementalResult(incrementalResult) - // Merge errors and extensions (if any) of the incremental result - (incrementalResult["errors"] as? List)?.let { mergedErrors += it } - (incrementalResult["extensions"] as? JsonMap)?.let { mergedExtensions += it } - } - // Keep only this payload's errors and extensions, if any - if (mergedErrors.isNotEmpty()) { - _merged["errors"] = mergedErrors - } else { - _merged.remove("errors") - } - if (mergedExtensions.isNotEmpty()) { - _merged["extensions"] = mapOf("incremental" to mergedExtensions) - } else { - _merged.remove("extensions") - } - } - - hasNext = part["hasNext"] as Boolean? ?: false - - return merged - } - - private fun incrementalResult(incrementalResult: JsonMap) { - val data = incrementalResult["data"] as JsonMap? - val path = incrementalResult["path"] as List - val mergedData = merged["data"] as JsonMap - - // data can be null if there are errors - if (data != null) { - val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap - deepMergeObject(nodeToMergeInto, data) - - _deferredFragmentIdentifiers += DeferredFragmentIdentifier(path = path, label = incrementalResult["label"] as String?) - } - } - - override fun reset() { - _merged.clear() - _deferredFragmentIdentifiers.clear() - hasNext = true - isEmptyResponse = false - } -} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt index ec676d8f0e1..36043b07272 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_1IncrementalResultsMerger.kt @@ -11,15 +11,12 @@ internal class V0_1IncrementalResultsMerger : IncrementalResultsMerger { private val _merged: MutableJsonMap = mutableMapOf() override val merged: JsonMap = _merged - /** - * Map of identifiers to their corresponding IncrementalResultIdentifier, found in `pending`. - */ - private val _pendingResultIds = mutableMapOf() + private val _deferredFragmentIdentifiers = mutableSetOf() /** - * For this protocol, this represents the set of ids that are pending. + * For this protocol, this represents the set of fragment ids that are already merged. */ - override val deferredFragmentIdentifiers: Set get() = _pendingResultIds.values.toSet() + DeferredFragmentIdentifier.Pending + override val deferredFragmentIdentifiers: Set = _deferredFragmentIdentifiers override var hasNext: Boolean = true private set @@ -32,97 +29,60 @@ internal class V0_1IncrementalResultsMerger : IncrementalResultsMerger { } override fun merge(part: JsonMap): JsonMap { - val completed = part["completed"] as? List if (merged.isEmpty()) { - // Initial part, no merging needed (strip some fields that should not appear in the final result) - _merged += part - "hasNext" - "pending" - handlePending(part) - handleCompleted(completed) + // Initial part, no merging needed + _merged += part return merged } - handlePending(part) val incremental = part["incremental"] as? List - if (incremental != null) { + if (incremental == null) { + isEmptyResponse = true + } else { + isEmptyResponse = false + val mergedErrors = mutableListOf() + val mergedExtensions = mutableListOf() for (incrementalResult in incremental) { - mergeIncrementalResult(incrementalResult) - // Merge errors (if any) of the incremental result - (incrementalResult["errors"] as? List)?.let { getOrPutMergedErrors() += it } + incrementalResult(incrementalResult) + // Merge errors and extensions (if any) of the incremental result + (incrementalResult["errors"] as? List)?.let { mergedErrors += it } + (incrementalResult["extensions"] as? JsonMap)?.let { mergedExtensions += it } + } + // Keep only this payload's errors and extensions, if any + if (mergedErrors.isNotEmpty()) { + _merged["errors"] = mergedErrors + } else { + _merged.remove("errors") + } + if (mergedExtensions.isNotEmpty()) { + _merged["extensions"] = mapOf("incremental" to mergedExtensions) + } else { + _merged.remove("extensions") } } - isEmptyResponse = completed == null && incremental == null hasNext = part["hasNext"] as Boolean? ?: false - handleCompleted(completed) - - (part["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } - return merged } - private fun getOrPutMergedErrors() = _merged.getOrPut("errors") { mutableListOf() } as MutableList - - private fun getOrPutExtensions() = _merged.getOrPut("extensions") { mutableMapOf() } as MutableJsonMap - - private fun handlePending(part: JsonMap) { - val pending = part["pending"] as? List - if (pending != null) { - for (pendingResult in pending) { - val id = pendingResult["id"] as String - val path = pendingResult["path"] as List - val label = pendingResult["label"] as String? - _pendingResultIds[id] = DeferredFragmentIdentifier(path = path, label = label) - } - } - } - - private fun handleCompleted(completed: List?) { - if (completed != null) { - for (completedResult in completed) { - // Merge errors (if any) of the completed result - val errors = completedResult["errors"] as? List - if (errors != null) { - getOrPutMergedErrors() += errors - } else { - // Fragment is no longer pending - only if there were no errors - val id = completedResult["id"] as String - _pendingResultIds.remove(id) ?: error("Id '$id' not found in pending results") - } - } - } - } - - private fun mergeIncrementalResult(incrementalResult: JsonMap) { - val id = incrementalResult["id"] as String? ?: error("No id found in incremental result") + private fun incrementalResult(incrementalResult: JsonMap) { val data = incrementalResult["data"] as JsonMap? - val items = incrementalResult["items"] as List? - val subPath = incrementalResult["subPath"] as List? ?: emptyList() - val path = (_pendingResultIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath + val path = incrementalResult["path"] as List val mergedData = merged["data"] as JsonMap - val nodeToMergeInto = nodeAtPath(mergedData, path) - when { - data != null -> { - deepMergeObject(nodeToMergeInto as MutableJsonMap, data) - } - items != null -> { - mergeList(nodeToMergeInto as MutableList, items) - } + // data can be null if there are errors + if (data != null) { + val nodeToMergeInto = nodeAtPath(mergedData, path) as MutableJsonMap + deepMergeObject(nodeToMergeInto, data) - else -> { - error("Neither data nor items found in incremental result") - } + _deferredFragmentIdentifiers += DeferredFragmentIdentifier(path = path, label = incrementalResult["label"] as String?) } } - private fun mergeList(destination: MutableList, items: List) { - destination.addAll(items) - } - override fun reset() { _merged.clear() - _pendingResultIds.clear() + _deferredFragmentIdentifiers.clear() hasNext = true isEmptyResponse = false } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_2IncrementalResultsMerger.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_2IncrementalResultsMerger.kt new file mode 100644 index 00000000000..f946fd4a893 --- /dev/null +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/internal/incremental/V0_2IncrementalResultsMerger.kt @@ -0,0 +1,129 @@ +package com.apollographql.apollo.internal.incremental + +import com.apollographql.apollo.api.DeferredFragmentIdentifier +import okio.BufferedSource + +/** + * Merger for the [com.apollographql.apollo.network.IncrementalDeliveryProtocol.V0_2] protocol format. + */ +@Suppress("UNCHECKED_CAST") +internal class V0_2IncrementalResultsMerger : IncrementalResultsMerger { + private val _merged: MutableJsonMap = mutableMapOf() + override val merged: JsonMap = _merged + + /** + * Map of identifiers to their corresponding IncrementalResultIdentifier, found in `pending`. + */ + private val _pendingResultIds = mutableMapOf() + + /** + * For this protocol, this represents the set of ids that are pending. + */ + override val deferredFragmentIdentifiers: Set get() = _pendingResultIds.values.toSet() + DeferredFragmentIdentifier.Pending + + override var hasNext: Boolean = true + private set + + override var isEmptyResponse: Boolean = false + private set + + override fun merge(part: BufferedSource): JsonMap { + return merge(part.toJsonMap()) + } + + override fun merge(part: JsonMap): JsonMap { + val completed = part["completed"] as? List + if (merged.isEmpty()) { + // Initial part, no merging needed (strip some fields that should not appear in the final result) + _merged += part - "hasNext" - "pending" + handlePending(part) + handleCompleted(completed) + return merged + } + handlePending(part) + + val incremental = part["incremental"] as? List + if (incremental != null) { + for (incrementalResult in incremental) { + mergeIncrementalResult(incrementalResult) + // Merge errors (if any) of the incremental result + (incrementalResult["errors"] as? List)?.let { getOrPutMergedErrors() += it } + } + } + isEmptyResponse = completed == null && incremental == null + + hasNext = part["hasNext"] as Boolean? ?: false + + handleCompleted(completed) + + (part["extensions"] as? JsonMap)?.let { getOrPutExtensions() += it } + + return merged + } + + private fun getOrPutMergedErrors() = _merged.getOrPut("errors") { mutableListOf() } as MutableList + + private fun getOrPutExtensions() = _merged.getOrPut("extensions") { mutableMapOf() } as MutableJsonMap + + private fun handlePending(part: JsonMap) { + val pending = part["pending"] as? List + if (pending != null) { + for (pendingResult in pending) { + val id = pendingResult["id"] as String + val path = pendingResult["path"] as List + val label = pendingResult["label"] as String? + _pendingResultIds[id] = DeferredFragmentIdentifier(path = path, label = label) + } + } + } + + private fun handleCompleted(completed: List?) { + if (completed != null) { + for (completedResult in completed) { + // Merge errors (if any) of the completed result + val errors = completedResult["errors"] as? List + if (errors != null) { + getOrPutMergedErrors() += errors + } else { + // Fragment is no longer pending - only if there were no errors + val id = completedResult["id"] as String + _pendingResultIds.remove(id) ?: error("Id '$id' not found in pending results") + } + } + } + } + + private fun mergeIncrementalResult(incrementalResult: JsonMap) { + val id = incrementalResult["id"] as String? ?: error("No id found in incremental result") + val data = incrementalResult["data"] as JsonMap? + val items = incrementalResult["items"] as List? + val subPath = incrementalResult["subPath"] as List? ?: emptyList() + val path = (_pendingResultIds[id]?.path ?: error("Id '$id' not found in pending results")) + subPath + val mergedData = merged["data"] as JsonMap + val nodeToMergeInto = nodeAtPath(mergedData, path) + when { + data != null -> { + deepMergeObject(nodeToMergeInto as MutableJsonMap, data) + } + + items != null -> { + mergeList(nodeToMergeInto as MutableList, items) + } + + else -> { + error("Neither data nor items found in incremental result") + } + } + } + + private fun mergeList(destination: MutableList, items: List) { + destination.addAll(items) + } + + override fun reset() { + _merged.clear() + _pendingResultIds.clear() + hasNext = true + isEmptyResponse = false + } +} diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt index 261db36e7e3..d8240aac561 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt @@ -8,20 +8,20 @@ import com.apollographql.apollo.annotations.ApolloExperimental @ApolloExperimental enum class IncrementalDeliveryProtocol { /** - * First draft format as specified in this historical commit: - * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec, - * and implemented by `graphql.js` version `17.0.0-alpha.2`, also referred to as `20220824`. + * Draft v0.1 format as specified by https://specs.apollo.dev/incremental/v0.1/, and in this historical commit: + * https://github.com/graphql/graphql-spec/tree/48cf7263a71a683fab03d45d309fd42d8d9a6659/spec, and implemented by `graphql.js` + * version `17.0.0-alpha.2`, also referred to as `20220824`. * * Only `@defer` is supported with this format. * * This is the default. */ - V0_0, + V0_1, /** - * Draft v0.1 format as specified by https://specs.apollo.dev/incremental/v0.1/, and implemented by `graphql.js` version `17.0.0-alpha.9`. + * Draft v0.2 format as specified by https://specs.apollo.dev/incremental/v0.2/, and implemented by `graphql.js` version `17.0.0-alpha.9`. * * Both `@defer` and `@stream` are supported with this format. */ - V0_1, + V0_2, } diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index e3f68466f4d..a10c87e5883 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -326,7 +326,7 @@ private constructor( private var engine: HttpEngine? = null private val interceptors: MutableList = mutableListOf() private var exposeErrorBody: Boolean = false - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_1 private val headers: MutableList = mutableListOf() fun httpRequestComposer(httpRequestComposer: HttpRequestComposer) = apply { @@ -373,7 +373,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.V0_0] + * Default: [IncrementalDeliveryProtocol.V0_1] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt index e80da5c5dc7..5804644be8c 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketNetworkTransport.kt @@ -116,7 +116,7 @@ class WebSocketNetworkTransport private constructor( private var pingInterval: Duration? = null private var idleTimeout: Duration? = null private var parserFactory: SubscriptionParserFactory? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_1 /** * @param serverUrl a server url that is called every time a WebSocket @@ -183,7 +183,7 @@ class WebSocketNetworkTransport private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.V0_0] + * Default: [IncrementalDeliveryProtocol.V0_1] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt index 264b597fb62..5c93a5ca2dc 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/ws/WebSocketNetworkTransport.kt @@ -374,7 +374,7 @@ private constructor( private var idleTimeoutMillis: Long? = null private var protocolFactory: WsProtocol.Factory? = null private var reopenWhen: (suspend (Throwable, attempt: Long) -> Boolean)? = null - private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_0 + private var incrementalDeliveryProtocol: IncrementalDeliveryProtocol = IncrementalDeliveryProtocol.V0_1 /** * Configure the server URL. @@ -444,7 +444,7 @@ private constructor( /** * The incremental delivery protocol to use when using `@defer` and/or `@stream`. * - * Default: [IncrementalDeliveryProtocol.V0_0] + * Default: [IncrementalDeliveryProtocol.V0_1] */ @ApolloExperimental fun incrementalDeliveryProtocol(incrementalDeliveryProtocol: IncrementalDeliveryProtocol) = apply { diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt deleted file mode 100644 index a871de914c8..00000000000 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_0IncrementalResultsMergerTest.kt +++ /dev/null @@ -1,747 +0,0 @@ -@file:OptIn(ApolloInternal::class) - -package test.defer - -import com.apollographql.apollo.annotations.ApolloInternal -import com.apollographql.apollo.api.DeferredFragmentIdentifier -import com.apollographql.apollo.api.json.BufferedSourceJsonReader -import com.apollographql.apollo.api.json.readAny -import com.apollographql.apollo.internal.incremental.V0_0IncrementalResultsMerger -import okio.Buffer -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -private fun String.buffer() = Buffer().writeUtf8(this) - -@Suppress("UNCHECKED_CAST") -private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map - -class V0_0IncrementalResultsMergerTest { - @Test - fun mergeJsonSingleIncrementalItem() { - val incrementalResultsMerger = V0_0IncrementalResultsMerger() - - //language=JSON - val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "hasNext": true - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - } - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "duration": { - "amount": 100, - "unit": "ms" - } - } - ] - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } - } - } - ], - "hasNext": true - } - """.trimIndent() - - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "duration": { - "amount": 25, - "unit": "ms" - } - } - ] - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload4 = """ - { - "incremental": [ - { - "data": null, - "path": [ - "computers", - 0, - "screen" - ], - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], - "label": "fragment:ComputerFields:0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } - } - ] - }, - "hasNext": true, - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ] - } - """.trimIndent() - incrementalResultsMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload5 = """ - { - "incremental": [ - { - "data": { - "isColor": false - }, - "path": [ - "computers", - 1, - "screen" - ], - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ], - "label": "fragment:ComputerFields:0", - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4_5 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480", - "isColor": false - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - ] - }, - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ] - } - """.trimIndent() - incrementalResultsMerger.merge(payload5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - } - - @Test - fun mergeJsonMultipleIncrementalItems() { - val incrementalResultsMerger = V0_0IncrementalResultsMerger() - - //language=JSON - val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "hasNext": true - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload2_3 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - } - }, - { - "data": { - "cpu": "486", - "year": 1996, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } - } - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480" - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "duration": { - "amount": 100, - "unit": "ms" - } - }, - { - "duration": { - "amount": 25, - "unit": "ms" - } - } - ] - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2_3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - - //language=JSON - val payload4_5 = """ - { - "incremental": [ - { - "data": null, - "path": [ - "computers", - 0, - "screen" - ], - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - } - ], - "label": "fragment:ComputerFields:0" - }, - { - "data": { - "isColor": false - }, - "path": [ - "computers", - 1, - "screen" - ], - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ], - "label": "fragment:ComputerFields:0", - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4_5 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "cpu": "386", - "year": 1993, - "screen": { - "isTouch": true, - "resolution": "640x480" - } - }, - { - "id": "Computer2", - "cpu": "486", - "year": 1996, - "screen": { - "isTouch": false, - "resolution": "640x480", - "isColor": false - } - } - ] - }, - "hasNext": true, - "extensions": { - "incremental": [ - { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } - ] - }, - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 - } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - }, - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ] - } - """.trimIndent() - incrementalResultsMerger.merge(payload4_5.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers - ) - } - - @Test - fun emptyPayloads() { - val incrementalResultsMerger = V0_0IncrementalResultsMerger() - - //language=JSON - val payload1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - }, - "hasNext": true - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertFalse(incrementalResultsMerger.isEmptyResponse) - - //language=JSON - val payload2 = """ - { - "hasNext": true - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertTrue(incrementalResultsMerger.isEmptyResponse) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "data": { - "cpu": "386", - "year": 1993, - "screen": { - "resolution": "640x480" - } - }, - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0", - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - } - } - ], - "hasNext": true - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertFalse(incrementalResultsMerger.isEmptyResponse) - - //language=JSON - val payload4 = """ - { - "hasNext": false - } - """.trimIndent() - incrementalResultsMerger.merge(payload4.buffer()) - assertTrue(incrementalResultsMerger.isEmptyResponse) - } -} diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt index 82ce73b0231..d35c71808eb 100644 --- a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_1IncrementalResultsMergerTest.kt @@ -42,47 +42,14 @@ class V0_1IncrementalResultsMergerTest { } ] }, - "pending": [ - { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" - } - ], "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - } - } - """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -97,30 +64,19 @@ class V0_1IncrementalResultsMergerTest { "resolution": "640x480" } }, - "id": "0" - } - ], - "completed": [ - { - "id": "0" - } - ], - "pending": [ - { - "id": "1", "path": [ "computers", - 1 + 0 ], - "label": "query:Query1:0" + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } } ], - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - }, "hasNext": true } """.trimIndent() @@ -146,21 +102,26 @@ class V0_1IncrementalResultsMergerTest { } ] }, + "hasNext": true, "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } + "incremental": [ + { + "duration": { + "amount": 100, + "unit": "ms" + } + } + ] } - } + } """.trimIndent() incrementalResultsMerger.merge(payload2.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0") + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -175,34 +136,23 @@ class V0_1IncrementalResultsMergerTest { "resolution": "640x480" } }, - "id": "1" - } - ], - "completed": [ - { - "id": "1" - } - ], - "pending": [ - { - "id": "2", "path": [ "computers", - 0, - "screen" + 1 ], - "label": "fragment:ComputerFields:0" + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } } ], - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } - }, "hasNext": true } """.trimIndent() + //language=JSON val mergedPayloads_1_2_3 = """ { @@ -228,11 +178,16 @@ class V0_1IncrementalResultsMergerTest { } ] }, + "hasNext": true, "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } + "incremental": [ + { + "duration": { + "amount": 25, + "unit": "ms" + } + } + ] } } """.trimIndent() @@ -240,17 +195,23 @@ class V0_1IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON val payload4 = """ { - "completed": [ + "incremental": [ { - "id": "2", + "data": null, + "path": [ + "computers", + 0, + "screen" + ], "errors": [ { "message": "Cannot resolve isColor", @@ -267,16 +228,6 @@ class V0_1IncrementalResultsMergerTest { "isColor" ] } - ] - } - ], - "pending": [ - { - "id": "3", - "path": [ - "computers", - 1, - "screen" ], "label": "fragment:ComputerFields:0" } @@ -309,6 +260,7 @@ class V0_1IncrementalResultsMergerTest { } ] }, + "hasNext": true, "errors": [ { "message": "Cannot resolve isColor", @@ -325,23 +277,17 @@ class V0_1IncrementalResultsMergerTest { "isColor" ] } - ], - "extensions": { - "duration": { - "amount": 25, - "unit": "ms" - } - } + ] } """.trimIndent() incrementalResultsMerger.merge(payload4.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -352,7 +298,11 @@ class V0_1IncrementalResultsMergerTest { "data": { "isColor": false }, - "id": "3", + "path": [ + "computers", + 1, + "screen" + ], "errors": [ { "message": "Another error", @@ -363,21 +313,17 @@ class V0_1IncrementalResultsMergerTest { } ] } - ] - } - ], - "completed": [ - { - "id": "3" + ], + "label": "fragment:ComputerFields:0", + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } } ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - }, "hasNext": false } """.trimIndent() @@ -407,22 +353,19 @@ class V0_1IncrementalResultsMergerTest { } ] }, - "errors": [ - { - "message": "Cannot resolve isColor", - "locations": [ - { - "line": 12, - "column": 11 + "hasNext": true, + "extensions": { + "incremental": [ + { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" } - ], - "path": [ - "computers", - 0, - "screen", - "isColor" - ] - }, + } + ] + }, + "errors": [ { "message": "Another error", "locations": [ @@ -432,23 +375,18 @@ class V0_1IncrementalResultsMergerTest { } ] } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } + ] } """.trimIndent() incrementalResultsMerger.merge(payload5.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) } @@ -475,56 +413,14 @@ class V0_1IncrementalResultsMergerTest { } ] }, - "pending": [ - { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" - }, - { - "id": "1", - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0" - } - ], "hasNext": true } """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "computers": [ - { - "id": "Computer1", - "screen": { - "isTouch": true - } - }, - { - "id": "Computer2", - "screen": { - "isTouch": false - } - } - ] - } - } - """.trimIndent() incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals(jsonToMap(payload1), incrementalResultsMerger.merged) assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -539,7 +435,17 @@ class V0_1IncrementalResultsMergerTest { "resolution": "640x480" } }, - "id": "0" + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } }, { "data": { @@ -549,43 +455,19 @@ class V0_1IncrementalResultsMergerTest { "resolution": "640x480" } }, - "id": "1" - } - ], - "completed": [ - { - "id": "0" - }, - { - "id": "1" - } - ], - "pending": [ - { - "id": "2", - "path": [ - "computers", - 0, - "screen" - ], - "label": "fragment:ComputerFields:0" - }, - { - "id": "3", "path": [ "computers", - 1, - "screen" + 1 ], - "label": "fragment:ComputerFields:0" + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } } ], - "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } - }, "hasNext": true } """.trimIndent() @@ -614,11 +496,22 @@ class V0_1IncrementalResultsMergerTest { } ] }, + "hasNext": true, "extensions": { - "duration": { - "amount": 100, - "unit": "ms" - } + "incremental": [ + { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + { + "duration": { + "amount": 25, + "unit": "ms" + } + } + ] } } """.trimIndent() @@ -626,10 +519,10 @@ class V0_1IncrementalResultsMergerTest { assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), - DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) //language=JSON @@ -637,26 +530,12 @@ class V0_1IncrementalResultsMergerTest { { "incremental": [ { - "data": { - "isColor": false - }, - "id": "3", - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 - } - ] - } - ] - } - ], - "completed": [ - { - "id": "2", + "data": null, + "path": [ + "computers", + 0, + "screen" + ], "errors": [ { "message": "Cannot resolve isColor", @@ -673,26 +552,46 @@ class V0_1IncrementalResultsMergerTest { "isColor" ] } - ] + ], + "label": "fragment:ComputerFields:0" }, { - "id": "3" - } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - }, - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4_5 = """ - { - "data": { + "data": { + "isColor": false + }, + "path": [ + "computers", + 1, + "screen" + ], + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ], + "label": "fragment:ComputerFields:0", + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { "computers": [ { "id": "Computer1", @@ -715,16 +614,19 @@ class V0_1IncrementalResultsMergerTest { } ] }, - "errors": [ - { - "message": "Another error", - "locations": [ - { - "line": 1, - "column": 1 + "hasNext": true, + "extensions": { + "incremental": [ + { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" } - ] - }, + } + ] + }, + "errors": [ { "message": "Cannot resolve isColor", "locations": [ @@ -739,24 +641,28 @@ class V0_1IncrementalResultsMergerTest { "screen", "isColor" ] + }, + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] } - ], - "extensions": { - "value": 42, - "duration": { - "amount": 130, - "unit": "ms" - } - } + ] } """.trimIndent() incrementalResultsMerger.merge(payload4_5.buffer()) assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) assertEquals( setOf( - DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + incrementalResultsMerger.deferredFragmentIdentifiers ) } @@ -783,24 +689,6 @@ class V0_1IncrementalResultsMergerTest { } ] }, - "pending": [ - { - "id": "0", - "path": [ - "computers", - 0 - ], - "label": "query:Query1:0" - }, - { - "id": "1", - "path": [ - "computers", - 1 - ], - "label": "query:Query1:0" - } - ], "hasNext": true } """.trimIndent() @@ -809,12 +697,13 @@ class V0_1IncrementalResultsMergerTest { //language=JSON val payload2 = """ - { - "hasNext": true - } + { + "hasNext": true + } """.trimIndent() incrementalResultsMerger.merge(payload2.buffer()) assertTrue(incrementalResultsMerger.isEmptyResponse) + //language=JSON val payload3 = """ { @@ -827,7 +716,17 @@ class V0_1IncrementalResultsMergerTest { "resolution": "640x480" } }, - "id": "0" + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0", + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } } ], "hasNext": true @@ -845,1670 +744,4 @@ class V0_1IncrementalResultsMergerTest { incrementalResultsMerger.merge(payload4.buffer()) assertTrue(incrementalResultsMerger.isEmptyResponse) } - - /** - * Example A from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleA() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { - "h": "h", - "i": "i" - } - } - } - }, - "pending": [ - { - "path": [], - "id": "0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { - "h": "h", - "i": "i" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "MyFragment": "Query" - } - }, - { - "id": "0", - "subPath": [ - "f2", - "c", - "f" - ], - "data": { - "j": "j" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "f2": { - "a": "a", - "b": "b", - "c": { - "d": "d", - "e": "e", - "f": { - "h": "h", - "i": "i", - "j": "j" - } - } - }, - "MyFragment": "Query" - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example A2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleA2() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I" - } - } - } - }, - "pending": [ - { - "id": "0", - "path": [], - "label": "D1" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "D1"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "subPath": [ - "f2", - "c", - "f" - ], - "data": { - "j": "J", - "k": "K" - } - } - ], - "pending": [ - { - "id": "1", - "path": [ - "f2", - "c", - "f" - ], - "label": "D2" - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I", - "j": "J", - "k": "K" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "l": "L", - "m": "M" - } - } - ], - "completed": [ - { - "id": "1" - } - ], - "hasNext": false - } - """.trimIndent() - - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "f2": { - "a": "A", - "b": "B", - "c": { - "d": "D", - "e": "E", - "f": { - "h": "H", - "i": "I", - "j": "J", - "k": "K", - "l": "L", - "m": "M" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example B1 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleB1() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } - } - } - }, - "pending": [ - { - "path": [], - "id": "0", - "label": "Blue" - }, - { - "path": [ - "a", - "b" - ], - "id": "1", - "label": "Red" - } - ], - "hasNext": true - } - """.trimIndent() - - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - }, - { - "id": "1", - "data": { - "e": { - "f": "f" - } - } - } - ], - "completed": [ - { - "id": "1" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example B2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleB2() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } - } - } - }, - "pending": [ - { - "path": [], - "id": "0", - "label": "Blue" - }, - { - "path": [ - "a", - "b" - ], - "id": "1", - "label": "Red" - } - ], - "hasNext": true - } - """.trimIndent() - - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "Blue"), - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } - }, - { - "id": "1", - "data": { - "e": { - "f": "f" - } - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - } - } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - } - ], - "completed": [ - { - "id": "1" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "a": { - "b": { - "c": { - "d": "d" - }, - "e": { - "f": "f" - }, - "potentiallySlowFieldA": "potentiallySlowFieldA" - } - }, - "g": { - "h": "h" - }, - "potentiallySlowFieldB": "potentiallySlowFieldB" - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example D from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleD() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "me": {} - }, - "pending": [ - { - "path": [], - "id": "0" - }, - { - "path": [ - "me" - ], - "id": "1" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "me": {} - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), - DeferredFragmentIdentifier(path = listOf("me"), label = null), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "list": [ - { - "item": {} - }, - { - "item": {} - }, - { - "item": {} - } - ] - } - }, - { - "id": "1", - "subPath": [ - "list", - 0, - "item" - ], - "data": { - "id": "1" - } - }, - { - "id": "1", - "subPath": [ - "list", - 1, - "item" - ], - "data": { - "id": "2" - } - }, - { - "id": "1", - "subPath": [ - "list", - 2, - "item" - ], - "data": { - "id": "3" - } - } - ], - "completed": [ - { - "id": "1" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "list": [ - { - "item": { - "id": "1" - } - }, - { - "item": { - "id": "2" - } - }, - { - "item": { - "id": "3" - } - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = null), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "id": "0", - "subPath": [ - "me", - "list", - 0, - "item" - ], - "data": { - "value": "Foo" - } - }, - { - "id": "0", - "subPath": [ - "me", - "list", - 1, - "item" - ], - "data": { - "value": "Bar" - } - }, - { - "id": "0", - "subPath": [ - "me", - "list", - 2, - "item" - ], - "data": { - "value": "Baz" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "list": [ - { - "item": { - "id": "1", - "value": "Foo" - } - }, - { - "item": { - "id": "2", - "value": "Bar" - } - }, - { - "item": { - "id": "3", - "value": "Baz" - } - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example F from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleF() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "me": {} - }, - "pending": [ - { - "id": "0", - "path": [ - "me" - ], - "label": "B" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "me": {} - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "a": "A", - "b": "B" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "a": "A", - "b": "B" - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example G from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleG() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [ - { - "name": "My Project" - } - ] - } - }, - "pending": [ - { - "id": "0", - "path": [ - "me" - ], - "label": "Billing" - }, - { - "id": "1", - "path": [ - "me" - ], - "label": "Prev" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [ - { - "name": "My Project" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "Billing"), - DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "tier": "BRONZE", - "renewalDate": "2023-03-20", - "latestInvoiceTotal": "${'$'}12.34" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [ - { - "name": "My Project" - } - ], - "tier": "BRONZE", - "renewalDate": "2023-03-20", - "latestInvoiceTotal": "${'$'}12.34" - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "incremental": [ - { - "id": "1", - "data": { - "previousInvoices": [ - { - "name": "My Invoice" - } - ] - } - } - ], - "completed": [ - { - "id": "1" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "id": 1, - "avatarUrl": "http://…", - "projects": [ - { - "name": "My Project" - } - ], - "tier": "BRONZE", - "renewalDate": "2023-03-20", - "latestInvoiceTotal": "${'$'}12.34", - "previousInvoices": [ - { - "name": "My Invoice" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example H from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) - */ - @Test - fun june2023ExampleH() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "me": {} - }, - "pending": [ - { - "id": "0", - "path": [], - "label": "A" - }, - { - "id": "1", - "path": [ - "me" - ], - "label": "B" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "me": {} - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf(), label = "A"), - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "0", - "subPath": [ - "me" - ], - "data": { - "foo": { - "bar": {} - } - } - }, - { - "id": "0", - "subPath": [ - "me", - "foo", - "bar" - ], - "data": { - "baz": "BAZ" - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "me": { - "foo": { - "bar": { - "baz": "BAZ" - } - } - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "completed": [ - { - "id": "1", - "errors": [ - { - "message": "Cannot return null for non-nullable field Bar.qux.", - "locations": [ - { - "line": 1, - "column": 1 - } - ], - "path": [ - "foo", - "bar", - "qux" - ] - } - ] - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "me": { - "foo": { - "bar": { - "baz": "BAZ" - } - } - } - }, - "errors": [ - { - "message": "Cannot return null for non-nullable field Bar.qux.", - "locations": [ - { - "line": 1, - "column": 1 - } - ], - "path": [ - "foo", - "bar", - "qux" - ] - } - ] - } - """.trimIndent() - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("me"), label = "B"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example I from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) - */ - @Test - fun july2025ExampleI() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "person": { - "name": "Luke Skywalker", - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - } - ] - } - }, - "pending": [ - { - "id": "0", - "path": [ - "person" - ], - "label": "homeWorldDefer" - }, - { - "id": "1", - "path": [ - "person", - "films" - ], - "label": "filmsStream" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "person": { - "name": "Luke Skywalker", - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "1", - "items": [ - { - "title": "Return of the Jedi" - } - ] - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "person": { - "name": "Luke Skywalker", - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - }, - { - "title": "Return of the Jedi" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "completed": [ - { - "id": "1" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "person": { - "name": "Luke Skywalker", - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - }, - { - "title": "Return of the Jedi" - } - ] - } - } - } - """.trimIndent() - - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload4 = """ - { - "incremental": [ - { - "id": "0", - "data": { - "homeworld": { - "name": "Tatooine" - } - } - } - ], - "completed": [ - { - "id": "0" - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3_4 = """ - { - "data": { - "person": { - "name": "Luke Skywalker", - "homeworld": { - "name": "Tatooine" - }, - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - }, - { - "title": "Return of the Jedi" - } - ] - } - } - } - """.trimIndent() - - incrementalResultsMerger.merge(payload4.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) - assertEquals( - setOf(), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } - - /** - * Example J from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) - */ - @Test - fun july2025ExampleJ() { - val incrementalResultsMerger = V0_1IncrementalResultsMerger() - //language=JSON - val payload1 = """ - { - "data": { - "person": { - "films": [ - { - "title": "A New Hope" - } - ] - } - }, - "pending": [ - { - "id": "1", - "path": [ - "person", - "films" - ], - "label": "filmsStream" - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1 = """ - { - "data": { - "person": { - "films": [ - { - "title": "A New Hope" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload1.buffer()) - assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload2 = """ - { - "incremental": [ - { - "id": "1", - "items": [ - { - "title": "The Empire Strikes Back" - } - ] - } - ], - "hasNext": true - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2 = """ - { - "data": { - "person": { - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - } - ] - } - } - } - """.trimIndent() - incrementalResultsMerger.merge(payload2.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - - //language=JSON - val payload3 = """ - { - "completed": [ - { - "id": "1", - "errors": [ - { - "message": "Cannot return null for non-nullable field Person.films.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], - "path": [ - "person", - "films" - ] - } - ] - } - ], - "hasNext": false - } - """.trimIndent() - //language=JSON - val mergedPayloads_1_2_3 = """ - { - "data": { - "person": { - "films": [ - { - "title": "A New Hope" - }, - { - "title": "The Empire Strikes Back" - } - ] - } - }, - "errors": [ - { - "message": "Cannot return null for non-nullable field Person.films.", - "locations": [ - { - "line": 2, - "column": 3 - } - ], - "path": [ - "person", - "films" - ] - } - ] - } - """.trimIndent() - - incrementalResultsMerger.merge(payload3.buffer()) - assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) - assertEquals( - setOf( - DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), - ), - incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() - ) - } -} - -private fun Set.nonPending(): Set { - return filter { it !== DeferredFragmentIdentifier.Pending }.toSet() } diff --git a/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_2IncrementalResultsMergerTest.kt b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_2IncrementalResultsMergerTest.kt new file mode 100644 index 00000000000..3f2b26e7e3f --- /dev/null +++ b/libraries/apollo-runtime/src/commonTest/kotlin/test/defer/V0_2IncrementalResultsMergerTest.kt @@ -0,0 +1,2514 @@ +@file:OptIn(ApolloInternal::class) + +package test.defer + +import com.apollographql.apollo.annotations.ApolloInternal +import com.apollographql.apollo.api.DeferredFragmentIdentifier +import com.apollographql.apollo.api.json.BufferedSourceJsonReader +import com.apollographql.apollo.api.json.readAny +import com.apollographql.apollo.internal.incremental.V0_2IncrementalResultsMerger +import okio.Buffer +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +private fun String.buffer() = Buffer().writeUtf8(this) + +@Suppress("UNCHECKED_CAST") +private fun jsonToMap(json: String): Map = BufferedSourceJsonReader(json.buffer()).readAny() as Map + +class V0_2IncrementalResultsMergerTest { + @Test + fun mergeJsonSingleIncrementalItem() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0") + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + } + ], + "completed": [ + { + "id": "0" + } + ], + "pending": [ + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0") + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "id": "1" + } + ], + "completed": [ + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", + "path": [ + "computers", + 0, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload4 = """ + { + "completed": [ + { + "id": "2", + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ] + } + ], + "pending": [ + { + "id": "3", + "path": [ + "computers", + 1, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ], + "extensions": { + "duration": { + "amount": 25, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload5 = """ + { + "incremental": [ + { + "data": { + "isColor": false + }, + "id": "3", + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ] + } + ], + "completed": [ + { + "id": "3" + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } + } + ] + }, + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + }, + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + @Test + fun mergeJsonMultipleIncrementalItems() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + }, + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0), label = "query:Query1:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1), label = "query:Query1:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2_3 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + }, + { + "data": { + "cpu": "486", + "year": 1996, + "screen": { + "resolution": "640x480" + } + }, + "id": "1" + } + ], + "completed": [ + { + "id": "0" + }, + { + "id": "1" + } + ], + "pending": [ + { + "id": "2", + "path": [ + "computers", + 0, + "screen" + ], + "label": "fragment:ComputerFields:0" + }, + { + "id": "3", + "path": [ + "computers", + 1, + "screen" + ], + "label": "fragment:ComputerFields:0" + } + ], + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + }, + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480" + } + } + ] + }, + "extensions": { + "duration": { + "amount": 100, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2_3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + DeferredFragmentIdentifier(path = listOf("computers", 1, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload4_5 = """ + { + "incremental": [ + { + "data": { + "isColor": false + }, + "id": "3", + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + } + ] + } + ], + "completed": [ + { + "id": "2", + "errors": [ + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ] + }, + { + "id": "3" + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + }, + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4_5 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "cpu": "386", + "year": 1993, + "screen": { + "isTouch": true, + "resolution": "640x480" + } + }, + { + "id": "Computer2", + "cpu": "486", + "year": 1996, + "screen": { + "isTouch": false, + "resolution": "640x480", + "isColor": false + } + } + ] + }, + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 1, + "column": 1 + } + ] + }, + { + "message": "Cannot resolve isColor", + "locations": [ + { + "line": 12, + "column": 11 + } + ], + "path": [ + "computers", + 0, + "screen", + "isColor" + ] + } + ], + "extensions": { + "value": 42, + "duration": { + "amount": 130, + "unit": "ms" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload4_5.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4_5), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("computers", 0, "screen"), label = "fragment:ComputerFields:0"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + @Test + fun emptyPayloads() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + + //language=JSON + val payload1 = """ + { + "data": { + "computers": [ + { + "id": "Computer1", + "screen": { + "isTouch": true + } + }, + { + "id": "Computer2", + "screen": { + "isTouch": false + } + } + ] + }, + "pending": [ + { + "id": "0", + "path": [ + "computers", + 0 + ], + "label": "query:Query1:0" + }, + { + "id": "1", + "path": [ + "computers", + 1 + ], + "label": "query:Query1:0" + } + ], + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) + + //language=JSON + val payload2 = """ + { + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "data": { + "cpu": "386", + "year": 1993, + "screen": { + "resolution": "640x480" + } + }, + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertFalse(incrementalResultsMerger.isEmptyResponse) + + //language=JSON + val payload4 = """ + { + "hasNext": false + } + """.trimIndent() + incrementalResultsMerger.merge(payload4.buffer()) + assertTrue(incrementalResultsMerger.isEmptyResponse) + } + + /** + * Example A from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleA() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i" + } + } + } + }, + "pending": [ + { + "path": [], + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "MyFragment": "Query" + } + }, + { + "id": "0", + "subPath": [ + "f2", + "c", + "f" + ], + "data": { + "j": "j" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "f2": { + "a": "a", + "b": "b", + "c": { + "d": "d", + "e": "e", + "f": { + "h": "h", + "i": "i", + "j": "j" + } + } + }, + "MyFragment": "Query" + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example A2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleA2() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I" + } + } + } + }, + "pending": [ + { + "id": "0", + "path": [], + "label": "D1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "D1"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "subPath": [ + "f2", + "c", + "f" + ], + "data": { + "j": "J", + "k": "K" + } + } + ], + "pending": [ + { + "id": "1", + "path": [ + "f2", + "c", + "f" + ], + "label": "D2" + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("f2", "c", "f"), label = "D2"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "1", + "data": { + "l": "L", + "m": "M" + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } + """.trimIndent() + + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "f2": { + "a": "A", + "b": "B", + "c": { + "d": "D", + "e": "E", + "f": { + "h": "H", + "i": "I", + "j": "J", + "k": "K", + "l": "L", + "m": "M" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example B1 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleB1() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + }, + "pending": [ + { + "path": [], + "id": "0", + "label": "Blue" + }, + { + "path": [ + "a", + "b" + ], + "id": "1", + "label": "Red" + } + ], + "hasNext": true + } + """.trimIndent() + + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "data": { + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + { + "id": "1", + "data": { + "e": { + "f": "f" + } + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example B2 from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleB2() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + }, + "pending": [ + { + "path": [], + "id": "0", + "label": "Blue" + }, + { + "path": [ + "a", + "b" + ], + "id": "1", + "label": "Red" + } + ], + "hasNext": true + } + """.trimIndent() + + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "Blue"), + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + }, + { + "id": "1", + "data": { + "e": { + "f": "f" + } + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + } + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("a", "b"), label = "Red"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "1", + "data": { + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "a": { + "b": { + "c": { + "d": "d" + }, + "e": { + "f": "f" + }, + "potentiallySlowFieldA": "potentiallySlowFieldA" + } + }, + "g": { + "h": "h" + }, + "potentiallySlowFieldB": "potentiallySlowFieldB" + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example D from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleD() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": {} + }, + "pending": [ + { + "path": [], + "id": "0" + }, + { + "path": [ + "me" + ], + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + DeferredFragmentIdentifier(path = listOf("me"), label = null), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "data": { + "list": [ + { + "item": {} + }, + { + "item": {} + }, + { + "item": {} + } + ] + } + }, + { + "id": "1", + "subPath": [ + "list", + 0, + "item" + ], + "data": { + "id": "1" + } + }, + { + "id": "1", + "subPath": [ + "list", + 1, + "item" + ], + "data": { + "id": "2" + } + }, + { + "id": "1", + "subPath": [ + "list", + 2, + "item" + ], + "data": { + "id": "3" + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "list": [ + { + "item": { + "id": "1" + } + }, + { + "item": { + "id": "2" + } + }, + { + "item": { + "id": "3" + } + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = null), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "0", + "subPath": [ + "me", + "list", + 0, + "item" + ], + "data": { + "value": "Foo" + } + }, + { + "id": "0", + "subPath": [ + "me", + "list", + 1, + "item" + ], + "data": { + "value": "Bar" + } + }, + { + "id": "0", + "subPath": [ + "me", + "list", + 2, + "item" + ], + "data": { + "value": "Baz" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "list": [ + { + "item": { + "id": "1", + "value": "Foo" + } + }, + { + "item": { + "id": "2", + "value": "Bar" + } + }, + { + "item": { + "id": "3", + "value": "Baz" + } + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example F from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleF() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": {} + }, + "pending": [ + { + "id": "0", + "path": [ + "me" + ], + "label": "B" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "a": "A", + "b": "B" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "a": "A", + "b": "B" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example G from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleG() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ] + } + }, + "pending": [ + { + "id": "0", + "path": [ + "me" + ], + "label": "Billing" + }, + { + "id": "1", + "path": [ + "me" + ], + "label": "Prev" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "Billing"), + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34" + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "Prev"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "incremental": [ + { + "id": "1", + "data": { + "previousInvoices": [ + { + "name": "My Invoice" + } + ] + } + } + ], + "completed": [ + { + "id": "1" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "id": 1, + "avatarUrl": "http://…", + "projects": [ + { + "name": "My Project" + } + ], + "tier": "BRONZE", + "renewalDate": "2023-03-20", + "latestInvoiceTotal": "${'$'}12.34", + "previousInvoices": [ + { + "name": "My Invoice" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example H from https://github.com/graphql/defer-stream-wg/discussions/69 (Dec 13 2024 version) + */ + @Test + fun june2023ExampleH() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "me": {} + }, + "pending": [ + { + "id": "0", + "path": [], + "label": "A" + }, + { + "id": "1", + "path": [ + "me" + ], + "label": "B" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "me": {} + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf(), label = "A"), + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "0", + "subPath": [ + "me" + ], + "data": { + "foo": { + "bar": {} + } + } + }, + { + "id": "0", + "subPath": [ + "me", + "foo", + "bar" + ], + "data": { + "baz": "BAZ" + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" + } + } + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1", + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": [ + "foo", + "bar", + "qux" + ] + } + ] + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "me": { + "foo": { + "bar": { + "baz": "BAZ" + } + } + } + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field Bar.qux.", + "locations": [ + { + "line": 1, + "column": 1 + } + ], + "path": [ + "foo", + "bar", + "qux" + ] + } + ] + } + """.trimIndent() + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("me"), label = "B"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example I from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleI() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + }, + "pending": [ + { + "id": "0", + "path": [ + "person" + ], + "label": "homeWorldDefer" + }, + { + "id": "1", + "path": [ + "person", + "films" + ], + "label": "filmsStream" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "items": [ + { + "title": "Return of the Jedi" + } + ] + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } + } + } + """.trimIndent() + + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person"), label = "homeWorldDefer"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload4 = """ + { + "incremental": [ + { + "id": "0", + "data": { + "homeworld": { + "name": "Tatooine" + } + } + } + ], + "completed": [ + { + "id": "0" + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3_4 = """ + { + "data": { + "person": { + "name": "Luke Skywalker", + "homeworld": { + "name": "Tatooine" + }, + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + }, + { + "title": "Return of the Jedi" + } + ] + } + } + } + """.trimIndent() + + incrementalResultsMerger.merge(payload4.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3_4), incrementalResultsMerger.merged) + assertEquals( + setOf(), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } + + /** + * Example J from https://github.com/graphql/defer-stream-wg/discussions/69 (Jul 18 2025 version) + */ + @Test + fun july2025ExampleJ() { + val incrementalResultsMerger = V0_2IncrementalResultsMerger() + //language=JSON + val payload1 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + } + ] + } + }, + "pending": [ + { + "id": "1", + "path": [ + "person", + "films" + ], + "label": "filmsStream" + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload1.buffer()) + assertEquals(jsonToMap(mergedPayloads_1), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload2 = """ + { + "incremental": [ + { + "id": "1", + "items": [ + { + "title": "The Empire Strikes Back" + } + ] + } + ], + "hasNext": true + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + } + } + """.trimIndent() + incrementalResultsMerger.merge(payload2.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + + //language=JSON + val payload3 = """ + { + "completed": [ + { + "id": "1", + "errors": [ + { + "message": "Cannot return null for non-nullable field Person.films.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "person", + "films" + ] + } + ] + } + ], + "hasNext": false + } + """.trimIndent() + //language=JSON + val mergedPayloads_1_2_3 = """ + { + "data": { + "person": { + "films": [ + { + "title": "A New Hope" + }, + { + "title": "The Empire Strikes Back" + } + ] + } + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field Person.films.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "person", + "films" + ] + } + ] + } + """.trimIndent() + + incrementalResultsMerger.merge(payload3.buffer()) + assertEquals(jsonToMap(mergedPayloads_1_2_3), incrementalResultsMerger.merged) + assertEquals( + setOf( + DeferredFragmentIdentifier(path = listOf("person", "films"), label = "filmsStream"), + ), + incrementalResultsMerger.deferredFragmentIdentifiers.nonPending() + ) + } +} + +private fun Set.nonPending(): Set { + return filter { it !== DeferredFragmentIdentifier.Pending }.toSet() +} diff --git a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch index 74a83c2afd1..8d7bcbf35d8 100644 --- a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch +++ b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@apollo/server/dist/esm/ApolloServer.js b/node_modules/@apollo/server/dist/esm/ApolloServer.js -index 7665490..1e8bb12 100644 +index 7665490..233a304 100644 --- a/node_modules/@apollo/server/dist/esm/ApolloServer.js +++ b/node_modules/@apollo/server/dist/esm/ApolloServer.js @@ -631,7 +631,7 @@ export const MEDIA_TYPES = { @@ -7,12 +7,12 @@ index 7665490..1e8bb12 100644 APPLICATION_GRAPHQL_RESPONSE_JSON: 'application/graphql-response+json; charset=utf-8', MULTIPART_MIXED_NO_DEFER_SPEC: 'multipart/mixed', - MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; deferSpec=20220824', -+ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalSpec=v0.1', ++ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalSpec=v0.2', TEXT_HTML: 'text/html', }; export function chooseContentTypeForSingleResultResponse(head) { diff --git a/node_modules/@apollo/server/dist/esm/runHttpQuery.js b/node_modules/@apollo/server/dist/esm/runHttpQuery.js -index 96ef0ab..f35174b 100644 +index 96ef0ab..d650c7b 100644 --- a/node_modules/@apollo/server/dist/esm/runHttpQuery.js +++ b/node_modules/@apollo/server/dist/esm/runHttpQuery.js @@ -161,9 +161,9 @@ export async function runHttpQuery({ server, httpRequest, contextValue, schemaDe @@ -20,10 +20,10 @@ index 96ef0ab..f35174b 100644 '(@defer or @stream), but the client does not accept multipart/mixed ' + 'HTTP responses. To enable incremental delivery support, add the HTTP ' + - "header 'Accept: multipart/mixed; deferSpec=20220824'.", { extensions: { http: { status: 406 } } }); -+ "header 'Accept: multipart/mixed; incrementalSpec=v0.1'.", { extensions: { http: { status: 406 } } }); ++ "header 'Accept: multipart/mixed; incrementalSpec=v0.2'.", { extensions: { http: { status: 406 } } }); } - graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; deferSpec=20220824'); -+ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalSpec=v0.1'); ++ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalSpec=v0.2'); return { ...graphQLResponse.http, body: { diff --git a/tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt deleted file mode 100644 index 95d9ba77b19..00000000000 --- a/tests/defer/src/commonTest/kotlin/test/DeferV0_0Test.kt +++ /dev/null @@ -1,386 +0,0 @@ -package test - -import com.apollographql.apollo.ApolloClient -import com.apollographql.apollo.api.ApolloResponse -import com.apollographql.apollo.api.Error -import com.apollographql.apollo.autoPersistedQueryInfo -import com.apollographql.apollo.mpp.currentTimeMillis -import com.apollographql.apollo.testing.internal.runTest -import com.apollographql.mockserver.MockServer -import com.apollographql.mockserver.enqueueMultipart -import com.apollographql.mockserver.enqueueString -import com.apollographql.mockserver.enqueueStrings -import com.benasher44.uuid.uuid4 -import defer.SimpleDeferQuery -import defer.WithFragmentSpreadsQuery -import defer.WithInlineFragmentsQuery -import defer.fragment.ComputerFields -import defer.fragment.ScreenFields -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.last -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.launch -import okio.ByteString.Companion.encodeUtf8 -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class DeferV0_0Test { - private lateinit var mockServer: MockServer - private lateinit var apolloClient: ApolloClient - - private suspend fun setUp() { - mockServer = MockServer() - apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) - .build() - } - - private fun tearDown() { - mockServer.close() - } - - @Test - fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", - ) - - val expectedDataList = listOf( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ), - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), - ) - ), - ) - - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) - val actualDataList = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - } - - @Test - fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"b"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"b"}],"hasNext":false}""", - ) - - val expectedDataList = listOf( - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", null), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false)))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), - ) - ), - WithInlineFragmentsQuery.Data( - listOf( - WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, - WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false)))), - WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, - WithInlineFragmentsQuery.Screen("Screen", "800x600", - WithInlineFragmentsQuery.OnScreen(true)))), - ) - ), - ) - - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) - val actualDataList = apolloClient.query(WithInlineFragmentsQuery()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - } - - @Test - fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) { - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", - ) - - val query = WithFragmentSpreadsQuery() - val uuid = uuid4() - - val expectedDataList = listOf( - ApolloResponse.Builder( - query, - uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - )).build(), - - ApolloResponse.Builder( - query, - uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ) - ).build(), - - ApolloResponse.Builder( - query, - uuid, - ) - .data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ) - ) - .errors( - listOf( - Error.Builder(message = "Cannot resolve isColor") - .locations(listOf(Error.Location(1, 119))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() - ) - ) - .build(), - - ApolloResponse.Builder( - query, - uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null))), - ) - ) - ).build(), - - ApolloResponse.Builder( - query, - uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), - ) - ) - ).build(), - ) - - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) - val actualResponseList = apolloClient.query(query).toFlow().toList() - assertResponseListEquals(expectedDataList, actualResponseList) - } - - @Test - fun payloadsAreReceivedIncrementally() = runTest(before = { setUp() }, after = { tearDown() }) { - @Suppress("DEPRECATION") - if (com.apollographql.apollo.testing.platform() == com.apollographql.apollo.testing.Platform.Js) { - // TODO For now chunked is not supported on JS - remove this check when it is - return@runTest - } - val delayMillis = 200L - - val multipartBody = mockServer.enqueueMultipart("application/json") - - val syncChannel = Channel() - val job = launch { - apolloClient.query(WithFragmentSpreadsQuery()).toFlow().collect { - syncChannel.send(Unit) - } - } - - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", - ) - - jsonList.withIndex().forEach { (index, value) -> - multipartBody.enqueueDelay(delayMillis) - multipartBody.enqueuePart(value.encodeUtf8(), index == jsonList.lastIndex) - - val timeBeforeReceive = currentTimeMillis() - syncChannel.receive() - assertTrue(currentTimeMillis() - timeBeforeReceive >= delayMillis) - } - - job.cancel() - } - - @Test - fun emptyPayloadsAreIgnored() = runTest(before = { setUp() }, after = { tearDown() }) { - val jsonWithEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":true}""", - """{"hasNext":false}""", - ) - val jsonWithoutEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", - ) - - val expectedDataList = listOf( - SimpleDeferQuery.Data( - listOf(SimpleDeferQuery.Computer("Computer", "computer1", null)) - ), - SimpleDeferQuery.Data( - listOf(SimpleDeferQuery.Computer("Computer", "computer1", SimpleDeferQuery.OnComputer("386"))) - ), - ) - - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithEmptyPayload) - var actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithoutEmptyPayload) - actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } - assertEquals(expectedDataList, actualDataList) - } - - @Test - fun deferWithApqFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) - .autoPersistedQueries() - .build() - - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", - ) - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) - val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() - assertEquals(true, finalResponse.autoPersistedQueryInfo?.hit) - assertEquals( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), - ) - ), - finalResponse.dataOrThrow() - ) - } - - @Test - fun deferWithApqNotFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = ApolloClient.Builder() - .serverUrl(mockServer.url()) - .autoPersistedQueries() - .build() - - mockServer.enqueueString("""{"errors":[{"message":"PersistedQueryNotFound"}]}""") - val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", - ) - mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) - val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() - assertEquals(false, finalResponse.autoPersistedQueryInfo?.hit) - assertEquals( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false)))), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true)))), - ) - ), - finalResponse.dataOrThrow() - ) - } -} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt index de36ee6613a..1ec111669b0 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_1NormalizedCacheTest.kt @@ -4,7 +4,6 @@ import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error -import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.api.Operation import com.apollographql.apollo.cache.normalized.ApolloStore import com.apollographql.apollo.cache.normalized.FetchPolicy @@ -18,9 +17,7 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.CacheMissException -import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport -import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.testing.internal.runTest import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.assertNoRequest @@ -56,14 +53,7 @@ class DeferV0_1NormalizedCacheTest { private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() - apolloClient = ApolloClient.Builder() - .networkTransport( - HttpNetworkTransport.Builder() - .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) - .build() - ) - .store(store).build() + apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store).build() } private fun tearDown() { @@ -82,8 +72,9 @@ class DeferV0_1NormalizedCacheTest { // Fill the cache by doing a network only request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -95,22 +86,9 @@ class DeferV0_1NormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ) assertEquals(cacheExpected, cacheActual) } @@ -121,8 +99,9 @@ class DeferV0_1NormalizedCacheTest { // Fill the cache by doing a first request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -135,28 +114,16 @@ class DeferV0_1NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -167,8 +134,9 @@ class DeferV0_1NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -180,28 +148,16 @@ class DeferV0_1NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -220,8 +176,9 @@ class DeferV0_1NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.NetworkFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -231,28 +188,16 @@ class DeferV0_1NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -271,8 +216,9 @@ class DeferV0_1NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheAndNetwork).build() val jsonList1 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"isColor":false},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList1) @@ -287,22 +233,21 @@ class DeferV0_1NormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ) - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) val jsonList2 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"0"},{"data":{"isColor":true},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList2) @@ -318,15 +263,13 @@ class DeferV0_1NormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null)) ), WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ) - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null)))) + ), + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true))))) ), ) @@ -338,8 +281,9 @@ class DeferV0_1NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -354,44 +298,37 @@ class DeferV0_1NormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), - ) - ) - ).build(), + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + )).build(), + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + )).build(), ApolloResponse.Builder( query, uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ) + .data( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) ) ) - ).errors( - listOf( - Builder("Error field") - .locations(listOf(Error.Location(3, 35))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() + .errors( + listOf( + Error.Builder(message = "Cannot resolve isColor") + .locations(listOf(Error.Location(1, 119))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) ) - ).build() + .build(), ) assertResponseListEquals(networkExpected, networkActual) @@ -400,7 +337,7 @@ class DeferV0_1NormalizedCacheTest { val exception = apolloClient.query(WithFragmentSpreadsQuery()).execute().exception check(exception is CacheMissException) assertIs(exception.suppressedExceptions.first()) - assertEquals("Object 'computers.0' has no field named 'cpu'", exception.message) + assertEquals("Object 'computers.0.screen' has no field named 'isColor'", exception.message) mockServer.awaitRequest() } @@ -412,26 +349,17 @@ class DeferV0_1NormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ) - ).build(), + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + )).build(), ApolloResponse.Builder( query, uuid, - ).data( - WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ) - ) - ) - ).build(), + ).data(WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + )).build(), ) apolloClient = ApolloClient.Builder() @@ -450,8 +378,7 @@ class DeferV0_1NormalizedCacheTest { emit(ApolloResponse.Builder(requestUuid = uuid, operation = query) .exception(ApolloNetworkException("Network error")) .isLast(true) - .build() as ApolloResponse - ) + .build() as ApolloResponse) } } @@ -477,8 +404,9 @@ class DeferV0_1NormalizedCacheTest { @Test fun mutation() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", - """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val networkActual = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataOrThrow() } @@ -486,27 +414,16 @@ class DeferV0_1NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsMutation.Data( - listOf( - WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null), - WithFragmentSpreadsMutation.Computer("Computer", "Computer2", null), - ) + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsMutation.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ) - ) + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null)))) + ), + WithFragmentSpreadsMutation.Data( + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ), ) assertEquals(networkExpected, networkActual) @@ -516,22 +433,9 @@ class DeferV0_1NormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf( - WithFragmentSpreadsQuery.Computer("Computer", "Computer1", - ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), - WithFragmentSpreadsQuery.Computer("Computer", "Computer2", - ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), - ) + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false))))) ) assertEquals(cacheExpected, cacheActual) } @@ -539,8 +443,9 @@ class DeferV0_1NormalizedCacheTest { @Test fun mutationWithOptimisticDataFails() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", - """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val responses = apolloClient.mutation(WithFragmentSpreadsMutation()).optimisticUpdates( @@ -563,8 +468,8 @@ class DeferV0_1NormalizedCacheTest { return@runTest } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", ) val multipartBody = mockServer.enqueueMultipart("application/json") multipartBody.enqueuePart(jsonList[0].encodeUtf8(), false) diff --git a/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt index 46da4af0484..668317b45a4 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_1Test.kt @@ -3,14 +3,9 @@ package test import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error -import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.autoPersistedQueryInfo import com.apollographql.apollo.mpp.currentTimeMillis -import com.apollographql.apollo.network.IncrementalDeliveryProtocol -import com.apollographql.apollo.network.http.HttpNetworkTransport -import com.apollographql.apollo.testing.Platform import com.apollographql.apollo.testing.internal.runTest -import com.apollographql.apollo.testing.platform import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.enqueueMultipart import com.apollographql.mockserver.enqueueString @@ -37,12 +32,7 @@ class DeferV0_1Test { private suspend fun setUp() { mockServer = MockServer() apolloClient = ApolloClient.Builder() - .networkTransport( - HttpNetworkTransport.Builder() - .serverUrl(mockServer.url()) - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) - .build() - ) + .serverUrl(mockServer.url()) .build() } @@ -53,8 +43,11 @@ class DeferV0_1Test { @Test fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) val expectedDataList = listOf( @@ -64,20 +57,38 @@ class DeferV0_1Test { WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null))), + ) + ), WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", null))), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), ) @@ -90,8 +101,11 @@ class DeferV0_1Test { @Test fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"b"},{"id":"3","path":["computers",1,"screen"],"label":"b"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"b"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"b"}],"hasNext":false}""", ) val expectedDataList = listOf( @@ -101,20 +115,38 @@ class DeferV0_1Test { WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), ) ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", null))), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), + ) + ), WithInlineFragmentsQuery.Data( listOf( WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, WithInlineFragmentsQuery.Screen("Screen", "640x480", - WithInlineFragmentsQuery.OnScreen(false) - ) - ) - ), + WithInlineFragmentsQuery.OnScreen(false)))), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", null))), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", + WithInlineFragmentsQuery.OnScreen(false)))), WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, WithInlineFragmentsQuery.Screen("Screen", "800x600", - WithInlineFragmentsQuery.OnScreen(true) - ) - ) - ), + WithInlineFragmentsQuery.OnScreen(true)))), ) ), ) @@ -127,8 +159,11 @@ class DeferV0_1Test { @Test fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) val query = WithFragmentSpreadsQuery() @@ -143,9 +178,43 @@ class DeferV0_1Test { WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), ) - ) + )).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) ).build(), + ApolloResponse.Builder( + query, + uuid, + ) + .data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ) + .errors( + listOf( + Error.Builder(message = "Cannot resolve isColor") + .locations(listOf(Error.Location(1, 119))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ) + .build(), ApolloResponse.Builder( query, @@ -154,25 +223,27 @@ class DeferV0_1Test { WithFragmentSpreadsQuery.Data( listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null) - ) - ), + ComputerFields.Screen("Screen", "640x480", null))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ComputerFields.Screen("Screen", "800x600", null))), ) ) - ).errors( - listOf( - Builder("Error field") - .locations(listOf(Error.Location(3, 35))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() + ).build(), + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null))), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true)))), + ) ) - ).build() + ).build(), ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -183,7 +254,7 @@ class DeferV0_1Test { @Test fun payloadsAreReceivedIncrementally() = runTest(before = { setUp() }, after = { tearDown() }) { @Suppress("DEPRECATION") - if (platform() == Platform.Js) { + if (com.apollographql.apollo.testing.platform() == com.apollographql.apollo.testing.Platform.Js) { // TODO For now chunked is not supported on JS - remove this check when it is return@runTest } @@ -199,8 +270,11 @@ class DeferV0_1Test { } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) jsonList.withIndex().forEach { (index, value) -> @@ -218,27 +292,21 @@ class DeferV0_1Test { @Test fun emptyPayloadsAreIgnored() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonWithEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":true}""", """{"hasNext":false}""", ) val jsonWithoutEmptyPayload = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"computer1"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", ) val expectedDataList = listOf( SimpleDeferQuery.Data( - listOf( - SimpleDeferQuery.Computer("Computer", "Computer1", null), - SimpleDeferQuery.Computer("Computer", "Computer2", null), - ) + listOf(SimpleDeferQuery.Computer("Computer", "computer1", null)) ), SimpleDeferQuery.Data( - listOf( - SimpleDeferQuery.Computer("Computer", "Computer1", SimpleDeferQuery.OnComputer("386")), - SimpleDeferQuery.Computer("Computer", "Computer2", SimpleDeferQuery.OnComputer("486")), - ) + listOf(SimpleDeferQuery.Computer("Computer", "computer1", SimpleDeferQuery.OnComputer("386"))) ), ) @@ -253,13 +321,17 @@ class DeferV0_1Test { @Test fun deferWithApqFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = apolloClient.newBuilder() + apolloClient = ApolloClient.Builder() + .serverUrl(mockServer.url()) .autoPersistedQueries() .build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() @@ -269,16 +341,10 @@ class DeferV0_1Test { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), finalResponse.dataOrThrow() @@ -287,14 +353,18 @@ class DeferV0_1Test { @Test fun deferWithApqNotFound() = runTest(before = { setUp() }, after = { tearDown() }) { - apolloClient = apolloClient.newBuilder() + apolloClient = ApolloClient.Builder() + .serverUrl(mockServer.url()) .autoPersistedQueries() .build() mockServer.enqueueString("""{"errors":[{"message":"PersistedQueryNotFound"}]}""") val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", - """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", + """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",1]}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":true}""", + """{"incremental": [{"data":{"isColor":true},"path":["computers",1,"screen"],"label":"a"}],"hasNext":false}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() @@ -304,16 +374,10 @@ class DeferV0_1Test { listOf( WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, ComputerFields.Screen("Screen", "640x480", - ScreenFields(false) - ) - ) - ), + ScreenFields(false)))), WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, ComputerFields.Screen("Screen", "800x600", - ScreenFields(true) - ) - ) - ), + ScreenFields(true)))), ) ), finalResponse.dataOrThrow() diff --git a/tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_2NormalizedCacheTest.kt similarity index 52% rename from tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt rename to tests/defer/src/commonTest/kotlin/test/DeferV0_2NormalizedCacheTest.kt index 40bf823c7b9..3cbef80f563 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferV0_0NormalizedCacheTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_2NormalizedCacheTest.kt @@ -1,9 +1,13 @@ +@file:OptIn(ApolloExperimental::class) + package test import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.annotations.ApolloExperimental import com.apollographql.apollo.api.ApolloRequest import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Error.Builder import com.apollographql.apollo.api.Operation import com.apollographql.apollo.cache.normalized.ApolloStore import com.apollographql.apollo.cache.normalized.FetchPolicy @@ -17,7 +21,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException import com.apollographql.apollo.exception.CacheMissException +import com.apollographql.apollo.network.IncrementalDeliveryProtocol import com.apollographql.apollo.network.NetworkTransport +import com.apollographql.apollo.network.http.HttpNetworkTransport import com.apollographql.apollo.testing.internal.runTest import com.apollographql.mockserver.MockServer import com.apollographql.mockserver.assertNoRequest @@ -45,7 +51,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertTrue -class DeferV0_0NormalizedCacheTest { +class DeferV0_2NormalizedCacheTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore @@ -53,7 +59,14 @@ class DeferV0_0NormalizedCacheTest { private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() - apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).store(store).build() + apolloClient = ApolloClient.Builder() + .networkTransport( + HttpNetworkTransport.Builder() + .serverUrl(mockServer.url()) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_2) + .build() + ) + .store(store).build() } private fun tearDown() { @@ -72,9 +85,8 @@ class DeferV0_0NormalizedCacheTest { // Fill the cache by doing a network only request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -86,9 +98,22 @@ class DeferV0_0NormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ) assertEquals(cacheExpected, cacheActual) } @@ -99,9 +124,8 @@ class DeferV0_0NormalizedCacheTest { // Fill the cache by doing a first request val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) apolloClient.query(WithFragmentSpreadsQuery()).fetchPolicy(FetchPolicy.NetworkOnly).toFlow().collect() @@ -114,16 +138,28 @@ class DeferV0_0NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -134,9 +170,8 @@ class DeferV0_0NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -148,16 +183,28 @@ class DeferV0_0NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -176,9 +223,8 @@ class DeferV0_0NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.NetworkFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -188,16 +234,28 @@ class DeferV0_0NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ), ) assertEquals(networkExpected, networkActual) @@ -216,9 +274,8 @@ class DeferV0_0NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheAndNetwork).build() val jsonList1 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"isColor":false},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList1) @@ -233,21 +290,22 @@ class DeferV0_0NormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ) + ) ), ) assertEquals(networkExpected, networkActual) val jsonList2 = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":true},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"0"},{"data":{"isColor":true},"id":"2"}],"completed":[{"id":"0"},{"id":"2"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList2) @@ -263,13 +321,15 @@ class DeferV0_0NormalizedCacheTest { listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null)) ), WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", null)))) - ), - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, - ComputerFields.Screen("Screen", "800x600", - ScreenFields(true))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ) + ) ), ) @@ -281,9 +341,8 @@ class DeferV0_0NormalizedCacheTest { apolloClient = apolloClient.newBuilder().fetchPolicy(FetchPolicy.CacheFirst).build() val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0]}],"hasNext":true}""", - """{"incremental": [{"data":null,"path":["computers",0,"screen"],"label":"b","errors":[{"message":"Cannot resolve isColor","locations":[{"line":1,"column":119}],"path":["computers",0,"screen","isColor"]}]}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) @@ -298,37 +357,44 @@ class DeferV0_0NormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - )).build(), + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ).build(), - ApolloResponse.Builder( - query, - uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - )).build(), ApolloResponse.Builder( query, uuid, - ) - .data( - WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - ) - ) - .errors( + ).data( + WithFragmentSpreadsQuery.Data( listOf( - Error.Builder(message = "Cannot resolve isColor") - .locations(listOf(Error.Location(1, 119))) - .path(listOf("computers", 0, "screen", "isColor")) - .build() + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), ) ) - .build(), + ).errors( + listOf( + Builder("Error field") + .locations(listOf(Error.Location(3, 35))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ).build() ) assertResponseListEquals(networkExpected, networkActual) @@ -337,7 +403,7 @@ class DeferV0_0NormalizedCacheTest { val exception = apolloClient.query(WithFragmentSpreadsQuery()).execute().exception check(exception is CacheMissException) assertIs(exception.suppressedExceptions.first()) - assertEquals("Object 'computers.0.screen' has no field named 'isColor'", exception.message) + assertEquals("Object 'computers.0' has no field named 'cpu'", exception.message) mockServer.awaitRequest() } @@ -349,17 +415,26 @@ class DeferV0_0NormalizedCacheTest { ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) - )).build(), + ).data( + WithFragmentSpreadsQuery.Data( + listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null)) + ) + ).build(), ApolloResponse.Builder( query, uuid, - ).data(WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) - )).build(), + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ) + ) + ) + ).build(), ) apolloClient = ApolloClient.Builder() @@ -378,7 +453,8 @@ class DeferV0_0NormalizedCacheTest { emit(ApolloResponse.Builder(requestUuid = uuid, operation = query) .exception(ApolloNetworkException("Network error")) .isLast(true) - .build() as ApolloResponse) + .build() as ApolloResponse + ) } } @@ -404,9 +480,8 @@ class DeferV0_0NormalizedCacheTest { @Test fun mutation() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val networkActual = apolloClient.mutation(WithFragmentSpreadsMutation()).toFlow().toList().map { it.dataOrThrow() } @@ -414,16 +489,27 @@ class DeferV0_0NormalizedCacheTest { val networkExpected = listOf( WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null)) - ), - WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", null)))) + listOf( + WithFragmentSpreadsMutation.Computer("Computer", "Computer1", null), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", null), + ) ), WithFragmentSpreadsMutation.Data( - listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf(WithFragmentSpreadsMutation.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsMutation.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ) + ) ), ) assertEquals(networkExpected, networkActual) @@ -433,9 +519,22 @@ class DeferV0_0NormalizedCacheTest { // We get the last/fully formed data val cacheExpected = WithFragmentSpreadsQuery.Data( - listOf(WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, - ComputerFields.Screen("Screen", "640x480", - ScreenFields(false))))) + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", + ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", + ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) ) assertEquals(cacheExpected, cacheActual) } @@ -443,9 +542,8 @@ class DeferV0_0NormalizedCacheTest { @Test fun mutationWithOptimisticDataFails() = runTest(before = { setUp() }, after = { tearDown() }) { val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"path":["computers",0],"label":"c"}],"hasNext":true}""", - """{"incremental": [{"data":{"isColor":false},"path":["computers",0,"screen"],"label":"a"}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0],"label":"c"},{"id":"1","path":["computers",1],"label":"c"}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", ) mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) val responses = apolloClient.mutation(WithFragmentSpreadsMutation()).optimisticUpdates( @@ -468,8 +566,8 @@ class DeferV0_0NormalizedCacheTest { return@runTest } val jsonList = listOf( - """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"}]},"hasNext":true}""", - """{"incremental": [{"data":{"cpu":"386"},"path":["computers",0]}],"hasNext":false}""", + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", ) val multipartBody = mockServer.enqueueMultipart("application/json") multipartBody.enqueuePart(jsonList[0].encodeUtf8(), false) diff --git a/tests/defer/src/commonTest/kotlin/test/DeferV0_2Test.kt b/tests/defer/src/commonTest/kotlin/test/DeferV0_2Test.kt new file mode 100644 index 00000000000..5a2c69a0c7b --- /dev/null +++ b/tests/defer/src/commonTest/kotlin/test/DeferV0_2Test.kt @@ -0,0 +1,325 @@ +@file:OptIn(ApolloExperimental::class) + +package test + +import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.annotations.ApolloExperimental +import com.apollographql.apollo.api.ApolloResponse +import com.apollographql.apollo.api.Error +import com.apollographql.apollo.api.Error.Builder +import com.apollographql.apollo.autoPersistedQueryInfo +import com.apollographql.apollo.mpp.currentTimeMillis +import com.apollographql.apollo.network.IncrementalDeliveryProtocol +import com.apollographql.apollo.network.http.HttpNetworkTransport +import com.apollographql.apollo.testing.Platform +import com.apollographql.apollo.testing.internal.runTest +import com.apollographql.apollo.testing.platform +import com.apollographql.mockserver.MockServer +import com.apollographql.mockserver.enqueueMultipart +import com.apollographql.mockserver.enqueueString +import com.apollographql.mockserver.enqueueStrings +import com.benasher44.uuid.uuid4 +import defer.SimpleDeferQuery +import defer.WithFragmentSpreadsQuery +import defer.WithInlineFragmentsQuery +import defer.fragment.ComputerFields +import defer.fragment.ScreenFields +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.last +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import okio.ByteString.Companion.encodeUtf8 +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class DeferV0_2Test { + private lateinit var mockServer: MockServer + private lateinit var apolloClient: ApolloClient + + private suspend fun setUp() { + mockServer = MockServer() + apolloClient = ApolloClient.Builder() + .networkTransport( + HttpNetworkTransport.Builder() + .serverUrl(mockServer.url()) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_2) + .build() + ) + .build() + } + + private fun tearDown() { + mockServer.close() + } + + @Test + fun deferWithFragmentSpreads() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + ) + + val expectedDataList = listOf( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualDataList = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithInlineFragments() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"b"},{"id":"3","path":["computers",1,"screen"],"label":"b"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + ) + + val expectedDataList = listOf( + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", null), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", null), + ) + ), + WithInlineFragmentsQuery.Data( + listOf( + WithInlineFragmentsQuery.Computer("Computer", "Computer1", WithInlineFragmentsQuery.OnComputer("386", 1993, + WithInlineFragmentsQuery.Screen("Screen", "640x480", + WithInlineFragmentsQuery.OnScreen(false) + ) + ) + ), + WithInlineFragmentsQuery.Computer("Computer", "Computer2", WithInlineFragmentsQuery.OnComputer("486", 1996, + WithInlineFragmentsQuery.Screen("Screen", "800x600", + WithInlineFragmentsQuery.OnScreen(true) + ) + ) + ), + ) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualDataList = apolloClient.query(WithInlineFragmentsQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithFragmentSpreadsAndError() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2","errors":[{"message":"Error field","locations":[{"line":3,"column":35}],"path":["computers",0,"screen","isColor"]}]},{"id":"3"}]}""", + ) + + val query = WithFragmentSpreadsQuery() + val uuid = uuid4() + + val expectedDataList = listOf( + ApolloResponse.Builder( + query, + uuid, + ).data(WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", null), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", null), + ) + ) + ).build(), + + + ApolloResponse.Builder( + query, + uuid, + ).data( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", null) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ) + ).errors( + listOf( + Builder("Error field") + .locations(listOf(Error.Location(3, 35))) + .path(listOf("computers", 0, "screen", "isColor")) + .build() + ) + ).build() + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val actualResponseList = apolloClient.query(query).toFlow().toList() + assertResponseListEquals(expectedDataList, actualResponseList) + } + + @Test + fun payloadsAreReceivedIncrementally() = runTest(before = { setUp() }, after = { tearDown() }) { + @Suppress("DEPRECATION") + if (platform() == Platform.Js) { + // TODO For now chunked is not supported on JS - remove this check when it is + return@runTest + } + val delayMillis = 200L + + val multipartBody = mockServer.enqueueMultipart("application/json") + + val syncChannel = Channel() + val job = launch { + apolloClient.query(WithFragmentSpreadsQuery()).toFlow().collect { + syncChannel.send(Unit) + } + } + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + ) + + jsonList.withIndex().forEach { (index, value) -> + multipartBody.enqueueDelay(delayMillis) + multipartBody.enqueuePart(value.encodeUtf8(), index == jsonList.lastIndex) + + val timeBeforeReceive = currentTimeMillis() + syncChannel.receive() + assertTrue(currentTimeMillis() - timeBeforeReceive >= delayMillis) + } + + job.cancel() + } + + @Test + fun emptyPayloadsAreIgnored() = runTest(before = { setUp() }, after = { tearDown() }) { + val jsonWithEmptyPayload = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", + """{"hasNext":false}""", + ) + val jsonWithoutEmptyPayload = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":false,"incremental":[{"data":{"cpu":"386"},"id":"0"},{"data":{"cpu":"486"},"id":"1"}],"completed":[{"id":"0"},{"id":"1"}]}""", + ) + + val expectedDataList = listOf( + SimpleDeferQuery.Data( + listOf( + SimpleDeferQuery.Computer("Computer", "Computer1", null), + SimpleDeferQuery.Computer("Computer", "Computer2", null), + ) + ), + SimpleDeferQuery.Data( + listOf( + SimpleDeferQuery.Computer("Computer", "Computer1", SimpleDeferQuery.OnComputer("386")), + SimpleDeferQuery.Computer("Computer", "Computer2", SimpleDeferQuery.OnComputer("486")), + ) + ), + ) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithEmptyPayload) + var actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonWithoutEmptyPayload) + actualDataList = apolloClient.query(SimpleDeferQuery()).toFlow().toList().map { it.dataOrThrow() } + assertEquals(expectedDataList, actualDataList) + } + + @Test + fun deferWithApqFound() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder() + .autoPersistedQueries() + .build() + + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() + assertEquals(true, finalResponse.autoPersistedQueryInfo?.hit) + assertEquals( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + finalResponse.dataOrThrow() + ) + } + + @Test + fun deferWithApqNotFound() = runTest(before = { setUp() }, after = { tearDown() }) { + apolloClient = apolloClient.newBuilder() + .autoPersistedQueries() + .build() + + mockServer.enqueueString("""{"errors":[{"message":"PersistedQueryNotFound"}]}""") + val jsonList = listOf( + """{"data":{"computers":[{"__typename":"Computer","id":"Computer1"},{"__typename":"Computer","id":"Computer2"}]},"pending":[{"id":"0","path":["computers",0]},{"id":"1","path":["computers",1]}],"hasNext":true}""", + """{"hasNext":true,"pending":[{"id":"2","path":["computers",0,"screen"],"label":"a"},{"id":"3","path":["computers",1,"screen"],"label":"a"}],"incremental":[{"data":{"cpu":"386","year":1993,"screen":{"__typename":"Screen","resolution":"640x480"}},"id":"0"},{"data":{"cpu":"486","year":1996,"screen":{"__typename":"Screen","resolution":"800x600"}},"id":"1"},{"data":{"isColor":false},"id":"2"},{"data":{"isColor":true},"id":"3"}],"completed":[{"id":"0"},{"id":"1"},{"id":"2"},{"id":"3"}]}""", + ) + mockServer.enqueueMultipart("application/json").enqueueStrings(jsonList) + val finalResponse = apolloClient.query(WithFragmentSpreadsQuery()).toFlow().last() + assertEquals(false, finalResponse.autoPersistedQueryInfo?.hit) + assertEquals( + WithFragmentSpreadsQuery.Data( + listOf( + WithFragmentSpreadsQuery.Computer("Computer", "Computer1", ComputerFields("386", 1993, + ComputerFields.Screen("Screen", "640x480", + ScreenFields(false) + ) + ) + ), + WithFragmentSpreadsQuery.Computer("Computer", "Computer2", ComputerFields("486", 1996, + ComputerFields.Screen("Screen", "800x600", + ScreenFields(true) + ) + ) + ), + ) + ), + finalResponse.dataOrThrow() + ) + } +} diff --git a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt index 301cac4654b..43d60d745a7 100644 --- a/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt +++ b/tests/defer/src/commonTest/kotlin/test/DeferWithApolloServerTest.kt @@ -1,6 +1,9 @@ +@file:OptIn(ApolloExperimental::class) + package test import com.apollographql.apollo.ApolloClient +import com.apollographql.apollo.annotations.ApolloExperimental import com.apollographql.apollo.api.ApolloResponse import com.apollographql.apollo.api.Error import com.apollographql.apollo.api.Optional @@ -49,7 +52,7 @@ class DeferWithApolloServerTest { .networkTransport( HttpNetworkTransport.Builder() .serverUrl("http://127.0.0.1:4000/") - .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_1) + .incrementalDeliveryProtocol(IncrementalDeliveryProtocol.V0_2) .build() ) .build() From 8ede1944e4afeda1fdd193d2a3ea252525c56147 Mon Sep 17 00:00:00 2001 From: BoD Date: Mon, 20 Oct 2025 11:57:50 +0200 Subject: [PATCH 31/38] Update API dump --- libraries/apollo-runtime/api/android/apollo-runtime.api | 2 +- libraries/apollo-runtime/api/apollo-runtime.klib.api | 2 +- libraries/apollo-runtime/api/jvm/apollo-runtime.api | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/apollo-runtime/api/android/apollo-runtime.api b/libraries/apollo-runtime/api/android/apollo-runtime.api index a89e12a6cf1..45f6d588113 100644 --- a/libraries/apollo-runtime/api/android/apollo-runtime.api +++ b/libraries/apollo-runtime/api/android/apollo-runtime.api @@ -216,8 +216,8 @@ public final class com/apollographql/apollo/interceptor/RetryOnErrorInterceptorK } public final class com/apollographql/apollo/network/IncrementalDeliveryProtocol : java/lang/Enum { - public static final field V0_0 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static final field V0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field V0_2 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun values ()[Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; diff --git a/libraries/apollo-runtime/api/apollo-runtime.klib.api b/libraries/apollo-runtime/api/apollo-runtime.klib.api index 17e4a69c655..39f43c44926 100644 --- a/libraries/apollo-runtime/api/apollo-runtime.klib.api +++ b/libraries/apollo-runtime/api/apollo-runtime.klib.api @@ -22,8 +22,8 @@ final enum class com.apollographql.apollo.network.ws/WsFrameType : kotlin/Enum { // com.apollographql.apollo.network/IncrementalDeliveryProtocol|null[0] - enum entry V0_0 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.V0_0|null[0] enum entry V0_1 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.V0_1|null[0] + enum entry V0_2 // com.apollographql.apollo.network/IncrementalDeliveryProtocol.V0_2|null[0] final val entries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries|#static{}entries[0] final fun (): kotlin.enums/EnumEntries // com.apollographql.apollo.network/IncrementalDeliveryProtocol.entries.|#static(){}[0] diff --git a/libraries/apollo-runtime/api/jvm/apollo-runtime.api b/libraries/apollo-runtime/api/jvm/apollo-runtime.api index 4eed1e8386e..f9fd0f52941 100644 --- a/libraries/apollo-runtime/api/jvm/apollo-runtime.api +++ b/libraries/apollo-runtime/api/jvm/apollo-runtime.api @@ -216,8 +216,8 @@ public final class com/apollographql/apollo/interceptor/RetryOnErrorInterceptorK } public final class com/apollographql/apollo/network/IncrementalDeliveryProtocol : java/lang/Enum { - public static final field V0_0 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static final field V0_1 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; + public static final field V0_2 Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; public static fun values ()[Lcom/apollographql/apollo/network/IncrementalDeliveryProtocol; From 7c769ef810244eca4a1aef1cd9545d1711202081 Mon Sep 17 00:00:00 2001 From: BoD Date: Tue, 28 Oct 2025 10:19:09 +0100 Subject: [PATCH 32/38] Use Apollo Server 5.1.0-rc.0 instead of 4.11.2 + ad hoc patch. --- tests/defer/apollo-server/package.json | 5 +- .../patches/@apollo+server+4.11.2.patch | 53 ------------------- 2 files changed, 2 insertions(+), 56 deletions(-) delete mode 100644 tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch diff --git a/tests/defer/apollo-server/package.json b/tests/defer/apollo-server/package.json index 57416828baf..8bc02b56c97 100644 --- a/tests/defer/apollo-server/package.json +++ b/tests/defer/apollo-server/package.json @@ -8,9 +8,8 @@ "start": "node computers.js" }, "dependencies": { - "@apollo/server": "4.11.2", - "graphql": "17.0.0-alpha.9", - "patch-package": "^8.0.0" + "@apollo/server": "5.1.0-rc.0", + "graphql": "17.0.0-alpha.9" }, "keywords": [], "author": "", diff --git a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch b/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch deleted file mode 100644 index 8d7bcbf35d8..00000000000 --- a/tests/defer/apollo-server/patches/@apollo+server+4.11.2.patch +++ /dev/null @@ -1,53 +0,0 @@ -diff --git a/node_modules/@apollo/server/dist/esm/ApolloServer.js b/node_modules/@apollo/server/dist/esm/ApolloServer.js -index 7665490..233a304 100644 ---- a/node_modules/@apollo/server/dist/esm/ApolloServer.js -+++ b/node_modules/@apollo/server/dist/esm/ApolloServer.js -@@ -631,7 +631,7 @@ export const MEDIA_TYPES = { - APPLICATION_JSON_GRAPHQL_CALLBACK: 'application/json; callbackSpec=1.0; charset=utf-8', - APPLICATION_GRAPHQL_RESPONSE_JSON: 'application/graphql-response+json; charset=utf-8', - MULTIPART_MIXED_NO_DEFER_SPEC: 'multipart/mixed', -- MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; deferSpec=20220824', -+ MULTIPART_MIXED_EXPERIMENTAL: 'multipart/mixed; incrementalSpec=v0.2', - TEXT_HTML: 'text/html', - }; - export function chooseContentTypeForSingleResultResponse(head) { -diff --git a/node_modules/@apollo/server/dist/esm/runHttpQuery.js b/node_modules/@apollo/server/dist/esm/runHttpQuery.js -index 96ef0ab..d650c7b 100644 ---- a/node_modules/@apollo/server/dist/esm/runHttpQuery.js -+++ b/node_modules/@apollo/server/dist/esm/runHttpQuery.js -@@ -161,9 +161,9 @@ export async function runHttpQuery({ server, httpRequest, contextValue, schemaDe - throw new BadRequestError('Apollo server received an operation that uses incremental delivery ' + - '(@defer or @stream), but the client does not accept multipart/mixed ' + - 'HTTP responses. To enable incremental delivery support, add the HTTP ' + -- "header 'Accept: multipart/mixed; deferSpec=20220824'.", { extensions: { http: { status: 406 } } }); -+ "header 'Accept: multipart/mixed; incrementalSpec=v0.2'.", { extensions: { http: { status: 406 } } }); - } -- graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; deferSpec=20220824'); -+ graphQLResponse.http.headers.set('content-type', 'multipart/mixed; boundary="-"; incrementalSpec=v0.2'); - return { - ...graphQLResponse.http, - body: { -@@ -187,6 +187,7 @@ function orderExecutionResultFields(result) { - } - function orderInitialIncrementalExecutionResultFields(result) { - return { -+ ...result, - hasNext: result.hasNext, - errors: result.errors, - data: result.data, -@@ -196,6 +197,7 @@ function orderInitialIncrementalExecutionResultFields(result) { - } - function orderSubsequentIncrementalExecutionResultFields(result) { - return { -+ ...result, - hasNext: result.hasNext, - incremental: orderIncrementalResultFields(result.incremental), - extensions: result.extensions, -@@ -203,6 +205,7 @@ function orderSubsequentIncrementalExecutionResultFields(result) { - } - function orderIncrementalResultFields(incremental) { - return incremental?.map((i) => ({ -+ ...i, - hasNext: i.hasNext, - errors: i.errors, - path: i.path, From fc55ef9296d849dd72de8a4bf8cdc687ba01834c Mon Sep 17 00:00:00 2001 From: Benoit 'BoD' Lubek Date: Wed, 29 Oct 2025 09:25:33 +0100 Subject: [PATCH 33/38] Update libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt Co-authored-by: Martin Bonnin --- .../apollo/api/http/DefaultHttpRequestComposer.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt index 5d6c20816de..dcce2c56f27 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/http/DefaultHttpRequestComposer.kt @@ -53,6 +53,10 @@ class DefaultHttpRequestComposer( addAll(apolloRequest.httpHeaders) } if (get("accept") == null) { + /** + * This is for backward compatibility reasons only. + * We should encourage users to set the accept headers before calling DefaultHttpRequestComposer + */ add( HttpHeader("accept", if (apolloRequest.operation is Subscription<*>) { From 55b3b2f452db4cc7d8226f7c0f44426e5a64c60e Mon Sep 17 00:00:00 2001 From: Benoit 'BoD' Lubek Date: Wed, 29 Oct 2025 09:25:59 +0100 Subject: [PATCH 34/38] Update libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt Co-authored-by: Martin Bonnin --- .../apollographql/apollo/network/IncrementalDeliveryProtocol.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt index d8240aac561..8c5824c7e2e 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/IncrementalDeliveryProtocol.kt @@ -13,8 +13,6 @@ enum class IncrementalDeliveryProtocol { * version `17.0.0-alpha.2`, also referred to as `20220824`. * * Only `@defer` is supported with this format. - * - * This is the default. */ V0_1, From c391cc8cea8f79a0bcff21a70e82d74142a4c1c3 Mon Sep 17 00:00:00 2001 From: Benoit 'BoD' Lubek Date: Wed, 29 Oct 2025 09:27:03 +0100 Subject: [PATCH 35/38] Update libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt Co-authored-by: Martin Bonnin --- .../kotlin/com/apollographql/apollo/api/BooleanExpression.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index 6bd3cbfe456..c168dc34aa6 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -88,7 +88,7 @@ private fun shouldParseFragment(deferredFragmentIdentifiers: Set Date: Wed, 29 Oct 2025 09:27:19 +0100 Subject: [PATCH 36/38] Update libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt Co-authored-by: Martin Bonnin --- .../kotlin/com/apollographql/apollo/api/BooleanExpression.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt index c168dc34aa6..e09cd70de6a 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt @@ -91,7 +91,7 @@ private fun shouldParseFragment(deferredFragmentIdentifiers: Set Date: Wed, 29 Oct 2025 09:28:32 +0100 Subject: [PATCH 37/38] Use Apollo Server 5.1.0 and fix README and CI job --- .github/workflows/defer-integration-tests.yml | 1 - tests/defer/README.md | 2 +- tests/defer/apollo-server/README.md | 3 +-- tests/defer/apollo-server/package.json | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/defer-integration-tests.yml b/.github/workflows/defer-integration-tests.yml index 16b84fdc9db..3164c35397a 100644 --- a/.github/workflows/defer-integration-tests.yml +++ b/.github/workflows/defer-integration-tests.yml @@ -37,7 +37,6 @@ jobs: - working-directory: tests/defer/apollo-server/ run: | npm install --legacy-peer-deps - npx patch-package APOLLO_PORT=4000 npm start & - env: diff --git a/tests/defer/README.md b/tests/defer/README.md index 4f4ac085620..cca9d4afe9b 100644 --- a/tests/defer/README.md +++ b/tests/defer/README.md @@ -27,5 +27,5 @@ They are enabled only when running from the specific `defer-with-apollo-server-t To run them locally: 1. Install and run the - subgraph: `(cd tests/defer/apollo-server && npm install --legacy-peer-deps && npx patch-package && APOLLO_PORT=4000 npm start)&` + subgraph: `(cd tests/defer/apollo-server && npm install --legacy-peer-deps && APOLLO_PORT=4000 npm start)&` 2. Run the tests: `DEFER_WITH_APOLLO_SERVER_TESTS=true ./gradlew -p tests :defer:allTests` diff --git a/tests/defer/apollo-server/README.md b/tests/defer/apollo-server/README.md index 5111b87d42e..ca694f5f501 100644 --- a/tests/defer/apollo-server/README.md +++ b/tests/defer/apollo-server/README.md @@ -1,4 +1,3 @@ # Test server using Apollo Server, for `@defer` tests -- This uses graphql-js `17.0.0-alpha.9`, which implements the latest draft of the `@defer` incremental format (as of 2025-09-24). -- Apollo Server `4.11.2` needs a patch (in `patches`) to surface this format in the responses. +- This uses Apollo Server `5.1.0` and graphql-js `17.0.0-alpha.9`, which implement the v0.2 incremental format and `@stream`. diff --git a/tests/defer/apollo-server/package.json b/tests/defer/apollo-server/package.json index 8bc02b56c97..a1439877ca6 100644 --- a/tests/defer/apollo-server/package.json +++ b/tests/defer/apollo-server/package.json @@ -8,7 +8,7 @@ "start": "node computers.js" }, "dependencies": { - "@apollo/server": "5.1.0-rc.0", + "@apollo/server": "5.1.0", "graphql": "17.0.0-alpha.9" }, "keywords": [], From a7b879498fe1a743fbb5db3c8c068fc465f304c5 Mon Sep 17 00:00:00 2001 From: BoD Date: Wed, 29 Oct 2025 09:39:38 +0100 Subject: [PATCH 38/38] Fix HttpNetworkTransport.newBuilder --- libraries/apollo-runtime/api/android/apollo-runtime.api | 2 +- libraries/apollo-runtime/api/jvm/apollo-runtime.api | 2 +- .../apollo/network/http/HttpNetworkTransport.kt | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/apollo-runtime/api/android/apollo-runtime.api b/libraries/apollo-runtime/api/android/apollo-runtime.api index 45f6d588113..819995eff1c 100644 --- a/libraries/apollo-runtime/api/android/apollo-runtime.api +++ b/libraries/apollo-runtime/api/android/apollo-runtime.api @@ -334,7 +334,7 @@ public abstract interface class com/apollographql/apollo/network/http/HttpInterc } public final class com/apollographql/apollo/network/http/HttpNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/network/IncrementalDeliveryProtocol;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; public final fun getInterceptors ()Ljava/util/List; diff --git a/libraries/apollo-runtime/api/jvm/apollo-runtime.api b/libraries/apollo-runtime/api/jvm/apollo-runtime.api index f9fd0f52941..2a7eb59c308 100644 --- a/libraries/apollo-runtime/api/jvm/apollo-runtime.api +++ b/libraries/apollo-runtime/api/jvm/apollo-runtime.api @@ -330,7 +330,7 @@ public abstract interface class com/apollographql/apollo/network/http/HttpInterc } public final class com/apollographql/apollo/network/http/HttpNetworkTransport : com/apollographql/apollo/network/NetworkTransport { - public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/internal/incremental/IncrementalDeliveryProtocolImpl;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lcom/apollographql/apollo/api/http/HttpRequestComposer;Lcom/apollographql/apollo/network/http/HttpEngine;Ljava/util/List;ZLcom/apollographql/apollo/network/IncrementalDeliveryProtocol;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun execute (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow; public final fun getInterceptors ()Ljava/util/List; diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt index a10c87e5883..f3fd02b1727 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/http/HttpNetworkTransport.kt @@ -48,9 +48,10 @@ private constructor( private val engine: HttpEngine, val interceptors: List, private val exposeErrorBody: Boolean, - private val incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl, + private val incrementalDeliveryProtocol: IncrementalDeliveryProtocol, ) : NetworkTransport { private val engineInterceptor = EngineInterceptor() + private val incrementalDeliveryProtocolImpl: IncrementalDeliveryProtocolImpl = incrementalDeliveryProtocol.impl override fun execute( request: ApolloRequest, @@ -315,6 +316,7 @@ private constructor( .interceptors(interceptors) .httpRequestComposer(httpRequestComposer) .exposeErrorBody(exposeErrorBody) + .incrementalDeliveryProtocol(incrementalDeliveryProtocol) } /** @@ -406,7 +408,7 @@ private constructor( engine = engine ?: DefaultHttpEngine(), interceptors = interceptors, exposeErrorBody = exposeErrorBody, - incrementalDeliveryProtocolImpl = incrementalDeliveryProtocol.impl, + incrementalDeliveryProtocol = incrementalDeliveryProtocol, ) } }