Skip to content

Commit 6d0bd1e

Browse files
authored
build: Release (#10354)
2 parents d14374b + d01675b commit 6d0bd1e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+27467
-18737
lines changed

.github/workflows/ci-performance.yml

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
env:
6969
NODE_ENV: production
7070
run: |
71+
set -o pipefail
7172
echo "Running baseline benchmarks..."
7273
if [ ! -f "benchmark/performance.js" ]; then
7374
echo "⚠️ Benchmark script not found - this is expected for new features"
@@ -76,17 +77,9 @@ jobs:
7677
echo "Baseline: N/A (no benchmark script)" > baseline-output.txt
7778
exit 0
7879
fi
79-
taskset -c 0 npm run benchmark > baseline-output.txt 2>&1 || npm run benchmark > baseline-output.txt 2>&1 || true
80-
echo "Benchmark command completed with exit code: $?"
81-
echo "Output file size: $(wc -c < baseline-output.txt) bytes"
82-
echo "--- Begin baseline-output.txt ---"
83-
cat baseline-output.txt
84-
echo "--- End baseline-output.txt ---"
80+
taskset -c 0 npm run benchmark 2>&1 | tee baseline-output.txt || npm run benchmark 2>&1 | tee baseline-output.txt || true
8581
# Extract JSON from output (everything between first [ and last ])
8682
sed -n '/^\[/,/^\]/p' baseline-output.txt > baseline.json || echo '[]' > baseline.json
87-
echo "Extracted JSON size: $(wc -c < baseline.json) bytes"
88-
echo "Baseline benchmark results:"
89-
cat baseline.json
9083
continue-on-error: true
9184

9285
- name: Save baseline results to temp location
@@ -133,18 +126,11 @@ jobs:
133126
env:
134127
NODE_ENV: production
135128
run: |
129+
set -o pipefail
136130
echo "Running PR benchmarks..."
137-
taskset -c 0 npm run benchmark > pr-output.txt 2>&1 || npm run benchmark > pr-output.txt 2>&1 || true
138-
echo "Benchmark command completed with exit code: $?"
139-
echo "Output file size: $(wc -c < pr-output.txt) bytes"
140-
echo "--- Begin pr-output.txt ---"
141-
cat pr-output.txt
142-
echo "--- End pr-output.txt ---"
131+
taskset -c 0 npm run benchmark 2>&1 | tee pr-output.txt || npm run benchmark 2>&1 | tee pr-output.txt || true
143132
# Extract JSON from output (everything between first [ and last ])
144133
sed -n '/^\[/,/^\]/p' pr-output.txt > pr.json || echo '[]' > pr.json
145-
echo "Extracted JSON size: $(wc -c < pr.json) bytes"
146-
echo "PR benchmark results:"
147-
cat pr.json
148134
continue-on-error: true
149135

150136
- name: Upload PR results

.github/workflows/release-automated.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
persist-credentials: false
1818
- uses: actions/setup-node@v4
1919
with:
20-
node-version: 20
20+
node-version: 22
2121
registry-url: https://registry.npmjs.org/
2222
- name: Cache Node.js modules
2323
uses: actions/cache@v4

DEPRECATIONS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ The following is a list of deprecations, according to the [Deprecation Policy](h
2424
| DEPPS18 | Config option `requestComplexity` limits enabled by default | [#10207](https://github.com/parse-community/parse-server/pull/10207) | 9.6.0 (2026) | 10.0.0 (2027) | deprecated | - |
2525
| DEPPS19 | Remove config option `enableProductPurchaseLegacyApi` | [#10228](https://github.com/parse-community/parse-server/pull/10228) | 9.6.0 (2026) | 10.0.0 (2027) | deprecated | - |
2626
| DEPPS20 | Remove config option `allowExpiredAuthDataToken` | | 9.6.0 (2026) | 10.0.0 (2027) | deprecated | - |
27+
| DEPPS21 | Config option `protectedFieldsOwnerExempt` defaults to `false` | | 9.6.0 (2026) | 10.0.0 (2027) | deprecated | - |
28+
| DEPPS22 | Config option `protectedFieldsTriggerExempt` defaults to `true` | | 9.6.0 (2026) | 10.0.0 (2027) | deprecated | - |
29+
| DEPPS23 | Config option `protectedFieldsSaveResponseExempt` defaults to `false` | | 9.7.0 (2026) | 10.0.0 (2027) | deprecated | - |
2730

2831
[i_deprecation]: ## "The version and date of the deprecation."
2932
[i_change]: ## "The version and date of the planned change."

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ COPY package*.json ./
1616
# Copy src to have config files for install
1717
COPY . .
1818

19+
# Increase npm network timeout and retries for slow platforms (e.g. arm64 via QEMU)
20+
ENV npm_config_fetch_retries=5
21+
ENV npm_config_fetch_retry_mintimeout=60000
22+
ENV npm_config_fetch_retry_maxtimeout=300000
23+
1924
# Install without scripts
2025
RUN npm ci --omit=dev --ignore-scripts \
2126
# Copy production node_modules aside for later

benchmark/performance.js

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ let core;
3030
// Logging helpers
3131
const logInfo = message => core.info(message);
3232
const logError = message => core.error(message);
33+
const logGroup = title => core.startGroup(title);
34+
const logGroupEnd = () => core.endGroup();
3335

3436
/**
3537
* Initialize Parse Server for benchmarking
@@ -199,9 +201,10 @@ async function measureOperation({ name, operation, iterations, skipWarmup = fals
199201

200202
/**
201203
* Measure GC pressure for an async operation over multiple iterations.
202-
* Tracks garbage collection duration per operation using PerformanceObserver.
203-
* Larger transient allocations (e.g., from unbounded cursor batch sizes) cause
204-
* more frequent and longer GC pauses, which this metric directly captures.
204+
* Tracks total garbage collection time per operation using PerformanceObserver.
205+
* Using total GC time (sum of all pauses) rather than max single pause provides
206+
* much more stable metrics — it eliminates the variance from V8 choosing to do
207+
* one long pause vs. many short pauses for the same amount of GC work.
205208
* @param {Object} options Measurement options.
206209
* @param {string} options.name Name of the operation being measured.
207210
* @param {Function} options.operation Async function to measure.
@@ -236,31 +239,34 @@ async function measureMemoryOperation({ name, operation, iterations, skipWarmup
236239
global.gc();
237240
}
238241

239-
// Track GC events during this iteration; measure the longest single GC pause,
240-
// which reflects the production impact of large transient allocations
241-
let maxGcPause = 0;
242+
// Track GC events during this iteration; sum all GC pause durations to
243+
// measure total GC work, which is stable regardless of whether V8 chooses
244+
// one long pause or many short pauses
245+
let totalGcTime = 0;
242246
const obs = new PerformanceObserver((list) => {
243247
for (const entry of list.getEntries()) {
244-
if (entry.duration > maxGcPause) {
245-
maxGcPause = entry.duration;
246-
}
248+
totalGcTime += entry.duration;
247249
}
248250
});
249251
obs.observe({ type: 'gc', buffered: false });
250252

251253
await operation();
252254

255+
// Force GC after the operation to flush pending GC work into this
256+
// iteration's measurement, preventing cross-iteration contamination
257+
if (typeof global.gc === 'function') {
258+
global.gc();
259+
}
260+
253261
// Flush any buffered entries before disconnecting to avoid data loss
254262
for (const entry of obs.takeRecords()) {
255-
if (entry.duration > maxGcPause) {
256-
maxGcPause = entry.duration;
257-
}
263+
totalGcTime += entry.duration;
258264
}
259265
obs.disconnect();
260-
gcDurations.push(maxGcPause);
266+
gcDurations.push(totalGcTime);
261267

262268
if (LOG_ITERATIONS) {
263-
logInfo(`Iteration ${i + 1}: ${maxGcPause.toFixed(2)} ms GC`);
269+
logInfo(`Iteration ${i + 1}: ${totalGcTime.toFixed(2)} ms GC`);
264270
} else if ((i + 1) % progressInterval === 0 || i + 1 === iterations) {
265271
const progress = Math.round(((i + 1) / iterations) * 100);
266272
logInfo(`Progress: ${progress}%`);
@@ -862,22 +868,38 @@ async function runBenchmarks() {
862868
];
863869

864870
// Run each benchmark with database cleanup
865-
for (const benchmark of benchmarks) {
866-
logInfo(`\nRunning benchmark '${benchmark.name}'...`);
867-
resetParseServer();
868-
await cleanupDatabase();
869-
results.push(await benchmark.fn(benchmark.name));
871+
const suiteStart = performance.now();
872+
for (let idx = 0; idx < benchmarks.length; idx++) {
873+
const benchmark = benchmarks[idx];
874+
const label = `[${idx + 1}/${benchmarks.length}] ${benchmark.name}`;
875+
logGroup(label);
876+
try {
877+
logInfo('Resetting database...');
878+
resetParseServer();
879+
await cleanupDatabase();
880+
logInfo('Running benchmark...');
881+
const benchStart = performance.now();
882+
const result = await benchmark.fn(benchmark.name);
883+
const benchDuration = ((performance.now() - benchStart) / 1000).toFixed(1);
884+
results.push(result);
885+
logInfo(`Result: ${result.value.toFixed(2)} ${result.unit} (${result.extra})`);
886+
logInfo(`Duration: ${benchDuration}s`);
887+
} finally {
888+
logGroupEnd();
889+
}
870890
}
891+
const suiteDuration = ((performance.now() - suiteStart) / 1000).toFixed(1);
871892

872893
// Output results in github-action-benchmark format (stdout)
873894
logInfo(JSON.stringify(results, null, 2));
874895

875-
// Output summary to stderr for visibility
876-
logInfo('Benchmarks completed successfully!');
877-
logInfo('Summary:');
896+
// Output summary
897+
logGroup('Summary');
878898
results.forEach(result => {
879-
logInfo(` ${result.name}: ${result.value.toFixed(2)} ${result.unit} (${result.extra})`);
899+
logInfo(`${result.name}: ${result.value.toFixed(2)} ${result.unit} (${result.extra})`);
880900
});
901+
logInfo(`Total duration: ${suiteDuration}s`);
902+
logGroupEnd();
881903

882904
} catch (error) {
883905
logError('Error running benchmarks:', error);

changelogs/CHANGELOG_alpha.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,129 @@
1+
# [9.7.0-alpha.18](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.17...9.7.0-alpha.18) (2026-03-30)
2+
3+
4+
### Features
5+
6+
* Extend storage adapter interface to optionally return `matchedCount` and `modifiedCount` from `DatabaseController.update` with `many: true` ([#10353](https://github.com/parse-community/parse-server/issues/10353)) ([aea7596](https://github.com/parse-community/parse-server/commit/aea7596cd2336c1c179ae130efd550f1596f5f3a))
7+
8+
# [9.7.0-alpha.17](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.16...9.7.0-alpha.17) (2026-03-29)
9+
10+
11+
### Bug Fixes
12+
13+
* Cloud Code trigger context vulnerable to prototype pollution ([#10352](https://github.com/parse-community/parse-server/issues/10352)) ([d5f5128](https://github.com/parse-community/parse-server/commit/d5f5128ade49749856d8ad5f9750ffd26d44836a))
14+
15+
# [9.7.0-alpha.16](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.15...9.7.0-alpha.16) (2026-03-29)
16+
17+
18+
### Bug Fixes
19+
20+
* LiveQuery protected-field guard bypass via array-like logical operator value ([GHSA-mmg8-87c5-jrc2](https://github.com/parse-community/parse-server/security/advisories/GHSA-mmg8-87c5-jrc2)) ([#10350](https://github.com/parse-community/parse-server/issues/10350)) ([f63fd1a](https://github.com/parse-community/parse-server/commit/f63fd1a3fe0a7c1c5fe809f01b0e04759e8c9b98))
21+
22+
# [9.7.0-alpha.15](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.14...9.7.0-alpha.15) (2026-03-29)
23+
24+
25+
### Bug Fixes
26+
27+
* Batch login sub-request rate limit uses IP-based keying ([#10349](https://github.com/parse-community/parse-server/issues/10349)) ([63c37c4](https://github.com/parse-community/parse-server/commit/63c37c49c7a72dc617635da8859004503021b8fd))
28+
29+
# [9.7.0-alpha.14](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.13...9.7.0-alpha.14) (2026-03-29)
30+
31+
32+
### Bug Fixes
33+
34+
* Session field immutability bypass via falsy-value guard ([GHSA-f6j3-w9v3-cq22](https://github.com/parse-community/parse-server/security/advisories/GHSA-f6j3-w9v3-cq22)) ([#10347](https://github.com/parse-community/parse-server/issues/10347)) ([9080296](https://github.com/parse-community/parse-server/commit/90802969fc713b7bc9733d7255c7519a6ed75d21))
35+
36+
# [9.7.0-alpha.13](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.12...9.7.0-alpha.13) (2026-03-29)
37+
38+
39+
### Features
40+
41+
* Add support for `partialFilterExpression` in MongoDB storage adapter ([#10346](https://github.com/parse-community/parse-server/issues/10346)) ([8dd7bf2](https://github.com/parse-community/parse-server/commit/8dd7bf2f61c07b0467d6dbc7aad5142db6694339))
42+
43+
# [9.7.0-alpha.12](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.11...9.7.0-alpha.12) (2026-03-29)
44+
45+
46+
### Bug Fixes
47+
48+
* GraphQL complexity validator exponential fragment traversal DoS ([GHSA-mfj6-6p54-m98c](https://github.com/parse-community/parse-server/security/advisories/GHSA-mfj6-6p54-m98c)) ([#10344](https://github.com/parse-community/parse-server/issues/10344)) ([f759bda](https://github.com/parse-community/parse-server/commit/f759bda075298ec44e2b4fb57659a0c56620483b))
49+
50+
# [9.7.0-alpha.11](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.10...9.7.0-alpha.11) (2026-03-28)
51+
52+
53+
### Bug Fixes
54+
55+
* Cloud function validator bypass via prototype chain traversal ([GHSA-vpj2-qq7w-5qq6](https://github.com/parse-community/parse-server/security/advisories/GHSA-vpj2-qq7w-5qq6)) ([#10342](https://github.com/parse-community/parse-server/issues/10342)) ([dc59e27](https://github.com/parse-community/parse-server/commit/dc59e272665644083c5b7f6862d88ce1ef0b2674))
56+
57+
# [9.7.0-alpha.10](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.9...9.7.0-alpha.10) (2026-03-27)
58+
59+
60+
### Bug Fixes
61+
62+
* GraphQL API endpoint ignores CORS origin restriction ([GHSA-q3p6-g7c4-829c](https://github.com/parse-community/parse-server/security/advisories/GHSA-q3p6-g7c4-829c)) ([#10334](https://github.com/parse-community/parse-server/issues/10334)) ([4dd0d3d](https://github.com/parse-community/parse-server/commit/4dd0d3d8be1c39664c74ad10bb0abaa76bc41203))
63+
64+
# [9.7.0-alpha.9](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.8...9.7.0-alpha.9) (2026-03-27)
65+
66+
67+
### Bug Fixes
68+
69+
* LiveQuery protected field leak via shared mutable state across concurrent subscribers ([GHSA-m983-v2ff-wq65](https://github.com/parse-community/parse-server/security/advisories/GHSA-m983-v2ff-wq65)) ([#10330](https://github.com/parse-community/parse-server/issues/10330)) ([776c71c](https://github.com/parse-community/parse-server/commit/776c71c3078e77d38c94937f463741793609d055))
70+
71+
# [9.7.0-alpha.8](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.7...9.7.0-alpha.8) (2026-03-26)
72+
73+
74+
### Bug Fixes
75+
76+
* MFA single-use token bypass via concurrent authData login requests ([GHSA-w73w-g5xw-rwhf](https://github.com/parse-community/parse-server/security/advisories/GHSA-w73w-g5xw-rwhf)) ([#10326](https://github.com/parse-community/parse-server/issues/10326)) ([e7efbeb](https://github.com/parse-community/parse-server/commit/e7efbebba398ce6abe5b6b6fb9829c6ebe310fbf))
77+
78+
# [9.7.0-alpha.7](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.6...9.7.0-alpha.7) (2026-03-26)
79+
80+
81+
### Bug Fixes
82+
83+
* Auth data exposed via verify password endpoint ([GHSA-wp76-gg32-8258](https://github.com/parse-community/parse-server/security/advisories/GHSA-wp76-gg32-8258)) ([#10323](https://github.com/parse-community/parse-server/issues/10323)) ([770be86](https://github.com/parse-community/parse-server/commit/770be8647424d92f5425c41fa81065ffbbb171ed))
84+
85+
# [9.7.0-alpha.6](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.5...9.7.0-alpha.6) (2026-03-26)
86+
87+
88+
### Bug Fixes
89+
90+
* Duplicate session destruction can cause unhandled promise rejection ([#10319](https://github.com/parse-community/parse-server/issues/10319)) ([92791c1](https://github.com/parse-community/parse-server/commit/92791c1d1d4b042a0e615ba45dcef491b904eccf))
91+
92+
# [9.7.0-alpha.5](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.4...9.7.0-alpha.5) (2026-03-25)
93+
94+
95+
### Bug Fixes
96+
97+
* Postgres query on non-existent column throws internal server error ([#10308](https://github.com/parse-community/parse-server/issues/10308)) ([c5c4325](https://github.com/parse-community/parse-server/commit/c5c43259d1f98af5bbbbc44d9daf7c0f1f8168d3))
98+
99+
# [9.7.0-alpha.4](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.3...9.7.0-alpha.4) (2026-03-24)
100+
101+
102+
### Bug Fixes
103+
104+
* Missing error messages in Parse errors ([#10304](https://github.com/parse-community/parse-server/issues/10304)) ([f128048](https://github.com/parse-community/parse-server/commit/f12804800bc9232de02b4314e886bab6b169f041))
105+
106+
# [9.7.0-alpha.3](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.2...9.7.0-alpha.3) (2026-03-23)
107+
108+
109+
### Bug Fixes
110+
111+
* Maintenance key blocked from querying protected fields ([#10290](https://github.com/parse-community/parse-server/issues/10290)) ([7c8b213](https://github.com/parse-community/parse-server/commit/7c8b213d96f1fd79f27d3a2bc01bef8bcaf588cd))
112+
113+
# [9.7.0-alpha.2](https://github.com/parse-community/parse-server/compare/9.7.0-alpha.1...9.7.0-alpha.2) (2026-03-23)
114+
115+
116+
### Features
117+
118+
* Add `protectedFieldsSaveResponseExempt` option to strip protected fields from save responses ([#10289](https://github.com/parse-community/parse-server/issues/10289)) ([4f7cb53](https://github.com/parse-community/parse-server/commit/4f7cb53bd114554cf9e6d7855b5e8911cb87544b))
119+
120+
# [9.7.0-alpha.1](https://github.com/parse-community/parse-server/compare/9.6.1...9.7.0-alpha.1) (2026-03-23)
121+
122+
123+
### Features
124+
125+
* Add `protectedFieldsTriggerExempt` option to exempt Cloud Code triggers from `protectedFields` ([#10288](https://github.com/parse-community/parse-server/issues/10288)) ([1610f98](https://github.com/parse-community/parse-server/commit/1610f98316f7cb1120a7e20be7a1570b0e116df7))
126+
1127
## [9.6.1-alpha.1](https://github.com/parse-community/parse-server/compare/9.6.0...9.6.1-alpha.1) (2026-03-22)
2128

3129

0 commit comments

Comments
 (0)