dd-trace 6.9.0 → 6.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -17
- package/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +150 -3
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +14 -10
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/cucumber.js +26 -11
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +634 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +26 -18
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +540 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +132 -25
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/playwright-reporter.js +99 -0
- package/packages/datadog-instrumentations/src/playwright.js +149 -2
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +180 -20
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +82 -30
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +606 -40
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +279 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +42 -13
- package/packages/datadog-plugin-vitest/src/index.js +14 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +27 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +6 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +24 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +14 -3
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +16 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +163 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +241 -87
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +280 -20
- package/packages/dd-trace/src/llmobs/experiments/index.js +58 -59
- package/packages/dd-trace/src/llmobs/experiments/noop.js +71 -3
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +41 -5
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +5 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +5 -65
- package/packages/dd-trace/src/plugins/util/test.js +93 -14
- package/packages/dd-trace/src/plugins/util/web.js +56 -2
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -1,73 +1,98 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { randomUUID } = require('node:crypto')
|
|
4
4
|
|
|
5
|
-
// Dataset record: { input, expectedOutput?, metadata?, id
|
|
6
|
-
//
|
|
5
|
+
// Dataset record: { input, expectedOutput?, metadata?, id }.
|
|
6
|
+
// IDs are generated locally unless the caller supplies one.
|
|
7
7
|
class DatasetRecord {
|
|
8
8
|
constructor (input, expectedOutput = null, metadata = {}, id = null) {
|
|
9
|
+
if (id != null && (typeof id !== 'string' || id.length === 0)) {
|
|
10
|
+
throw new Error('record id must be a non-empty string')
|
|
11
|
+
}
|
|
9
12
|
this.input = input
|
|
10
13
|
this.expectedOutput = expectedOutput ?? null
|
|
11
14
|
this.metadata = metadata ?? {}
|
|
12
|
-
this.id = id ??
|
|
15
|
+
this.id = id ?? randomUUID()
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
if (Array.isArray(response?.data)) return response.data
|
|
19
|
-
return []
|
|
19
|
+
function versionFromMutationResult (result) {
|
|
20
|
+
return result?.version ?? null
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function
|
|
23
|
-
return
|
|
23
|
+
function serializedRecord (record) {
|
|
24
|
+
return {
|
|
25
|
+
id: record.id,
|
|
26
|
+
input: record.input,
|
|
27
|
+
expected_output: record.expectedOutput ?? null,
|
|
28
|
+
metadata: record.metadata ?? {},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function serializedRecordUpdate (update) {
|
|
33
|
+
const output = { id: update.id }
|
|
34
|
+
if (Object.hasOwn(update, 'input') && update.input !== undefined) output.input = update.input
|
|
35
|
+
if (Object.hasOwn(update, 'expectedOutput') && update.expectedOutput !== undefined) {
|
|
36
|
+
output.expected_output = update.expectedOutput
|
|
37
|
+
}
|
|
38
|
+
if (Object.hasOwn(update, 'metadata') && update.metadata !== undefined) output.metadata = update.metadata
|
|
39
|
+
return output
|
|
24
40
|
}
|
|
25
41
|
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
.map(record => Number(record?.attributes?.valid_from_version ?? record?.attributes?.version))
|
|
29
|
-
.filter(Number.isFinite)
|
|
30
|
-
if (versions.length === 0) return null
|
|
31
|
-
return Math.max(...versions)
|
|
42
|
+
function valuesAreEqual (left, right) {
|
|
43
|
+
return JSON.stringify(left) === JSON.stringify(right)
|
|
32
44
|
}
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
function updateFromInsertedRecord (recordId, record, payload) {
|
|
47
|
+
const update = { id: recordId }
|
|
48
|
+
if (!valuesAreEqual(record.input, payload.input)) update.input = record.input
|
|
49
|
+
if (!valuesAreEqual(record.expectedOutput, payload.expected_output)) {
|
|
50
|
+
update.expectedOutput = record.expectedOutput
|
|
51
|
+
}
|
|
52
|
+
if (!valuesAreEqual(record.metadata, payload.metadata)) update.metadata = record.metadata
|
|
53
|
+
return update
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// A local dataset model that tracks pending changes and pushes them in one batch.
|
|
36
57
|
class Dataset {
|
|
37
58
|
#client
|
|
38
59
|
#name
|
|
39
60
|
#description
|
|
40
61
|
#records
|
|
41
|
-
#
|
|
62
|
+
#recordsById
|
|
63
|
+
#newRecordsById
|
|
64
|
+
#updatedRecordsById
|
|
65
|
+
#deletedRecordIds
|
|
42
66
|
#id
|
|
43
67
|
#projectId
|
|
44
|
-
#pushedCount
|
|
45
68
|
#version
|
|
46
69
|
#latestVersion
|
|
70
|
+
#pushPromise
|
|
47
71
|
|
|
48
72
|
constructor (client, name, description = '') {
|
|
49
73
|
this.#client = client
|
|
50
74
|
this.#name = name
|
|
51
75
|
this.#description = description
|
|
52
76
|
this.#records = []
|
|
53
|
-
this.#
|
|
77
|
+
this.#recordsById = new Map()
|
|
78
|
+
this.#newRecordsById = new Map()
|
|
79
|
+
this.#updatedRecordsById = new Map()
|
|
80
|
+
this.#deletedRecordIds = new Set()
|
|
54
81
|
this.#id = null
|
|
55
82
|
this.#projectId = null
|
|
56
|
-
this.#pushedCount = 0
|
|
57
83
|
this.#version = null
|
|
58
84
|
this.#latestVersion = null
|
|
85
|
+
this.#pushPromise = Promise.resolve()
|
|
59
86
|
}
|
|
60
87
|
|
|
61
88
|
// Build a Dataset that already exists remotely (used by pullDataset).
|
|
62
|
-
static fromExisting (client, name, description, id, projectId, records,
|
|
89
|
+
static fromExisting (client, name, description, id, projectId, records, version, latestVersion) {
|
|
63
90
|
const dataset = new Dataset(client, name, description)
|
|
64
91
|
dataset.#id = id
|
|
65
92
|
dataset.#projectId = projectId
|
|
66
|
-
dataset.#records.push(...records)
|
|
67
|
-
dataset.#recordIds.push(...recordIds)
|
|
68
|
-
dataset.#pushedCount = records.length
|
|
69
93
|
dataset.#version = version ?? null
|
|
70
94
|
dataset.#latestVersion = latestVersion ?? version ?? null
|
|
95
|
+
for (const record of records) dataset.#addExistingRecord(record)
|
|
71
96
|
return dataset
|
|
72
97
|
}
|
|
73
98
|
|
|
@@ -76,7 +101,53 @@ class Dataset {
|
|
|
76
101
|
const record = recordOrInput instanceof DatasetRecord
|
|
77
102
|
? recordOrInput
|
|
78
103
|
: new DatasetRecord(recordOrInput, expectedOutput, metadata)
|
|
79
|
-
this.#
|
|
104
|
+
this.#addRecord(record)
|
|
105
|
+
return this
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Update an existing dataset record. New records are updated in place and are sent with their insert.
|
|
110
|
+
* @param {number} index Dataset record index.
|
|
111
|
+
* @param {{input?: unknown, expectedOutput?: unknown, metadata?: object}} fields Fields to update.
|
|
112
|
+
* @returns {Dataset} This dataset for chaining.
|
|
113
|
+
*/
|
|
114
|
+
update (index, fields) {
|
|
115
|
+
if (fields == null || typeof fields !== 'object' || Array.isArray(fields)) {
|
|
116
|
+
throw new TypeError('record update must be an object')
|
|
117
|
+
}
|
|
118
|
+
const record = this.#recordAt(index)
|
|
119
|
+
const fieldNames = ['input', 'expectedOutput', 'metadata']
|
|
120
|
+
const providedFields = fieldNames.filter(field => Object.hasOwn(fields, field) && fields[field] !== undefined)
|
|
121
|
+
if (providedFields.length === 0) {
|
|
122
|
+
throw new Error('record update must include input, expectedOutput, or metadata')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const field of providedFields) {
|
|
126
|
+
record[field] = field === 'metadata' ? (fields[field] ?? {}) : fields[field]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (this.#newRecordsById.has(record.id)) return this
|
|
130
|
+
|
|
131
|
+
const update = this.#updatedRecordsById.get(record.id) ?? { id: record.id }
|
|
132
|
+
for (const field of providedFields) update[field] = record[field]
|
|
133
|
+
this.#updatedRecordsById.set(record.id, update)
|
|
134
|
+
return this
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Delete a dataset record. New records are removed locally without a backend operation.
|
|
139
|
+
* @param {number} index Dataset record index.
|
|
140
|
+
* @returns {Dataset} This dataset for chaining.
|
|
141
|
+
*/
|
|
142
|
+
delete (index) {
|
|
143
|
+
const record = this.#recordAt(index)
|
|
144
|
+
this.#records.splice(index, 1)
|
|
145
|
+
this.#recordsById.delete(record.id)
|
|
146
|
+
|
|
147
|
+
if (this.#newRecordsById.delete(record.id)) return this
|
|
148
|
+
|
|
149
|
+
this.#updatedRecordsById.delete(record.id)
|
|
150
|
+
this.#deletedRecordIds.add(record.id)
|
|
80
151
|
return this
|
|
81
152
|
}
|
|
82
153
|
|
|
@@ -84,12 +155,16 @@ class Dataset {
|
|
|
84
155
|
return this.#name
|
|
85
156
|
}
|
|
86
157
|
|
|
158
|
+
description () {
|
|
159
|
+
return this.#description
|
|
160
|
+
}
|
|
161
|
+
|
|
87
162
|
records () {
|
|
88
163
|
return [...this.#records]
|
|
89
164
|
}
|
|
90
165
|
|
|
91
166
|
recordIds () {
|
|
92
|
-
return
|
|
167
|
+
return this.#records.map(record => record.id)
|
|
93
168
|
}
|
|
94
169
|
|
|
95
170
|
id () {
|
|
@@ -114,92 +189,171 @@ class Dataset {
|
|
|
114
189
|
return `${this.#client.appBase}/llm/datasets/${this.#id}`
|
|
115
190
|
}
|
|
116
191
|
|
|
117
|
-
// Eagerly create the dataset (if needed) and push
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
192
|
+
// Eagerly create the dataset (if needed) and push all pending changes.
|
|
193
|
+
push () {
|
|
194
|
+
return this.#enqueuePush(async () => {
|
|
195
|
+
const projectId = await this.#client.ensureProjectId()
|
|
196
|
+
return this.#ensureCreatedAndPushed(projectId)
|
|
197
|
+
})
|
|
121
198
|
}
|
|
122
199
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
200
|
+
// Called by Experiment.run() after it has resolved the project id.
|
|
201
|
+
ensureCreatedAndPushed (projectId) {
|
|
202
|
+
return this.#enqueuePush(() => this.#ensureCreatedAndPushed(projectId))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
#enqueuePush (push) {
|
|
206
|
+
const next = this.#pushPromise.then(push, push)
|
|
207
|
+
this.#pushPromise = next.then(() => {}, () => {})
|
|
208
|
+
return next
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async #ensureCreatedAndPushed (projectId) {
|
|
127
212
|
if (this.#id === null) {
|
|
128
213
|
let response
|
|
129
214
|
try {
|
|
130
|
-
response = await this.#client.
|
|
131
|
-
data: { type: 'datasets', attributes: { name: this.#name, description: this.#description } },
|
|
132
|
-
})
|
|
215
|
+
response = await this.#client.createDataset(projectId, { name: this.#name, description: this.#description })
|
|
133
216
|
} catch (err) {
|
|
134
217
|
throw new Error(`Failed to create dataset '${this.#name}': ${err.message}`)
|
|
135
218
|
}
|
|
136
|
-
this.#id = response?.
|
|
219
|
+
this.#id = response?.id() ?? null
|
|
137
220
|
if (this.#id === null) {
|
|
138
221
|
throw new Error(`Failed to create dataset '${this.#name}': backend response is missing dataset id`)
|
|
139
222
|
}
|
|
140
223
|
this.#projectId = projectId
|
|
141
|
-
this.#version = response
|
|
142
|
-
this.#latestVersion = response
|
|
224
|
+
this.#version = response.version() ?? this.#version
|
|
225
|
+
this.#latestVersion = response.latestVersion() ?? this.#latestVersion
|
|
143
226
|
}
|
|
144
227
|
|
|
145
|
-
|
|
228
|
+
const pending = this.#pendingBatch()
|
|
229
|
+
if (pending.totalCount === 0) return { pushedCount: 0, totalCount: 0 }
|
|
230
|
+
|
|
231
|
+
let result
|
|
232
|
+
try {
|
|
233
|
+
result = await this.#client.batchUpdateDatasetRecords(projectId, this.#id, pending.attributes)
|
|
234
|
+
} catch (err) {
|
|
235
|
+
throw new Error(`Failed to push changes to dataset '${this.#name}': ${err.message}`)
|
|
236
|
+
}
|
|
146
237
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
238
|
+
this.#updateVersionFromMutationResult(result)
|
|
239
|
+
this.#clearCommittedChanges(pending)
|
|
240
|
+
return { pushedCount: pending.totalCount, totalCount: pending.totalCount }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#pendingBatch () {
|
|
244
|
+
const insertRecords = []
|
|
245
|
+
const insertPayloads = new Map()
|
|
246
|
+
for (const [recordId, record] of this.#newRecordsById) {
|
|
247
|
+
const payload = serializedRecord(record)
|
|
248
|
+
insertRecords.push(payload)
|
|
249
|
+
insertPayloads.set(recordId, payload)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const updateRecords = []
|
|
253
|
+
const updatePayloads = new Map()
|
|
254
|
+
for (const [recordId, update] of this.#updatedRecordsById) {
|
|
255
|
+
const payload = serializedRecordUpdate(update)
|
|
256
|
+
updateRecords.push(payload)
|
|
257
|
+
updatePayloads.set(recordId, payload)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const deleteRecordIds = [...this.#deletedRecordIds]
|
|
261
|
+
return {
|
|
262
|
+
attributes: {
|
|
263
|
+
insert_records: insertRecords,
|
|
264
|
+
update_records: updateRecords,
|
|
265
|
+
delete_records: deleteRecordIds,
|
|
266
|
+
deduplicate: true,
|
|
267
|
+
create_new_version: true,
|
|
268
|
+
},
|
|
269
|
+
deleteRecordIds,
|
|
270
|
+
insertPayloads,
|
|
271
|
+
updatePayloads,
|
|
272
|
+
totalCount: new Set([
|
|
273
|
+
...insertPayloads.keys(),
|
|
274
|
+
...updatePayloads.keys(),
|
|
275
|
+
...deleteRecordIds,
|
|
276
|
+
]).size,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
#clearCommittedChanges (pending) {
|
|
281
|
+
for (const [recordId, payload] of pending.insertPayloads) {
|
|
282
|
+
const current = this.#newRecordsById.get(recordId)
|
|
283
|
+
if (!current) {
|
|
284
|
+
this.#deletedRecordIds.add(recordId)
|
|
285
|
+
continue
|
|
152
286
|
}
|
|
153
|
-
if (
|
|
154
|
-
|
|
287
|
+
if (valuesAreEqual(serializedRecord(current), payload)) {
|
|
288
|
+
this.#newRecordsById.delete(recordId)
|
|
289
|
+
continue
|
|
155
290
|
}
|
|
156
|
-
|
|
157
|
-
|
|
291
|
+
|
|
292
|
+
this.#newRecordsById.delete(recordId)
|
|
293
|
+
const update = this.#updatedRecordsById.get(recordId) ??
|
|
294
|
+
updateFromInsertedRecord(recordId, current, payload)
|
|
295
|
+
this.#updatedRecordsById.set(recordId, update)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (const [recordId, payload] of pending.updatePayloads) {
|
|
299
|
+
const current = this.#updatedRecordsById.get(recordId)
|
|
300
|
+
if (current && valuesAreEqual(serializedRecordUpdate(current), payload)) {
|
|
301
|
+
this.#updatedRecordsById.delete(recordId)
|
|
158
302
|
}
|
|
159
|
-
|
|
160
|
-
})
|
|
303
|
+
}
|
|
161
304
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
response = await this.#client.request(
|
|
165
|
-
'POST',
|
|
166
|
-
`${API_BASE_PATH}/${projectId}/datasets/${this.#id}/records`,
|
|
167
|
-
{ data: { type: 'datasets', attributes: { records } } }
|
|
168
|
-
)
|
|
169
|
-
} catch (err) {
|
|
170
|
-
throw new Error(`Failed to push records to dataset '${this.#name}': ${err.message}`)
|
|
305
|
+
for (const recordId of pending.deleteRecordIds) {
|
|
306
|
+
if (!this.#recordsById.has(recordId)) this.#deletedRecordIds.delete(recordId)
|
|
171
307
|
}
|
|
308
|
+
}
|
|
172
309
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const created = createdRecordsFromResponse(response)
|
|
177
|
-
const pushedVersion = versionFromCreatedRecords(created)
|
|
178
|
-
if (pushedVersion === null) {
|
|
179
|
-
// The dataset contents changed, but the backend did not report the new
|
|
180
|
-
// version. Avoid pinning later experiments to the pre-append create version.
|
|
181
|
-
this.#version = null
|
|
182
|
-
} else {
|
|
310
|
+
#updateVersionFromMutationResult (result) {
|
|
311
|
+
const pushedVersion = versionFromMutationResult(result)
|
|
312
|
+
if (pushedVersion !== null) {
|
|
183
313
|
this.#version = pushedVersion
|
|
184
|
-
this.#latestVersion =
|
|
314
|
+
this.#latestVersion = pushedVersion
|
|
315
|
+
return
|
|
185
316
|
}
|
|
186
317
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
this.#recordIds.push(recordId)
|
|
318
|
+
const latestVersion = Number(this.#latestVersion)
|
|
319
|
+
if (Number.isFinite(latestVersion)) {
|
|
320
|
+
this.#version = latestVersion + 1
|
|
321
|
+
this.#latestVersion = this.#version
|
|
322
|
+
} else {
|
|
323
|
+
this.#version = null
|
|
195
324
|
}
|
|
196
|
-
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#addRecord (record) {
|
|
328
|
+
if (this.#recordsById.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
|
|
329
|
+
this.#records.push(record)
|
|
330
|
+
this.#recordsById.set(record.id, record)
|
|
331
|
+
this.#deletedRecordIds.delete(record.id)
|
|
332
|
+
this.#newRecordsById.set(record.id, record)
|
|
333
|
+
}
|
|
197
334
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
335
|
+
#addExistingRecord (record) {
|
|
336
|
+
if (record.id === null || record.id === undefined || record.id === '') {
|
|
337
|
+
throw new Error('Dataset records pulled from the backend must have an id')
|
|
338
|
+
}
|
|
339
|
+
if (!(record instanceof DatasetRecord)) {
|
|
340
|
+
record = new DatasetRecord(
|
|
341
|
+
record.input,
|
|
342
|
+
record.expectedOutput,
|
|
343
|
+
record.metadata,
|
|
344
|
+
record.id
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
if (this.#recordsById.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
|
|
348
|
+
this.#records.push(record)
|
|
349
|
+
this.#recordsById.set(record.id, record)
|
|
350
|
+
}
|
|
201
351
|
|
|
202
|
-
|
|
352
|
+
#recordAt (index) {
|
|
353
|
+
if (!Number.isInteger(index) || index < 0 || index >= this.#records.length) {
|
|
354
|
+
throw new RangeError(`Dataset record index ${index} is out of range`)
|
|
355
|
+
}
|
|
356
|
+
return this.#records[index]
|
|
203
357
|
}
|
|
204
358
|
}
|
|
205
359
|
|