queen-mq 1.0.6 → 1.2.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 +110 -4
- package/client-v2/Queen.js +68 -0
- package/client-v2/admin/Admin.js +15 -1
- package/client-v2/buffer/BufferManager.js +18 -6
- package/client-v2/buffer/MessageBuffer.js +17 -1
- package/client-v2/buffer/sinks.js +89 -0
- package/client-v2/builders/QueueBuilder.js +175 -16
- package/client-v2/consumer/ConsumerManager.js +79 -12
- package/client-v2/ephemeral/Ephemeral.js +551 -0
- package/client-v2/index.js +12 -0
- package/client-v2/streams/Stream.js +3 -0
- package/client-v2/streams/runtime/Runner.js +26 -3
- package/client-v2/utils/autopilot.js +168 -0
- package/client-v2/utils/conflation.js +118 -0
- package/client-v2/utils/defaults.js +16 -3
- package/package.json +3 -3
- package/test-v2/autopilot-unit/autopilotWire.test.js +410 -0
- package/test-v2/conflation-unit/conflationWire.test.js +398 -0
- package/test-v2/ephemeral-unit/_planServer.js +73 -0
- package/test-v2/ephemeral-unit/durableSinkPin.test.js +112 -0
- package/test-v2/ephemeral-unit/ephemeralBuffer.test.js +305 -0
- package/test-v2/ephemeral-unit/ephemeralWire.test.js +398 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pop autopilot, client side.
|
|
3
|
+
*
|
|
4
|
+
* The broker owns a controller that sizes a pop from state this client cannot
|
|
5
|
+
* see: how many partitions of the (queue, group) are ready, how old their
|
|
6
|
+
* oldest ready message is, at what rate messages are arriving. Two knobs are
|
|
7
|
+
* under its control — `partitions` (the sweep width) and `batch` (the message
|
|
8
|
+
* budget for the sweep).
|
|
9
|
+
*
|
|
10
|
+
* THE RULE, and it is the only one: an explicit user value is sacred. Autopilot
|
|
11
|
+
* applies ONLY to the knobs the user left unset, and it applies to them one by
|
|
12
|
+
* one. A consumer that pins `partitions(1)` and says nothing about batch keeps
|
|
13
|
+
* its single-partition claim forever and lets the broker size the batch; the
|
|
14
|
+
* pinned dimension is never "adjusted", not even towards a value the controller
|
|
15
|
+
* would consider better.
|
|
16
|
+
*
|
|
17
|
+
* The wire shape follows the conflation precedent (see conflation.js): a client
|
|
18
|
+
* that is not engaging autopilot sends the byte-identical request it sent
|
|
19
|
+
* before this feature existed.
|
|
20
|
+
*
|
|
21
|
+
* autopilot=true emitted ONLY when at least one of the two knobs is
|
|
22
|
+
* being left to the broker. Never as autopilot=false.
|
|
23
|
+
* partitions / batch OMITTED for the dimensions the broker is choosing,
|
|
24
|
+
* sent exactly as before for the ones the user set.
|
|
25
|
+
*
|
|
26
|
+
* WHAT AN OLD BROKER DOES, and why there is no capability check here. A broker
|
|
27
|
+
* older than 1.2 ignores unknown query params: the request succeeds, and the
|
|
28
|
+
* two omitted knobs fall back to the SERVER-side defaults (batch 200,
|
|
29
|
+
* partitions 1) instead of the old client-side ones. That is a sizing
|
|
30
|
+
* difference, not a correctness one — nothing is lost, misordered or delivered
|
|
31
|
+
* twice — so unlike conflation (which silently hands a last-value consumer a
|
|
32
|
+
* whole backlog, hence CONFLATION_UNSUPPORTED) this degrades quietly and on
|
|
33
|
+
* purpose. Callers who need the old numbers against an old broker set them
|
|
34
|
+
* explicitly, or turn autopilot off.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The environment variable that disables pop autopilot for a whole process:
|
|
39
|
+
* QUEEN_SDK_POP_AUTOPILOT=off restores the client-side defaults this SDK
|
|
40
|
+
* applied before autopilot existed, byte for byte. It is read once, in the
|
|
41
|
+
* Queen constructor, so a single deployment can be rolled back without touching
|
|
42
|
+
* code.
|
|
43
|
+
*
|
|
44
|
+
* "off", "false", "0", "no" and "disabled" all disable it (case-insensitive,
|
|
45
|
+
* surrounding space ignored). Every other value, including the empty one,
|
|
46
|
+
* leaves autopilot on.
|
|
47
|
+
*/
|
|
48
|
+
export const ENV_POP_AUTOPILOT = 'QUEEN_SDK_POP_AUTOPILOT'
|
|
49
|
+
|
|
50
|
+
const DISABLING_VALUES = new Set(['off', 'false', '0', 'no', 'disabled'])
|
|
51
|
+
|
|
52
|
+
/** Whether ENV_POP_AUTOPILOT asks for the pre-autopilot behavior. */
|
|
53
|
+
export function popAutopilotDisabledByEnv() {
|
|
54
|
+
// `process` is absent in a browser bundle; there the variable cannot be set
|
|
55
|
+
// and autopilot is simply on.
|
|
56
|
+
const raw = typeof process !== 'undefined' && process.env ? process.env[ENV_POP_AUTOPILOT] : undefined
|
|
57
|
+
return DISABLING_VALUES.has(String(raw ?? '').trim().toLowerCase())
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The batch/partitions/autopilot decision for one pop — the values that travel
|
|
62
|
+
* and the ones that do not.
|
|
63
|
+
*
|
|
64
|
+
* IT EXISTS SO THERE IS EXACTLY ONE COPY OF THE EMISSION RULE. pop() and
|
|
65
|
+
* consume() build their query strings separately (QueueBuilder.pop's inline
|
|
66
|
+
* params vs ConsumerManager#buildParams) and the two have drifted before — the
|
|
67
|
+
* standing comment in QueueBuilder.js is there because of it. A rule with three
|
|
68
|
+
* branches and a per-dimension carve-out is precisely the kind that gets copied
|
|
69
|
+
* wrong, so both builders call this and then only PLACE what it returns; where
|
|
70
|
+
* each key sits in the query string stays with the builder, because the
|
|
71
|
+
* pre-autopilot key order is part of what "byte-identical" means here.
|
|
72
|
+
*
|
|
73
|
+
* @param {object} opts
|
|
74
|
+
* @param {number|null} opts.batch - the USER's batch. null/0/undefined means
|
|
75
|
+
* unset: the dimension the broker gets to choose. Neither builder may
|
|
76
|
+
* substitute a default before calling this.
|
|
77
|
+
* @param {number|null} opts.maxPartitions - the USER's sweep width, same
|
|
78
|
+
* convention.
|
|
79
|
+
* @param {number} opts.fallbackBatch - client-side default applied to an unset
|
|
80
|
+
* batch when autopilot is NOT engaged.
|
|
81
|
+
* @param {boolean} opts.autopilot - the resolved decision for this call.
|
|
82
|
+
* @returns {{autopilot: boolean, batch: string|null, partitions: string|null}}
|
|
83
|
+
* Strings are ready to append; null means "this key does not travel".
|
|
84
|
+
*/
|
|
85
|
+
export function popSizing({ batch, maxPartitions, fallbackBatch, autopilot }) {
|
|
86
|
+
const batchSet = typeof batch === 'number' && batch > 0
|
|
87
|
+
const partitionsSet = typeof maxPartitions === 'number' && maxPartitions > 0
|
|
88
|
+
|
|
89
|
+
// Note the case that looks like an omission and is not: when the user set
|
|
90
|
+
// BOTH knobs there is nothing left for the controller to decide, so
|
|
91
|
+
// autopilot=true is NOT emitted and the request is byte-identical to the one
|
|
92
|
+
// this SDK sent before autopilot existed. Sending the flag anyway would be
|
|
93
|
+
// harmless on the broker and dishonest in a packet capture.
|
|
94
|
+
if (autopilot && !(batchSet && partitionsSet)) {
|
|
95
|
+
return {
|
|
96
|
+
autopilot: true,
|
|
97
|
+
batch: batchSet ? String(batch) : null,
|
|
98
|
+
partitions: partitionsSet ? String(maxPartitions) : null
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
autopilot: false,
|
|
104
|
+
batch: String(batchSet ? batch : fallbackBatch),
|
|
105
|
+
// The legacy gate: partitions travels only above 1, because 1 IS the
|
|
106
|
+
// server-side default and a v4-era client never sent it.
|
|
107
|
+
partitions: partitionsSet && maxPartitions > 1 ? String(maxPartitions) : null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* What the broker chose for one pop, echoed back in the response under
|
|
113
|
+
* "autopilot" when the request engaged autopilot. It is additive: a broker that
|
|
114
|
+
* does not send it, or a pop that never asked, yields null everywhere it is
|
|
115
|
+
* exposed.
|
|
116
|
+
*
|
|
117
|
+
* Reading it is optional — the messages are already sized by it — but it is the
|
|
118
|
+
* only way to see the controller working from the client side, and the only
|
|
119
|
+
* input to the empty-poll pacing below.
|
|
120
|
+
*
|
|
121
|
+
* Unknown keys inside it are ignored, and an unknown-shaped value is treated as
|
|
122
|
+
* absent rather than as an error: this field is the broker telling the client
|
|
123
|
+
* what it did, and a client that refuses to run because a newer broker grew a
|
|
124
|
+
* fourth number would be a self-inflicted outage.
|
|
125
|
+
*
|
|
126
|
+
* @param {object|null} result - parsed pop response (null for a 204)
|
|
127
|
+
* @returns {{partitions: number, batch: number, waitMillis: number}|null}
|
|
128
|
+
*/
|
|
129
|
+
export function parseAutopilotDecision(result) {
|
|
130
|
+
const raw = result && typeof result === 'object' ? result.autopilot : null
|
|
131
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null
|
|
132
|
+
|
|
133
|
+
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0)
|
|
134
|
+
return {
|
|
135
|
+
/** Sweep width the broker used for this pop. */
|
|
136
|
+
partitions: num(raw.partitions),
|
|
137
|
+
/** Message budget the broker used for this pop. */
|
|
138
|
+
batch: num(raw.batch),
|
|
139
|
+
/**
|
|
140
|
+
* The broker's advice on how long to wait before polling again (wire name:
|
|
141
|
+
* waitMs). Present only when the broker has an opinion, and it is advice,
|
|
142
|
+
* not a lease: the consume loop honors it for the sleep it was already
|
|
143
|
+
* taking between empty non-waiting pops, nothing more. 0 = no advice.
|
|
144
|
+
*/
|
|
145
|
+
waitMillis: num(raw.waitMs)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The sleep the consume loop has always taken between two empty pops that are
|
|
151
|
+
* NOT long-polling. A waiting pop already blocks on the broker, so it never
|
|
152
|
+
* reaches here.
|
|
153
|
+
*/
|
|
154
|
+
export const EMPTY_POLL_BACKOFF_MILLIS = 100
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* How long to wait after an empty pop: the broker's advice when it gave one,
|
|
158
|
+
* the historical constant otherwise.
|
|
159
|
+
*
|
|
160
|
+
* The advice is honored as given, without a ceiling of this client's invention.
|
|
161
|
+
* The sleep it feeds is raced against the caller's abort signal by the loop, so
|
|
162
|
+
* even an absurd value cannot outlive a cancellation — which is the only
|
|
163
|
+
* property that has to hold locally.
|
|
164
|
+
*/
|
|
165
|
+
export function emptyPollDelayMillis(decision) {
|
|
166
|
+
if (decision && decision.waitMillis > 0) return decision.waitMillis
|
|
167
|
+
return EMPTY_POLL_BACKOFF_MILLIS
|
|
168
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conflation response contract (PLAN_CONFLATION.md §3.3, §4).
|
|
3
|
+
*
|
|
4
|
+
* Conflation — last-value delivery for a consumer group — is declared as a pop
|
|
5
|
+
* query parameter and answered in the pop RESPONSE. Two response keys carry the
|
|
6
|
+
* whole client-side contract, and both are handled here so that pop() and the
|
|
7
|
+
* consume loop cannot drift apart:
|
|
8
|
+
*
|
|
9
|
+
* "conflation": true the broker actually applied last-value delivery.
|
|
10
|
+
* Rides EVERY conflating response, empty ones
|
|
11
|
+
* included — a conflating pop answers 200 with a
|
|
12
|
+
* body instead of a bodiless 204 precisely so the
|
|
13
|
+
* echo has somewhere to sit.
|
|
14
|
+
* "conflationConflict": true this consumer declared a policy the group does
|
|
15
|
+
* not have. The STORED group setting wins (§3.3);
|
|
16
|
+
* the consumer keeps working and warns once.
|
|
17
|
+
*
|
|
18
|
+
* DEGRADE LOUDLY (§4). No SDK negotiates a version with the broker. A 1.1.0
|
|
19
|
+
* client against an older broker sends `conflation=true`, the broker ignores the
|
|
20
|
+
* unknown query parameter, and the consumer silently processes the entire
|
|
21
|
+
* backlog message by message — the exact silent failure the feature must not
|
|
22
|
+
* have. So: requested conflation + no echo => raise, on the FIRST response,
|
|
23
|
+
* before a single message is handled. `null` (an old broker's 204 on an empty
|
|
24
|
+
* pop) is the most important case to raise on, not the least: it is what an idle
|
|
25
|
+
* queue looks like, and it is the first thing a fresh consumer sees.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import * as logger from './logger.js'
|
|
29
|
+
|
|
30
|
+
/** `error.code` on the degrade-loudly error, so callers can branch without string-matching. */
|
|
31
|
+
export const CONFLATION_UNSUPPORTED = 'conflation_unsupported'
|
|
32
|
+
|
|
33
|
+
/** The message the plan fixes for every SDK (§4). Keep it identical across clients. */
|
|
34
|
+
export const CONFLATION_UNSUPPORTED_MESSAGE =
|
|
35
|
+
'conflation was requested but this broker did not apply it — requires broker >= 1.1.0'
|
|
36
|
+
|
|
37
|
+
// Once per (queue, group) PER PROCESS, per §3.3: a mismatched fleet during a
|
|
38
|
+
// rolling deploy would otherwise emit one warning per pop, forever.
|
|
39
|
+
const warnedConflicts = new Set()
|
|
40
|
+
|
|
41
|
+
// A (queue, group) whose conflict the broker has ALREADY told us about. Proof
|
|
42
|
+
// that this broker understands the parameter, so a later response without the
|
|
43
|
+
// echo for that same pair is the same known conflict and not an old broker.
|
|
44
|
+
//
|
|
45
|
+
// It is belt-and-braces now rather than the load-bearing part it once was: a
|
|
46
|
+
// 1.1.0 broker keeps the 200 and the body whenever the answer has anything to
|
|
47
|
+
// say about conflation (`pop_status`), so an EMPTY conflicting pop carries
|
|
48
|
+
// `conflationConflict` too and the check above catches it on the first round
|
|
49
|
+
// trip. Keeping the memo costs nothing and still covers a first contact whose
|
|
50
|
+
// answer somehow arrives without either key.
|
|
51
|
+
const knownConflicts = new Set()
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Identity of the thing a policy hangs on: the queue (or namespace/task pop
|
|
55
|
+
* target) and the consumer group. Deliberately NOT the affinity key, which
|
|
56
|
+
* includes the partition — the policy is per (queue, group).
|
|
57
|
+
*/
|
|
58
|
+
export function conflationKey({ queue, namespace, task, group }) {
|
|
59
|
+
const target = queue || `${namespace || '*'}/${task || '*'}`
|
|
60
|
+
return `${target}|${group || '__QUEUE_MODE__'}`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Inspect one pop response on behalf of a consumer that ASKED for conflation.
|
|
65
|
+
* Call it for every response, including empty ones — that is where the old
|
|
66
|
+
* broker shows itself first.
|
|
67
|
+
*
|
|
68
|
+
* @param {object|null} result - parsed pop response (null for a 204)
|
|
69
|
+
* @param {object} ctx - { queue, namespace, task, group }
|
|
70
|
+
* @throws {Error} with `.code === CONFLATION_UNSUPPORTED` when the broker did
|
|
71
|
+
* not apply conflation and did not explain why.
|
|
72
|
+
*/
|
|
73
|
+
export function checkConflationResponse(result, ctx) {
|
|
74
|
+
const key = conflationKey(ctx)
|
|
75
|
+
|
|
76
|
+
if (result && result.conflationConflict === true) {
|
|
77
|
+
knownConflicts.add(key)
|
|
78
|
+
if (!warnedConflicts.has(key)) {
|
|
79
|
+
warnedConflicts.add(key)
|
|
80
|
+
logger.warn('Conflation.conflict', {
|
|
81
|
+
queue: ctx.queue || null,
|
|
82
|
+
namespace: ctx.namespace || null,
|
|
83
|
+
task: ctx.task || null,
|
|
84
|
+
group: ctx.group || null,
|
|
85
|
+
message:
|
|
86
|
+
'this consumer declared conflation but the consumer group is registered without it — ' +
|
|
87
|
+
'the STORED group setting wins and this consumer is receiving normal batches. ' +
|
|
88
|
+
'Delete/recreate the group to change its policy.'
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (result && result.conflation === true) return
|
|
95
|
+
|
|
96
|
+
// Pop maintenance: the broker refused the pop before it reached the claim
|
|
97
|
+
// path, so there is no policy to echo and nothing to conclude from the
|
|
98
|
+
// absence of one. Reading it as "old broker" would stop every conflating
|
|
99
|
+
// consumer in the fleet the moment an operator pauses pops.
|
|
100
|
+
if (result && result.paused === true) return
|
|
101
|
+
|
|
102
|
+
// The group is known to disagree with us; this response is that same
|
|
103
|
+
// conflict, seen through a body that could not carry the flag.
|
|
104
|
+
if (knownConflicts.has(key)) return
|
|
105
|
+
|
|
106
|
+
const error = new Error(CONFLATION_UNSUPPORTED_MESSAGE)
|
|
107
|
+
error.code = CONFLATION_UNSUPPORTED
|
|
108
|
+
throw error
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Test hook: the warn-once and known-conflict registries are process-wide by
|
|
113
|
+
* design, which makes them sticky across tests in one runner process.
|
|
114
|
+
*/
|
|
115
|
+
export function resetConflationWarnings() {
|
|
116
|
+
warnedConflicts.clear()
|
|
117
|
+
knownConflicts.clear()
|
|
118
|
+
}
|
|
@@ -51,9 +51,14 @@ export const QUEUE_DEFAULTS = {
|
|
|
51
51
|
encryptionEnabled: false // No encryption by default
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// batch and maxPartitions here are the AUTOPILOT-OFF defaults. With autopilot on
|
|
55
|
+
// (the default) a knob the caller never set is not defaulted at all -- it is
|
|
56
|
+
// omitted from the pop so the broker sizes it (see utils/autopilot.js). These
|
|
57
|
+
// values are what comes back with QueueBuilder.autopilot(false), or with
|
|
58
|
+
// QUEEN_SDK_POP_AUTOPILOT=off for a whole process.
|
|
54
59
|
export const CONSUME_DEFAULTS = {
|
|
55
60
|
concurrency: 1, // Single worker
|
|
56
|
-
batch: 1, // One message at a time
|
|
61
|
+
batch: 1, // One message at a time (autopilot off only)
|
|
57
62
|
autoAck: true, // Client-side auto-ack (NOT sent to server)
|
|
58
63
|
wait: true, // Long polling enabled
|
|
59
64
|
timeoutMillis: 30000, // 30 seconds long poll timeout
|
|
@@ -63,11 +68,19 @@ export const CONSUME_DEFAULTS = {
|
|
|
63
68
|
renewLeaseIntervalMillis: null, // Auto-renewal interval when enabled
|
|
64
69
|
subscriptionMode: null, // No subscription mode (standard queue mode)
|
|
65
70
|
subscriptionFrom: null, // No subscription start point
|
|
66
|
-
maxPartitions: 1
|
|
71
|
+
maxPartitions: 1, // v4 multi-partition pop cap (autopilot off only)
|
|
72
|
+
// Last-value delivery for this consumer group (PLAN_CONFLATION §1.1): a pop
|
|
73
|
+
// of a partition delivers only the NEWEST visible message and commits past
|
|
74
|
+
// the ones it skipped. Off by default, and only ever SENT when true, so a
|
|
75
|
+
// client that never touches it is byte-identical on the wire. Requires
|
|
76
|
+
// broker >= 1.1.0 — an older one ignores the parameter, which the SDK
|
|
77
|
+
// detects from the missing response echo and raises on (§4).
|
|
78
|
+
conflation: false
|
|
67
79
|
}
|
|
68
80
|
|
|
81
|
+
// As in CONSUME_DEFAULTS, batch is the autopilot-OFF default.
|
|
69
82
|
export const POP_DEFAULTS = {
|
|
70
|
-
batch: 1, // One message
|
|
83
|
+
batch: 1, // One message (autopilot off only)
|
|
71
84
|
wait: false, // No long polling (immediate return)
|
|
72
85
|
timeoutMillis: 30000, // 30 seconds if wait=true
|
|
73
86
|
autoAck: false // Server-side auto-ack (false = manual ack required)
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "queen-mq",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Partitioned message queue on PostgreSQL — broker client + fluent streaming SDK (windows, joins, gates) in one package",
|
|
6
6
|
"main": "client-v2/index.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "node --test test-v2/streams-unit/configHash.test.js test-v2/streams-unit/operators.test.js test-v2/streams-unit/cycle.test.js test-v2/streams-unit/eventTime.test.js test-v2/streams-unit/ack.test.js test-v2/http-unit/retry429.test.js test-v2/http-unit/hostHeader.test.js test-v2/kv-unit/kvWire.test.js test-v2/kv-unit/timerWire.test.js test-v2/kv-unit/txnWire.test.js test-v2/buffer-unit/buffer.test.js && node test-v2/run.js human",
|
|
9
|
-
"test:unit": "node --test test-v2/streams-unit/configHash.test.js test-v2/streams-unit/operators.test.js test-v2/streams-unit/cycle.test.js test-v2/streams-unit/eventTime.test.js test-v2/streams-unit/ack.test.js test-v2/http-unit/retry429.test.js test-v2/http-unit/hostHeader.test.js test-v2/kv-unit/kvWire.test.js test-v2/kv-unit/timerWire.test.js test-v2/kv-unit/txnWire.test.js test-v2/buffer-unit/buffer.test.js",
|
|
8
|
+
"test": "node --test test-v2/streams-unit/configHash.test.js test-v2/streams-unit/operators.test.js test-v2/streams-unit/cycle.test.js test-v2/streams-unit/eventTime.test.js test-v2/streams-unit/ack.test.js test-v2/http-unit/retry429.test.js test-v2/http-unit/hostHeader.test.js test-v2/kv-unit/kvWire.test.js test-v2/kv-unit/timerWire.test.js test-v2/kv-unit/txnWire.test.js test-v2/buffer-unit/buffer.test.js test-v2/conflation-unit/conflationWire.test.js test-v2/autopilot-unit/autopilotWire.test.js test-v2/ephemeral-unit/ephemeralWire.test.js test-v2/ephemeral-unit/ephemeralBuffer.test.js test-v2/ephemeral-unit/durableSinkPin.test.js && node test-v2/run.js human",
|
|
9
|
+
"test:unit": "node --test test-v2/streams-unit/configHash.test.js test-v2/streams-unit/operators.test.js test-v2/streams-unit/cycle.test.js test-v2/streams-unit/eventTime.test.js test-v2/streams-unit/ack.test.js test-v2/http-unit/retry429.test.js test-v2/http-unit/hostHeader.test.js test-v2/kv-unit/kvWire.test.js test-v2/kv-unit/timerWire.test.js test-v2/kv-unit/txnWire.test.js test-v2/buffer-unit/buffer.test.js test-v2/conflation-unit/conflationWire.test.js test-v2/autopilot-unit/autopilotWire.test.js test-v2/ephemeral-unit/ephemeralWire.test.js test-v2/ephemeral-unit/ephemeralBuffer.test.js test-v2/ephemeral-unit/durableSinkPin.test.js",
|
|
10
10
|
"test:unit:e2e": "node --test test-v2/streams-unit/e2e.test.js",
|
|
11
11
|
"test:integration": "node test-v2/run.js human",
|
|
12
12
|
"test:streams": "node test-v2/run.js stream",
|