@objectstack/plugin-reports 16.1.0 → 17.0.0-rc.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,467 @@
1
1
  # @objectstack/plugin-reports
2
2
 
3
+ ## 17.0.0-rc.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2e836de: chore(packaging): CHANGELOG.md ships in every npm tarball (#4261)
8
+
9
+ The AGENTS.md post-task checklist requires breaking changesets to carry their
10
+ FROM → TO migration because "this text ships to consumers as `CHANGELOG.md`
11
+ inside the npm package and is what an upgrading agent greps after the tombstone
12
+ error." That delivery path was severed for 68 of the 69 publishable packages:
13
+ npm packs `package.json` / `README*` / `LICENSE*` unconditionally but — unlike
14
+ older npm versions — not `CHANGELOG.md`, and the canonical
15
+ `"files": ["dist", "README.md"]` whitelist never named it. Measured on npm
16
+ 10.9.7: `npm pack --dry-run` on `@objectstack/types` shipped 3 files while its
17
+ 70KB `CHANGELOG.md` stayed behind. Only `@objectstack/spec` listed it
18
+ explicitly.
19
+
20
+ The tombstone-error scenario is precisely the one where the repo is out of
21
+ reach — the upgrading agent has `node_modules` and nothing else — so the
22
+ migration text has to ride in the tarball. Every publishable package now
23
+ declares `CHANGELOG.md` in `files`, and the canonical whitelist is
24
+ `["dist", "README.md", "CHANGELOG.md"]`.
25
+
26
+ The other half is the gate: `check:published-files` gains a fifth invariant,
27
+ COMPLETE — a whitelist that fails to cover `CHANGELOG.md` fails the
28
+ always-required lint job, so the next package cannot silently sever the path
29
+ again. `@objectstack/spec`'s per-package EXTRA_ENTRIES exemption dissolves
30
+ into the canonical set.
31
+
32
+ Consumer-visible change: one more file per install (the package's changelog,
33
+ e.g. 70.8KB for `@objectstack/types`), and `grep -r "removed key"
34
+ node_modules/@objectstack/*/CHANGELOG.md` now finds the migration it was
35
+ promised.
36
+
37
+ - b5f9397: fix(sharing,runtime): a `sort` passed straight to the engine never ordered anything; migrate every in-repo engine call to canonical QueryAST keys (#4346)
38
+
39
+ Two changes with different weights, from one sweep of every in-repo engine
40
+ call site that still speaks a deprecated alias.
41
+
42
+ **The bug — three dropped sorts.** #4346 made the engine fold `filter`→`where`
43
+ and `top`→`limit` on all six methods. The other four pairs in
44
+ `RPC_QUERY_ALIAS_SLOTS` (`select`, `sort`, `skip`, `populate`) are folded at
45
+ the RPC/wire layer only — their values need shape lowering that belongs to
46
+ those layers — and a **direct `engine.find()` never crosses that layer**. Three
47
+ call sites passed `sort` there, so it rode onto the AST untouched, every
48
+ driver's `Array.isArray(query.orderBy)` guard declined to emit an ORDER BY, and
49
+ the query returned an ordinary-looking, arbitrarily-ordered result:
50
+
51
+ | call site | asked for | actually got |
52
+ | ----------------------------------- | ------------------------------------------------- | --------------------------- |
53
+ | `share-link-routes.ts` | shared AI conversation messages, `created_at asc` | messages in arbitrary order |
54
+ | `runtime/domains/share-links.ts` | same route, runtime-domain copy | same |
55
+ | `share-link-service.ts` `listLinks` | the 200 most recent share links | an arbitrary 200 |
56
+
57
+ All three combine the dropped sort with a `limit` — the "latest N" shape whose
58
+ failure #4226 spelled out: an unapplied sort returns rows in arbitrary order,
59
+ which `limit` then slices into an arbitrary page. #4226 fixed that in the wire
60
+ normalizer; these calls sit one layer below it. `listLinks` had no test at all,
61
+ which is why it went unnoticed. Now pinned — on the option bag the engine
62
+ receives, not on row order, because the failure is that the key never becomes
63
+ `orderBy` and a fake engine honouring either spelling would pass either way.
64
+
65
+ **The cleanup — 27 no-op renames.** Every remaining in-repo engine call passing
66
+ `filter` now passes `where` (approvals 5, auth 2, reports 6, sharing 11,
67
+ webhooks 2, plus the one `filters` in a spec doc example). These are strict
68
+ no-ops since #4346 folds the alias — the point is that the framework stops
69
+ depending on a spelling it asks users to migrate off, which is a prerequisite
70
+ for ever retiring the aliases. Service-level `filter` PARAMETERS (each
71
+ service's own public API, e.g. `listRequests(filter)`) are deliberately
72
+ untouched — those are not engine option bags.
73
+
74
+ Two of the renamed calls were live victims of the #4346 bug rather than
75
+ cosmetic: `auth-manager`'s `stampIdentitySource` read the table's first row via
76
+ `findOne({filter})` and counted the whole table via `count({filter})`, so a
77
+ federated sign-in never stamped `source: 'idp_provisioned'`. #4346 already
78
+ corrected the behaviour; this makes the call say what it means.
79
+
80
+ - cc2de0e: chore(packaging): 20 packages stop publishing their sources, tests and build tooling (#4248)
81
+
82
+ These 20 packages declared no `files` field, so npm fell back to packing the
83
+ whole package directory. `npm pack --dry-run` on `@objectstack/plugin-webhooks`
84
+ listed **21 files** — 15 under `src/`, three of them unit tests
85
+ (`auto-enqueuer.test.ts`, `bootstrap-declared-webhooks.test.ts`, …), plus the
86
+ build-time `scripts/i18n-extract.config.ts`. `dist/` lands on top of that at
87
+ publish time rather than instead of it, so consumers were installing the
88
+ TypeScript sources and the test suite alongside the artifact they asked for.
89
+
90
+ Each now declares `"files": ["dist", "README.md"]`, matching the 29 packages
91
+ that already did. Nothing a consumer imports moves: every `main` / `types` /
92
+ `exports` target in all 20 already resolved inside `dist/`, which the new
93
+ `check:published-files` guard verifies rather than assumes. The visible change
94
+ is a smaller install and a smaller dependency-scanning surface — `npm pack` on
95
+ `@objectstack/plugin-webhooks` now yields 2 files plus `dist/`.
96
+
97
+ The other half of the fix is the gate. Half the packages declaring `files` and
98
+ half not was the #3786 shape — a hand-copied convention with nothing enforcing
99
+ it, where whoever forgets the line gets no signal at all. `check:published-files`
100
+ (new, wired into the always-required `lint` job) holds every non-private
101
+ workspace package to four invariants: `files` is **declared**; it is
102
+ **sufficient** (covers every entry point, so tightening a whitelist cannot ship
103
+ a package that fails to resolve); it is **minimal** (admits no test, test-harness
104
+ config or build script); and anything beyond `dist` + `README.md` is
105
+ **registered** with a reason, reconciled in both directions so a stale exemption
106
+ is an error rather than dead text. `@objectstack/spec` is the one package with
107
+ registered extras — its `.zod.ts` sources, JSON Schemas, liveness ledgers and
108
+ `CHANGELOG.md` are product, not build input.
109
+
110
+ This also closes an assumption #4206 was resting on. Excluding `<pkg>/scripts/**`
111
+ from the docs-drift implementation test is sound only while no package publishes
112
+ `scripts/` as runtime code; that held, but it held because someone read all three
113
+ offenders by hand. It is now checked on every PR.
114
+
115
+ - Updated dependencies [6a67d7a]
116
+ - Updated dependencies [0ecc656]
117
+ - Updated dependencies [06772eb]
118
+ - Updated dependencies [270650f]
119
+ - Updated dependencies [3aef718]
120
+ - Updated dependencies [1ea6bce]
121
+ - Updated dependencies [c1dcacd]
122
+ - Updated dependencies [ad303ed]
123
+ - Updated dependencies [32ccb23]
124
+ - Updated dependencies [f5a4ef0]
125
+ - Updated dependencies [2d3e255]
126
+ - Updated dependencies [7d7521f]
127
+ - Updated dependencies [5dc4d02]
128
+ - Updated dependencies [05154a1]
129
+ - Updated dependencies [9b6fe7c]
130
+ - Updated dependencies [8c711fb]
131
+ - Updated dependencies [09e4547]
132
+ - Updated dependencies [91f4c78]
133
+ - Updated dependencies [820eff9]
134
+ - Updated dependencies [8d895ff]
135
+ - Updated dependencies [f6472d7]
136
+ - Updated dependencies [78caf51]
137
+ - Updated dependencies [62a789b]
138
+ - Updated dependencies [789ad63]
139
+ - Updated dependencies [2af1988]
140
+ - Updated dependencies [0af50a3]
141
+ - Updated dependencies [2e836de]
142
+ - Updated dependencies [12a19a8]
143
+ - Updated dependencies [41dcda3]
144
+ - Updated dependencies [c8124e5]
145
+ - Updated dependencies [a1a4140]
146
+ - Updated dependencies [217e2e6]
147
+ - Updated dependencies [86a71d1]
148
+ - Updated dependencies [d5c75e2]
149
+ - Updated dependencies [03d26f7]
150
+ - Updated dependencies [4384921]
151
+ - Updated dependencies [3c628ce]
152
+ - Updated dependencies [7cb922e]
153
+ - Updated dependencies [1d22114]
154
+ - Updated dependencies [b5f9397]
155
+ - Updated dependencies [ed77493]
156
+ - Updated dependencies [58a03d2]
157
+ - Updated dependencies [dc530b4]
158
+ - Updated dependencies [e59786e]
159
+ - Updated dependencies [bcf1112]
160
+ - Updated dependencies [9774b78]
161
+ - Updated dependencies [b07d829]
162
+ - Updated dependencies [a648e96]
163
+ - Updated dependencies [a47ac06]
164
+ - Updated dependencies [e4c61a7]
165
+ - Updated dependencies [cc60165]
166
+ - Updated dependencies [081aa6f]
167
+ - Updated dependencies [91f4c78]
168
+ - Updated dependencies [e8d0c21]
169
+ - Updated dependencies [45dc446]
170
+ - Updated dependencies [c1d44f7]
171
+ - Updated dependencies [ab9fb5c]
172
+ - Updated dependencies [f985b3f]
173
+ - Updated dependencies [9a4932a]
174
+ - Updated dependencies [f9fc874]
175
+ - Updated dependencies [011b386]
176
+ - Updated dependencies [7777e8f]
177
+ - Updated dependencies [507b92a]
178
+ - Updated dependencies [7309c81]
179
+ - Updated dependencies [20bc1ec]
180
+ - Updated dependencies [90c2b15]
181
+ - Updated dependencies [42eeb7d]
182
+ - Updated dependencies [01e124d]
183
+ - Updated dependencies [7ce02eb]
184
+ - Updated dependencies [a13827e]
185
+ - Updated dependencies [7733604]
186
+ - Updated dependencies [40e420f]
187
+ - Updated dependencies [d13004a]
188
+ - Updated dependencies [be7360c]
189
+ - Updated dependencies [5b47ab5]
190
+ - Updated dependencies [b09d8d9]
191
+ - Updated dependencies [b09d8d9]
192
+ - Updated dependencies [8675db6]
193
+ - Updated dependencies [b09d8d9]
194
+ - Updated dependencies [3eb1b2b]
195
+ - Updated dependencies [59b85c0]
196
+ - Updated dependencies [6e357ed]
197
+ - Updated dependencies [d6938bf]
198
+ - Updated dependencies [31e0be9]
199
+ - Updated dependencies [4bfd455]
200
+ - Updated dependencies [ffd2ce2]
201
+ - Updated dependencies [62f8017]
202
+ - Updated dependencies [a831df1]
203
+ - Updated dependencies [f752ee3]
204
+ - Updated dependencies [a1b61e0]
205
+ - Updated dependencies [cd6b9f2]
206
+ - Updated dependencies [2cb6d3c]
207
+ - Updated dependencies [af2a095]
208
+ - Updated dependencies [ec796d5]
209
+ - Updated dependencies [e87fea1]
210
+ - Updated dependencies [c65e529]
211
+ - Updated dependencies [3ca34c1]
212
+ - Updated dependencies [239c3a3]
213
+ - Updated dependencies [94a0bbc]
214
+ - Updated dependencies [d6bfb3d]
215
+ - Updated dependencies [a2266a6]
216
+ - Updated dependencies [d25a0ec]
217
+ - Updated dependencies [667b83e]
218
+ - Updated dependencies [627b188]
219
+ - Updated dependencies [8d4eae7]
220
+ - Updated dependencies [857a6cf]
221
+ - Updated dependencies [65a3a84]
222
+ - Updated dependencies [ccd9397]
223
+ - Updated dependencies [bca935b]
224
+ - Updated dependencies [d92c72d]
225
+ - Updated dependencies [c54c822]
226
+ - Updated dependencies [8dcc0f5]
227
+ - Updated dependencies [75b9e51]
228
+ - Updated dependencies [0a2f233]
229
+ - Updated dependencies [8621cdd]
230
+ - Updated dependencies [6f23667]
231
+ - Updated dependencies [5d21a48]
232
+ - Updated dependencies [19365b7]
233
+ - Updated dependencies [b7ed26d]
234
+ - Updated dependencies [68dea0b]
235
+ - Updated dependencies [64f8cbe]
236
+ - Updated dependencies [b3a3d83]
237
+ - Updated dependencies [7a55913]
238
+ - Updated dependencies [35accbf]
239
+ - Updated dependencies [6038de7]
240
+ - Updated dependencies [eb95d97]
241
+ - Updated dependencies [e4c2dc8]
242
+ - Updated dependencies [1bd2795]
243
+ - Updated dependencies [8186a70]
244
+ - Updated dependencies [a329cca]
245
+ - Updated dependencies [6eec18c]
246
+ - Updated dependencies [4d7bebf]
247
+ - Updated dependencies [821ac7a]
248
+ - Updated dependencies [8f81731]
249
+ - Updated dependencies [8b50cb3]
250
+ - Updated dependencies [8c2db68]
251
+ - Updated dependencies [22b5e54]
252
+ - Updated dependencies [0166bd5]
253
+ - Updated dependencies [9b702dc]
254
+ - Updated dependencies [ab16331]
255
+ - @objectstack/spec@17.0.0-rc.1
256
+ - @objectstack/platform-objects@17.0.0-rc.1
257
+ - @objectstack/core@17.0.0-rc.1
258
+
259
+ ## 17.0.0-rc.0
260
+
261
+ ### Major Changes
262
+
263
+ - 4ed7ed4: feat(security)!: the export axis is now OPT-IN, explainable, and covers reports (#3544, #3710)
264
+
265
+ **BREAKING — `allowExport` unset no longer means "inherit read".** Reading a
266
+ record and taking a bulk machine-readable copy of the whole table are different
267
+ privileges (Salesforce "Export Reports", Dynamics "Export to Excel", NetSuite
268
+ "Export Lists", SAP `S_GUI` 61 all separate them). The axis now says so.
269
+
270
+ ### Migration — FROM → TO
271
+
272
+ | | before | after |
273
+ | -------------------- | ----------------------------------- | -------------------------- |
274
+ | `allowExport` unset | export **allowed** (inherited read) | export **denied** |
275
+ | `allowExport: false` | export denied | export denied (unchanged) |
276
+ | `allowExport: true` | export allowed | export allowed (unchanged) |
277
+
278
+ **The one-line fix:** add `allowExport: true` to the object entry (or the `'*'`
279
+ wildcard) of every permission set whose holders should keep exporting.
280
+
281
+ ```ts
282
+ objects: {
283
+ deal: { allowRead: true, allowExport: true }, // ← add the grant
284
+ }
285
+ ```
286
+
287
+ Nothing else changes: read, CRUD, RLS, FLS and sharing are untouched, and a set
288
+ that never exported is unaffected.
289
+
290
+ **Who is affected.** Package-shipped sets are re-seeded on upgrade, so the
291
+ built-ins are handled for you — `admin_full_access` and `organization_admin` now
292
+ carry `allowExport: true` explicitly. **Environment-authored sets are not**: any
293
+ custom set whose users export must be edited. `member_default` deliberately does
294
+ NOT carry the grant, so ordinary authenticated users lose export until an admin
295
+ grants it — that is the point of the flip, not an oversight.
296
+
297
+ **Merge semantics.** Most-permissive, exactly like the CRUD bits: any set
298
+ granting `true` grants export. `false` and unset are the same outcome; `false`
299
+ is authoring intent, not a veto, because permission sets are additive capability
300
+ containers (ADR-0090).
301
+
302
+ **Not implied by super-user bits.** `viewAllRecords` / `modifyAllRecords` no
303
+ longer confer export. Separating "may see all data" from "may take a bulk copy"
304
+ is the segregation-of-duties case the axis exists for.
305
+
306
+ ### Also in this change
307
+
308
+ - **spec** — a set carrying `allowExport` is now **high-privilege**
309
+ (`describeHighPrivilegeBits`), so it cannot be bound to the `everyone` /
310
+ `guest` audience anchors. Without this the opt-in was defeatable by binding an
311
+ export-granting set to `everyone`. One predicate, so the runtime anchor gate,
312
+ the `@objectstack/lint` security-posture rule and the install-time suggestion
313
+ surface all pick it up together.
314
+ - **spec / plugin-security** — `ExplainOperationSchema` gains `export`, so
315
+ `explain` can answer _why_ a caller got `403 EXPORT_NOT_PERMITTED`. It
316
+ explains as `read ∧ the export grant`: `object_crud` reports the conjunction
317
+ and attributes the granting set, while every data-shaped layer
318
+ (requiredPermissions, OWD/depth/sharing, RLS, record attribution) is computed
319
+ as the `find` the export actually performs — asking the RLS compiler about an
320
+ `export` operation would match no policy and wrongly report "no RLS applies".
321
+ `readFilter` is surfaced for `export` as it is for `read`.
322
+ - **plugin-reports** — closes the reports side door (#3710). A report rendered
323
+ as `csv`/`json` is the same bulk copy of the same object, so it is gated by
324
+ the same `ISecurityService.canExport`. Enforced in `executeReport`, which the
325
+ interactive run, the ad-hoc run and the scheduled dispatch all funnel through;
326
+ `scheduleReport` additionally refuses at create time so an author is not told
327
+ at 3am. A schedule created while granted stops delivering once the grant is
328
+ revoked. `html_table` stays a read — it is a rendered view, not a bulk copy.
329
+ Deployments without `plugin-security` are unaffected (no permission sets
330
+ exist, so the axis does not apply).
331
+
332
+ ### Patch Changes
333
+
334
+ - Updated dependencies [50616d9]
335
+ - Updated dependencies [08b5a3d]
336
+ - Updated dependencies [d99aeb3]
337
+ - Updated dependencies [4727eb8]
338
+ - Updated dependencies [f63cd09]
339
+ - Updated dependencies [fa3d0cf]
340
+ - Updated dependencies [af5a224]
341
+ - Updated dependencies [71f76e1]
342
+ - Updated dependencies [37b1346]
343
+ - Updated dependencies [99736a0]
344
+ - Updated dependencies [fe67e34]
345
+ - Updated dependencies [fdb4f50]
346
+ - Updated dependencies [1bd5652]
347
+ - Updated dependencies [14252d3]
348
+ - Updated dependencies [7fb436c]
349
+ - Updated dependencies [879ea13]
350
+ - Updated dependencies [201b31f]
351
+ - Updated dependencies [e2616e0]
352
+ - Updated dependencies [6fdc5c6]
353
+ - Updated dependencies [8b9d71e]
354
+ - Updated dependencies [33f5e23]
355
+ - Updated dependencies [259af21]
356
+ - Updated dependencies [587fc91]
357
+ - Updated dependencies [1986594]
358
+ - Updated dependencies [ad4af62]
359
+ - Updated dependencies [d44dbfa]
360
+ - Updated dependencies [474fe39]
361
+ - Updated dependencies [0bc685a]
362
+ - Updated dependencies [b949059]
363
+ - Updated dependencies [be1c52c]
364
+ - Updated dependencies [c5ff96d]
365
+ - Updated dependencies [84e7be9]
366
+ - Updated dependencies [a6c3f38]
367
+ - Updated dependencies [debc23a]
368
+ - Updated dependencies [0f8ad09]
369
+ - Updated dependencies [8f9689f]
370
+ - Updated dependencies [57a3bb3]
371
+ - Updated dependencies [5f9a987]
372
+ - Updated dependencies [9f060e5]
373
+ - Updated dependencies [bc17d39]
374
+ - Updated dependencies [db02d47]
375
+ - Updated dependencies [0bfdf46]
376
+ - Updated dependencies [376a061]
377
+ - Updated dependencies [7c7e246]
378
+ - Updated dependencies [f35cdc5]
379
+ - Updated dependencies [9ea2bc5]
380
+ - Updated dependencies [c2d9098]
381
+ - Updated dependencies [a227ed7]
382
+ - Updated dependencies [9613396]
383
+ - Updated dependencies [e47b342]
384
+ - Updated dependencies [4ed7ed4]
385
+ - Updated dependencies [2fa4ca1]
386
+ - Updated dependencies [f5a2320]
387
+ - Updated dependencies [deb538f]
388
+ - Updated dependencies [5b89711]
389
+ - Updated dependencies [0c8a22f]
390
+ - Updated dependencies [763931e]
391
+ - Updated dependencies [de9af8a]
392
+ - Updated dependencies [c4df271]
393
+ - Updated dependencies [a41ba5c]
394
+ - Updated dependencies [189854c]
395
+ - Updated dependencies [0e3a226]
396
+ - Updated dependencies [524151c]
397
+ - Updated dependencies [1d4756e]
398
+ - Updated dependencies [720c5ad]
399
+ - Updated dependencies [a8d1e24]
400
+ - Updated dependencies [d1cabaa]
401
+ - Updated dependencies [41642b0]
402
+ - Updated dependencies [4cca74c]
403
+ - Updated dependencies [88ef03e]
404
+ - Updated dependencies [9e2caf3]
405
+ - Updated dependencies [81ce41a]
406
+ - Updated dependencies [85e1e4e]
407
+ - Updated dependencies [dac6a08]
408
+ - Updated dependencies [394b7a1]
409
+ - Updated dependencies [677b591]
410
+ - Updated dependencies [d77d1b7]
411
+ - Updated dependencies [5b79a34]
412
+ - Updated dependencies [c757854]
413
+ - Updated dependencies [0045682]
414
+ - Updated dependencies [2a5f04a]
415
+ - Updated dependencies [4f740b0]
416
+ - Updated dependencies [67452d1]
417
+ - Updated dependencies [4921a95]
418
+ - Updated dependencies [0fc6219]
419
+ - Updated dependencies [605e190]
420
+ - Updated dependencies [c6c59f1]
421
+ - Updated dependencies [b0e78a8]
422
+ - Updated dependencies [f31cc8d]
423
+ - Updated dependencies [f343dc4]
424
+ - Updated dependencies [8269e32]
425
+ - Updated dependencies [74f7339]
426
+ - Updated dependencies [a6c35a2]
427
+ - Updated dependencies [c2f1002]
428
+ - Updated dependencies [f163028]
429
+ - Updated dependencies [f07808c]
430
+ - Updated dependencies [7ffc3d3]
431
+ - Updated dependencies [88346ba]
432
+ - Updated dependencies [4631592]
433
+ - Updated dependencies [32ff033]
434
+ - Updated dependencies [5ac93d4]
435
+ - Updated dependencies [93f267f]
436
+ - Updated dependencies [0024abf]
437
+ - Updated dependencies [acbf364]
438
+ - Updated dependencies [5487c20]
439
+ - Updated dependencies [aa8b847]
440
+ - Updated dependencies [7687f7b]
441
+ - Updated dependencies [1659072]
442
+ - Updated dependencies [abceb0d]
443
+ - Updated dependencies [0c302a7]
444
+ - Updated dependencies [6633337]
445
+ - Updated dependencies [f00d8d4]
446
+ - Updated dependencies [503be86]
447
+ - Updated dependencies [cde1975]
448
+ - Updated dependencies [0bc685a]
449
+ - Updated dependencies [11949fc]
450
+ - Updated dependencies [b098b0e]
451
+ - Updated dependencies [4d00b13]
452
+ - Updated dependencies [9aa5510]
453
+ - Updated dependencies [57bab76]
454
+ - Updated dependencies [b90086a]
455
+ - Updated dependencies [b95577a]
456
+ - Updated dependencies [83c161f]
457
+ - Updated dependencies [d8c4957]
458
+ - Updated dependencies [f24cb83]
459
+ - Updated dependencies [5dbbb92]
460
+ - Updated dependencies [69f1dfd]
461
+ - @objectstack/spec@17.0.0-rc.0
462
+ - @objectstack/platform-objects@17.0.0-rc.0
463
+ - @objectstack/core@17.0.0-rc.0
464
+
3
465
  ## 16.1.0
4
466
 
5
467
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -68,6 +68,21 @@ interface ReportServiceOptions {
68
68
  * closed instead of running with RLS bypassed (#2980).
69
69
  */
70
70
  resolveOwnerContext?: OwnerContextResolver;
71
+ /**
72
+ * [#3544 / #3710] The user-level export axis —
73
+ * `ISecurityService.canExport(object, context)`, wired by the reports plugin
74
+ * from `getService('security')`.
75
+ *
76
+ * A report rendered as `csv`/`json` IS a bulk machine-readable copy of the
77
+ * object, so it is the same privilege `GET /data/:object/export` gates.
78
+ * Without this the axis had a side door: a caller refused at that route could
79
+ * save a report on the same object, run it as CSV — or schedule one to their
80
+ * own inbox — and receive the identical rows.
81
+ *
82
+ * Omitted (no `plugin-security`, so no permission sets exist anywhere) → the
83
+ * axis does not apply, matching the REST export route's own fail-open.
84
+ */
85
+ canExport?: (object: string, context: unknown) => Promise<boolean>;
71
86
  }
72
87
  declare class ReportService implements IReportService {
73
88
  private readonly engine;
@@ -76,7 +91,28 @@ declare class ReportService implements IReportService {
76
91
  private readonly logger;
77
92
  private readonly maxRows;
78
93
  private readonly resolveOwnerContext?;
94
+ private readonly canExportFn?;
79
95
  constructor(opts: ReportServiceOptions);
96
+ /**
97
+ * [#3544 / #3710] Gate a report rendering on the user-level export axis.
98
+ *
99
+ * Throws `EXPORT_NOT_PERMITTED` when the principal behind `context` may not
100
+ * take a bulk copy of `object`. A no-op for non-bulk formats (`html_table`),
101
+ * for a system context, and when no `canExport` is wired.
102
+ *
103
+ * Deliberately checked HERE — one place — rather than at each of the three
104
+ * callers (`runReport`, the ad-hoc run, and the scheduled dispatch): a gate
105
+ * per call site is how a fourth call site later ships ungated. `dispatchDue`
106
+ * routes through `executeReport` too, so the scheduled CSV is covered by the
107
+ * same line. (`scheduleReport` additionally pre-checks, so an author is
108
+ * refused when they create the schedule rather than silently at 3am — but
109
+ * that is UX, and THIS is the enforcement: a grant revoked after the schedule
110
+ * was created must still stop the delivery.)
111
+ *
112
+ * Fails CLOSED on a throw — it resolves permission sets to decide, and a
113
+ * resolution failure must never read as a grant (ADR-0049).
114
+ */
115
+ private assertExportAllowed;
80
116
  /**
81
117
  * Authorization for a saved-report row. `sys_saved_report` is a
82
118
  * protection-locked system object, so its rows are *read* with
package/dist/index.d.ts CHANGED
@@ -68,6 +68,21 @@ interface ReportServiceOptions {
68
68
  * closed instead of running with RLS bypassed (#2980).
69
69
  */
70
70
  resolveOwnerContext?: OwnerContextResolver;
71
+ /**
72
+ * [#3544 / #3710] The user-level export axis —
73
+ * `ISecurityService.canExport(object, context)`, wired by the reports plugin
74
+ * from `getService('security')`.
75
+ *
76
+ * A report rendered as `csv`/`json` IS a bulk machine-readable copy of the
77
+ * object, so it is the same privilege `GET /data/:object/export` gates.
78
+ * Without this the axis had a side door: a caller refused at that route could
79
+ * save a report on the same object, run it as CSV — or schedule one to their
80
+ * own inbox — and receive the identical rows.
81
+ *
82
+ * Omitted (no `plugin-security`, so no permission sets exist anywhere) → the
83
+ * axis does not apply, matching the REST export route's own fail-open.
84
+ */
85
+ canExport?: (object: string, context: unknown) => Promise<boolean>;
71
86
  }
72
87
  declare class ReportService implements IReportService {
73
88
  private readonly engine;
@@ -76,7 +91,28 @@ declare class ReportService implements IReportService {
76
91
  private readonly logger;
77
92
  private readonly maxRows;
78
93
  private readonly resolveOwnerContext?;
94
+ private readonly canExportFn?;
79
95
  constructor(opts: ReportServiceOptions);
96
+ /**
97
+ * [#3544 / #3710] Gate a report rendering on the user-level export axis.
98
+ *
99
+ * Throws `EXPORT_NOT_PERMITTED` when the principal behind `context` may not
100
+ * take a bulk copy of `object`. A no-op for non-bulk formats (`html_table`),
101
+ * for a system context, and when no `canExport` is wired.
102
+ *
103
+ * Deliberately checked HERE — one place — rather than at each of the three
104
+ * callers (`runReport`, the ad-hoc run, and the scheduled dispatch): a gate
105
+ * per call site is how a fourth call site later ships ungated. `dispatchDue`
106
+ * routes through `executeReport` too, so the scheduled CSV is covered by the
107
+ * same line. (`scheduleReport` additionally pre-checks, so an author is
108
+ * refused when they create the schedule rather than silently at 3am — but
109
+ * that is UX, and THIS is the enforcement: a grant revoked after the schedule
110
+ * was created must still stop the delivery.)
111
+ *
112
+ * Fails CLOSED on a throw — it resolves permission sets to decide, and a
113
+ * resolution failure must never read as a grant (ADR-0049).
114
+ */
115
+ private assertExportAllowed;
80
116
  /**
81
117
  * Authorization for a saved-report row. `sys_saved_report` is a
82
118
  * protection-locked system object, so its rows are *read* with