@dudousxd/nestjs-media-dashboard 0.16.2 → 0.18.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.
Files changed (50) hide show
  1. package/dist/client/index.d.ts +1 -2
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +1 -4
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/client/media-console-client.d.ts +14 -23
  6. package/dist/client/media-console-client.d.ts.map +1 -1
  7. package/dist/client/media-console-client.js +53 -46
  8. package/dist/client/media-console-client.js.map +1 -1
  9. package/dist/client/types.d.ts +10 -6
  10. package/dist/client/types.d.ts.map +1 -1
  11. package/dist/server/index.cjs +805 -1235
  12. package/dist/server/index.cjs.map +1 -1
  13. package/dist/server/index.d.cts +362 -239
  14. package/dist/server/index.d.ts +362 -239
  15. package/dist/server/index.js +790 -1235
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/spa/assets/{ArchivePreview-h5wYCDJT.js → ArchivePreview-DacpA4QQ.js} +1 -1
  18. package/dist/spa/assets/CertificatePreview-CEfJ-ORW.js +127 -0
  19. package/dist/spa/assets/MarkdownPreview-47FMx48l.js +74 -0
  20. package/dist/spa/assets/ParquetPreview-DHvs5kV-.js +973 -0
  21. package/dist/spa/assets/{SheetPreview-Bfq7dI0a.js → SheetPreview-AT23RGVb.js} +1 -1
  22. package/dist/spa/assets/{SqlitePreview-JTQDjJJ3.js → SqlitePreview-D7KZuyZD.js} +1 -1
  23. package/dist/spa/assets/{index-Cjax4szq.js → index-BmejPQYi.js} +1 -1
  24. package/dist/spa/assets/{preview-BjGG6sSu.js → preview-DVKz8AwF.js} +2 -2
  25. package/dist/spa/assets/styles-CG_mruF7.css +1 -0
  26. package/dist/spa/assets/styles-CTvG1yXb.js +61 -0
  27. package/dist/spa/index.html +3 -3
  28. package/dist/spa/preview.html +3 -3
  29. package/package.json +25 -30
  30. package/dist/client/console-session.d.ts +0 -69
  31. package/dist/client/console-session.d.ts.map +0 -1
  32. package/dist/client/console-session.js +0 -120
  33. package/dist/client/console-session.js.map +0 -1
  34. package/dist/react/index.d.ts +0 -4
  35. package/dist/react/index.d.ts.map +0 -1
  36. package/dist/react/index.js +0 -15
  37. package/dist/react/index.js.map +0 -1
  38. package/dist/react/open-console-button.d.ts +0 -26
  39. package/dist/react/open-console-button.d.ts.map +0 -1
  40. package/dist/react/open-console-button.js +0 -29
  41. package/dist/react/open-console-button.js.map +0 -1
  42. package/dist/react/use-open-console.d.ts +0 -45
  43. package/dist/react/use-open-console.d.ts.map +0 -1
  44. package/dist/react/use-open-console.js +0 -75
  45. package/dist/react/use-open-console.js.map +0 -1
  46. package/dist/spa/assets/CertificatePreview-Bm4NkGaO.js +0 -127
  47. package/dist/spa/assets/MarkdownPreview-Cai2BrTH.js +0 -70
  48. package/dist/spa/assets/ParquetPreview-DfDTL_6f.js +0 -973
  49. package/dist/spa/assets/styles-Cui6Yy9Y.js +0 -61
  50. package/dist/spa/assets/styles-FRkIQ83o.css +0 -1
@@ -1,86 +1,132 @@
1
- import { DynamicModule, InjectionToken, OptionalFactoryDependency, Type, CanActivate, Provider, ModuleMetadata } from '@nestjs/common';
1
+ import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, InjectionToken, OptionalFactoryDependency, Provider, ModuleMetadata, StreamableFile } from '@nestjs/common';
2
+ import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
2
3
  import { Readable } from 'node:stream';
3
- import { StorageManager, MediaStore, UploadSessionStore, ReadRangeOptions } from '@dudousxd/nestjs-media-core';
4
+ import { MediaStore, UploadSessionStore, MultipartDrivers } from '@dudousxd/nestjs-media-core';
5
+ import { Storage, ServeFileOptions } from '@nestjs/storage';
4
6
 
5
- /** The session user a host `login`/`session` hook returns to mint a cookie. */
6
- interface ConsoleSessionUser {
7
- id: string;
8
- name?: string;
9
- roles?: string[];
7
+ type ConsoleAuthorizeResult = boolean | {
8
+ allowed: true;
9
+ } | {
10
+ allowed: false;
11
+ reason?: 'unauthenticated' | 'forbidden';
12
+ } | {
13
+ redirectTo: string;
14
+ };
15
+ /** Class form: an `@Injectable()` provider (constructor injection works). */
16
+ interface ConsoleAuthorizer {
17
+ authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
18
+ }
19
+ type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
20
+ /** `@nestjs/authorization` policy check: `policy[ability](user)`. */
21
+ interface ConsolePolicyCheck {
22
+ policy: Type<unknown>;
23
+ ability: string;
24
+ }
25
+ type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
26
+ default: ConsoleAuthorizerClass;
27
+ }>) | ConsolePolicyCheck | string;
28
+ type ConsoleDecision = {
29
+ allowed: true;
30
+ } | {
31
+ allowed: false;
32
+ reason: 'unauthenticated' | 'forbidden';
33
+ redirectTo?: string;
34
+ };
35
+ /** The provider `@dudousxd/nestjs-authz`'s `AuthzModule` registers to resolve gate names. */
36
+ declare const CONSOLE_GATE: symbol;
37
+ /** What `@dudousxd/nestjs-authz` provides under {@link CONSOLE_GATE}. */
38
+ interface ConsoleGate {
39
+ allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
40
+ }
41
+ /** What a resolved `authorize` is: called once per request, never throws. */
42
+ type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
43
+ /** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
44
+ declare const CONSOLE_AUTHORIZE_MISSING_WARNING: string;
45
+ /** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
46
+ declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
47
+ /**
48
+ * Resolves `authorize` into one function the guard calls per request. Configuration mistakes (a gate
49
+ * name without nestjs-authz, a policy without @nestjs/authorization and its module) throw here, so
50
+ * call it at boot. The returned function never throws: an error is a `forbidden`.
51
+ *
52
+ * `authorize` omitted: open outside production, `forbidden` for every request in production, and one
53
+ * warning logged (unless `warnIfMissing: false`).
54
+ */
55
+ declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
56
+ logger?: Logger;
57
+ warnIfMissing?: boolean;
58
+ registration?: string;
59
+ }): Promise<ConsoleDecide>;
60
+ /**
61
+ * Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
62
+ * is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
63
+ * empty, so a missing env var fails at config time instead of opening the console.
64
+ */
65
+ declare function authorizeByToken(options: {
66
+ token: string | undefined;
67
+ }): ConsoleAuthorize;
68
+ /** DI token of the resolved {@link ConsoleDecide} the guard runs. */
69
+ declare const CONSOLE_AUTHORIZE_DECIDE: symbol;
70
+ /** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
71
+ declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-media-dashboard:console-page";
72
+ declare const ConsolePage: () => ClassDecorator;
73
+ /**
74
+ * The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly
75
+ * where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.
76
+ */
77
+ declare class ConsoleAccessDenied extends HttpException {
78
+ readonly decision: Extract<ConsoleDecision, {
79
+ allowed: false;
80
+ }>;
81
+ /** Whether the denied request was a page navigation (answered with a redirect or a page). */
82
+ readonly page: boolean;
83
+ constructor(decision: Extract<ConsoleDecision, {
84
+ allowed: false;
85
+ }>,
86
+ /** Whether the denied request was a page navigation (answered with a redirect or a page). */
87
+ page?: boolean);
10
88
  }
11
-
12
- /** Host hook for Mode A — validates the host's own auth on the raw request. */
13
- type SessionHook = (request: unknown) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;
14
- /** Host hook for Mode B — validates submitted credentials from the built-in login screen. */
15
- type LoginHook = (username: string, password: string) => Promise<ConsoleSessionUser | null> | ConsoleSessionUser | null;
16
89
  /**
17
- * Re-checks a LIVE session when the cookie is slid forward. Runs at most once per `ttl/2` per
18
- * session, so a DB round-trip here is cheap. Return `false` to revoke (the cookie is cleared and
19
- * the request denied). Distinct from `session`: that hook reads the host's auth off a fresh
20
- * request, which a console XHR does not carry — this one receives the already-minted session.
90
+ * Runs the console's `authorize` on every request to a console controller. The dashboard applies it
91
+ * (after your `decorators`, so your authentication guards set `request.user` first); on controllers
92
+ * you mount yourself (`routes: false`) apply {@link UseConsoleAuthorize}.
21
93
  */
22
- type RevalidateHook = (session: ConsoleSessionUser) => Promise<boolean> | boolean;
23
- /** What `unauthenticatedPage` receives. An object (not positional args) so fields can be added later. */
24
- interface UnauthenticatedPageContext {
25
- /**
26
- * The platform-native request — Express' `Request`, Fastify's `FastifyRequest`. Typed `unknown`
27
- * for the same reason `SessionHook`'s is: this package refuses to depend on either platform.
28
- */
29
- request: unknown;
30
- /**
31
- * The platform-native response. The hook OWNS it: it must write AND end it. If it returns without
32
- * writing, the console falls back to serving its own SPA (whose auth screen then renders).
33
- */
34
- response: unknown;
35
- /** Where this console is mounted (e.g. `/media`) — useful for a "back to the console" link. */
36
- basePath: string;
94
+ declare class ConsoleAuthorizeGuard implements CanActivate {
95
+ private readonly decide;
96
+ constructor(decide: ConsoleDecide);
97
+ canActivate(context: ExecutionContext): Promise<boolean>;
37
98
  }
38
99
  /**
39
- * Host-owned page for an unauthenticated navigation to the console SPA.
40
- *
41
- * Without it, an unauthenticated visitor gets the SPA shell, which then renders the built-in auth
42
- * screen: a generic "open this console from your application" card, generic because the library
43
- * cannot know who hosts it. This hook replaces that with the host's own page — and, because it is
44
- * decided BEFORE the shell is served, it also stops the bundle from loading for a visitor who has
45
- * no session at all.
46
- *
47
- * Only consulted under Mode-A-only. With `login` configured the SPA's own login form IS the way in,
48
- * so gating the shell would lock Mode B hosts out of their own console.
49
- *
50
- * Fail-closed by construction: it only ever runs when the request has no valid session. A hook that
51
- * throws, or returns without writing, falls back to serving the SPA — it cannot let anyone in,
52
- * because every data route stays behind `MediaConsoleGuard` regardless.
100
+ * Answers a console denial: JSON for API calls; for a page navigation a 302 to `redirectTo`, an
101
+ * "Access denied" page (403) or a "Sign in required" page (401).
53
102
  */
54
- type UnauthenticatedPageHook = (context: UnauthenticatedPageContext) => void | Promise<void>;
55
- /** Author-facing `auth` option on `MediaDashboardModule.forRoot`. Mirrors telescope's dashboardAuth. */
56
- interface ConsoleAuthOptions {
57
- /** REQUIRED HMAC-SHA256 signing key. Missing/empty => boot error (fail closed). */
58
- secret: string;
59
- /** Cookie TTL as a duration string (`'8h'`, `'30m'`, `'7d'`). Default `'8h'`. */
60
- ttl?: string;
61
- /** Mode A: validate the host's own auth on the raw request. */
62
- session?: SessionHook;
63
- /** Mode B: validate credentials from the built-in login screen. */
64
- login?: LoginHook;
65
- /** Re-checks the session on sliding renewal. Not a mode — it cannot mint a session on its own. */
66
- revalidate?: RevalidateHook;
67
- /** Renders the host's own page for an unauthenticated navigation, in place of serving the SPA and
68
- * letting its built-in auth screen render; see `UnauthenticatedPageHook`. Mode-A-only. */
69
- unauthenticatedPage?: UnauthenticatedPageHook;
103
+ declare class ConsoleAuthorizeFilter implements ExceptionFilter {
104
+ private readonly adapterHost;
105
+ constructor(adapterHost: HttpAdapterHost);
106
+ catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
70
107
  }
108
+ /**
109
+ * The guard and its filter as one class decorator, for a console you mount yourself
110
+ * (`routes: false`). List it ABOVE your authentication guards, so it runs after them.
111
+ */
112
+ declare const UseConsoleAuthorize: () => ClassDecorator & MethodDecorator;
71
113
 
114
+ /**
115
+ * What a disk can do, computed by the server from the `@nestjs/storage` disk and its multipart
116
+ * side-car (`MediaDiskCapabilities` in `-core`).
117
+ */
72
118
  interface DiskCapabilities {
119
+ /** `signedUrl()` works (S3, or a local disk with `signedUrls`). */
73
120
  presign: boolean;
121
+ /** A multipart side-car is configured for the disk. */
74
122
  multipart: boolean;
123
+ /** The disk has a `publicUrl`. */
75
124
  publicUrls: boolean;
76
125
  list: boolean;
77
126
  /**
78
- * Can the disk serve a byte range without transferring the whole object (a ranged `object/raw`)?
79
- *
80
- * Optional HERE, unlike the required `ranged` on the driver's own `DriverCapabilities`, because
81
- * this is a WIRE shape: the console bundle can be served by a host still running an older server
82
- * build that never emits the field. `undefined` means "this server didn't say", which a caller
83
- * should treat as "don't count on it" — not as `false`.
127
+ * Can the disk serve a byte range (a ranged `object/raw`)? Every `@nestjs/storage` disk can, so
128
+ * current servers always send `true`. Optional because this is a WIRE shape: an older server may
129
+ * not emit it, and `undefined` means "don't count on it", not `false`.
84
130
  */
85
131
  ranged?: boolean;
86
132
  }
@@ -287,12 +333,12 @@ interface MediaDashboardOptions {
287
333
  /**
288
334
  * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under
289
335
  * your app's `/api` prefix — e.g. `/api/media/console` — so the API inherits the app's
290
- * auth/proxy rules while the UI stays at `basePath`.
336
+ * proxy rules while the UI stays at `basePath`.
291
337
  */
292
338
  apiBasePath?: string;
293
339
  /**
294
340
  * Enable the destructive endpoints (delete object/record, copy/move, abort upload). Default
295
- * `false` — the read API is always available. Front the mount with your own guard either way.
341
+ * `false` — the read API is always available.
296
342
  */
297
343
  actions?: boolean;
298
344
  /**
@@ -306,56 +352,87 @@ interface MediaDashboardOptions {
306
352
  */
307
353
  objectUrls?: ObjectUrlStrategy;
308
354
  /**
309
- * Gate the console (SPA + API) behind a built-in session-cookie login, telescope-style. Omit to
310
- * leave the console open (front it with your own guard). When set, the SPA renders a login screen
311
- * until a valid cookie exists; supply a `login(username, password)` and/or `session(request)`
312
- * hook that returns a session user (or `null` to deny), and optionally a `revalidate(session)`
313
- * hook that re-checks the user when the cookie is slid forward — see {@link ConsoleAuthOptions}.
355
+ * Extra `imports` merged into the dashboard's dynamic module — the DI resolution path for the
356
+ * dependencies of a guard passed through {@link decorators} (e.g. `UseGuards(AuthenticationGuard)`),
357
+ * or any other provider the controllers need reachable.
358
+ */
359
+ imports?: DynamicModule['imports'];
360
+ /**
361
+ * Host-supplied context about a disk object, rendered in the console's preview — see
362
+ * {@link ObjectInsightProvider}. The console can only describe a file as storage sees it; this is
363
+ * how it learns what the file *means* to your app. Omit for no annotation (the default).
364
+ *
365
+ * Use `forRootAsync`'s `useObjectInsights` when a provider needs injected services.
314
366
  */
315
- auth?: ConsoleAuthOptions;
367
+ objectInsights?: ObjectInsightProvider[];
316
368
  /**
317
- * Guard classes (or already-instantiated `CanActivate`s) fronting BOTH the page/asset controller
318
- * (`MediaDashboardUiController` — a plain REPLACE, since it ships with no guard of its own) and
319
- * the read/action JSON API controllers (APPENDED to their own built-in `MediaConsoleGuard`
320
- * session-cookie gate — a request must pass BOTH). Deliberately NOT applied to the auth
321
- * controller that MINTS that session cookie — see `MediaConsoleApiModule`'s own `guards` doc.
369
+ * Who may use the console: decided on EVERY console request (the page, its assets and the JSON
370
+ * API) by a guard that runs after your `decorators`, so your authentication guards set
371
+ * `request.user` first. Five forms:
322
372
  *
323
- * These two surfaces (page vs. API) live in the same package here, unlike a split dashboard —
324
- * but they're still SEPARATE controllers on separate host modules, so `guards` is stamped onto
325
- * each independently by this one option.
373
+ * ```ts
374
+ * authorize: (ctx) => ctx.switchToHttp().getRequest().user?.isAdmin === true,
375
+ * authorize: ConsoleAccess, // @Injectable() implements ConsoleAuthorizer
376
+ * authorize: () => import('./console-access.js'), // lazy, default export
377
+ * authorize: { policy: ConsolePolicy, ability: 'view' }, // @nestjs/authorization policy
378
+ * authorize: 'console.media', // a gate from @dudousxd/nestjs-authz
379
+ * ```
326
380
  *
327
- * Full-page navigations to `basePath` carry only cookies, never an `Authorization` header — a
328
- * guard passed here must be able to authenticate from a cookie (see the "Securing the console"
329
- * guide) or it will 401 every browser visit to the console, even an already-logged-in admin's.
381
+ * Answer `true`/`{ allowed: true }` to allow, `false`/`{ allowed: false }` for 403,
382
+ * `{ allowed: false, reason: 'unauthenticated' }` for 401, or `{ redirectTo: '/login' }` to send a
383
+ * page navigation there (API calls get 401 with `redirectTo`). A throw is a 403.
330
384
  *
331
- * `guards` and `auth` (the built-in cookie login) are independent and compose: with both set, a
332
- * request must pass the host guard AND (when a route carries `MediaConsoleGuard`) present a valid
333
- * built-in session cookie too. Configure one, the other, or both.
385
+ * Omitted: open outside production; in production every request answers 403, and one warning is
386
+ * logged at boot.
334
387
  *
335
- * A guard's own DEPENDENCIES resolve from this option's `imports` (see {@link imports}) — the
336
- * dashboard module has no application context of its own to pull them from otherwise.
388
+ * This is the console's access. It is not `MediaModule`'s `authorize(user, action, subject)` hook,
389
+ * which governs the product upload routes.
337
390
  */
338
- guards?: Array<Type<CanActivate> | CanActivate>;
391
+ authorize?: ConsoleAuthorize;
339
392
  /**
340
- * Extra `imports` merged into the dashboard's dynamic module — the DI resolution path for a class
341
- * passed to {@link guards} (or any other provider the controllers need reachable). Typically the
342
- * host's own auth module, e.g. `imports: [AuthModule]` alongside `guards: [SessionGuard]`.
393
+ * Class decorators applied to EVERY controller the console mounts — the page/asset controller
394
+ * and the read and action APIs — e.g.
395
+ * `[UseGuards(AuthenticationGuard), Can(MediaPolicy, 'console')]` with the first-party
396
+ * `@nestjs/authentication` / `@nestjs/authorization`. Default: none.
397
+ *
398
+ * Every console controller is marked `@Can.Anyone()`-open (`'authorization:can-anyone'`) so
399
+ * `@nestjs/authorization`'s deny-by-default global guard doesn't refuse the console; a `Can(...)`
400
+ * passed here is still checked (the guard treats `can-anyone` as open only when no requirement is
401
+ * declared).
402
+ *
403
+ * `decorators` don't decide access on their own: use them for authentication, throttling and the
404
+ * like, and {@link authorize} for who may use the console.
405
+ *
406
+ * Full-page navigations to `basePath` carry only cookies, never an `Authorization` header — a
407
+ * guard here must be able to authenticate the page from a cookie, or every browser visit 401s.
408
+ *
409
+ * Each registration decorates fresh subclasses of the controllers, so one registration's
410
+ * decorators never reach another.
343
411
  */
344
- imports?: DynamicModule['imports'];
412
+ decorators?: ClassDecorator[];
345
413
  /**
346
- * Host-supplied context about a disk object, rendered in the console's preview — see
347
- * {@link ObjectInsightProvider}. The console can only describe a file as storage sees it; this is
348
- * how it learns what the file *means* to your app. Omit for no annotation (the default).
414
+ * `false` mounts NO controller — no page, no JSON API — while every provider still works and is
415
+ * exported (`MediaConsoleService`, the resolved paths), so you serve
416
+ * the console from controllers of your own by extending the exported controller classes. Apply
417
+ * `UseConsoleAuthorize()` to them, ABOVE your authentication guard (class decorators apply
418
+ * bottom-up, so authentication runs first), so {@link authorize} still decides access. Keep
419
+ * `basePath`/`apiBasePath` equal to where you mount them: the SPA reads both.
349
420
  *
350
- * Use `forRootAsync`'s `useObjectInsights` when a provider needs injected services.
421
+ * ```ts
422
+ * @UseConsoleAuthorize()
423
+ * @UseGuards(AuthGuard)
424
+ * @Controller('media/api')
425
+ * export class ConsoleApiController extends MediaConsoleReadController {}
426
+ * ```
427
+ *
428
+ * Default `true`.
351
429
  */
352
- objectInsights?: ObjectInsightProvider[];
430
+ routes?: boolean;
353
431
  }
354
432
  /**
355
- * Async variant of {@link MediaDashboardOptions}. The mount paths + `actions` stay static (the
356
- * router needs them at module-definition time), but `auth` is built by an injected factory — so
357
- * the `login`/`session` hooks can reach your DB/services (e.g. an EntityManager to validate an
358
- * admin). Returning `undefined` from the factory leaves the console open.
433
+ * Async variant of {@link MediaDashboardOptions}. The mount paths, `actions` and `decorators` stay
434
+ * static (the router needs them at module-definition time); the `objectInsights` providers are
435
+ * built by an injected factory, so they can reach your repositories and services.
359
436
  */
360
437
  interface MediaDashboardAsyncOptions {
361
438
  basePath?: string;
@@ -364,110 +441,83 @@ interface MediaDashboardAsyncOptions {
364
441
  /** See {@link MediaDashboardOptions.objectUrls}. Static like the mount paths and `actions` —
365
442
  * it is read at module-definition time, not per request. */
366
443
  objectUrls?: ObjectUrlStrategy;
367
- /** Modules exporting the providers `inject` needs, or a guard class's own dependencies (omit
368
- * when they're global). Shared with {@link guards}. */
444
+ /** Modules exporting the providers `inject` needs, or a decorator guard's own dependencies (omit
445
+ * when they're global). */
369
446
  imports?: DynamicModule['imports'];
370
- /** Providers injected into `useAuth`, in order. */
447
+ /** Providers injected into `useObjectInsights`, in order. */
371
448
  inject?: Array<InjectionToken | OptionalFactoryDependency>;
372
- /** Build the `auth` config from injected deps (or `undefined` to leave the console open). */
373
- useAuth: (...deps: any[]) => ConsoleAuthOptions | undefined | Promise<ConsoleAuthOptions | undefined>;
374
- /**
375
- * Guard classes (or instances) fronting the console — see {@link MediaDashboardOptions.guards}.
376
- * Passed here directly (not resolved via `useAuth`'s factory) because guard stamping happens at
377
- * module-build time, synchronously — the SAME constraint as `basePath`/`apiBasePath` above. A
378
- * class guard's own dependencies resolve from `imports` above (shared with `useAuth`'s `inject`).
379
- */
380
- guards?: Array<Type<CanActivate> | CanActivate>;
381
449
  /**
382
450
  * Build the {@link MediaDashboardOptions.objectInsights} providers from injected deps — the usual
383
451
  * case, since a provider that says anything interesting needs a repository or a service.
384
452
  */
385
453
  useObjectInsights?: (...deps: any[]) => ObjectInsightProvider[] | Promise<ObjectInsightProvider[]>;
386
454
  /**
387
- * Providers injected into `useObjectInsights`, in order. Defaults to {@link inject} — its own
388
- * list because the insight providers rarely want the same dependencies the auth hooks do, and
389
- * sharing one list would force every host to inject the union of both.
455
+ * See {@link MediaDashboardOptions.decorators}. Static (not built by a factory): the decorators
456
+ * shape the routes when the module is built.
390
457
  */
391
- injectObjectInsights?: Array<InjectionToken | OptionalFactoryDependency>;
458
+ decorators?: ClassDecorator[];
459
+ /** See {@link MediaDashboardOptions.authorize}. Static, like `decorators`. */
460
+ authorize?: ConsoleAuthorize;
461
+ /** See {@link MediaDashboardOptions.routes}. Static, like `decorators`. */
462
+ routes?: boolean;
392
463
  }
393
464
  /**
394
465
  * Mounts the /media console: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
395
466
  * (default `<basePath>/api`). Import via `MediaDashboardModule.forRoot(...)` (or `forRootAsync` when
396
- * the `auth` hooks need injected services) alongside `MediaModule` (global), so it resolves the
397
- * storage/store/upload tokens. Exclude the UI/API paths from any global `/api` prefix.
467
+ * the `objectInsights` providers need injected services) alongside `MediaModule` (global), so it
468
+ * resolves the storage/store/upload tokens. Exclude the UI/API paths from any global `/api` prefix.
398
469
  *
399
- * Front it with your own auth via `guards` (+ `imports` for their dependencies) — see
400
- * {@link MediaDashboardOptions.guards} — instead of, or alongside, the built-in `auth` cookie login.
470
+ * Decide who may use it with {@link MediaDashboardOptions.authorize}; without it the console is open
471
+ * outside production and answers 403 to everything in production.
401
472
  */
402
473
  declare class MediaDashboardModule {
403
474
  static forRoot(options?: MediaDashboardOptions): DynamicModule;
404
- static forRootAsync(options: MediaDashboardAsyncOptions): DynamicModule;
405
- /** Shared wiring: static routing + the API module, with `auth` supplied by the given provider. */
475
+ static forRootAsync(options?: MediaDashboardAsyncOptions): DynamicModule;
476
+ /** Shared wiring: static routing + the API module, with the insights from the given provider. */
406
477
  private static build;
407
478
  }
408
479
 
409
- interface ApiModuleOptions {
480
+ interface MediaConsoleApiModuleOptions {
410
481
  actions: boolean;
411
- /** Cookie `Path` — the JSON API base — so the session cookie rides every console API request. */
412
- cookiePath: string;
413
- /** Provider for `MEDIA_CONSOLE_AUTH` — a `useValue` (forRoot) or a `useFactory` (forRootAsync). */
414
- authProvider: Provider;
415
482
  /** Provider for `MEDIA_CONSOLE_OBJECT_INSIGHTS` — the host's object-annotation providers.
416
483
  * Omitted binds the empty list, which is the no-op the console renders nothing for. */
417
484
  insightsProvider?: Provider;
418
485
  /** Resolved object-URL strategy + the API mount it builds proxy URLs against. */
419
486
  objectUrls: ObjectUrlConfig;
420
- /** Extra imports the auth factory's `inject` deps (or a guard class's own deps) live in. */
487
+ /** Extra imports the insights factory's `inject` deps (or a decorator's guards' deps) live in. */
421
488
  imports?: ModuleMetadata['imports'];
422
489
  /**
423
- * Guard classes (or instances) fronting the read + action controllers — APPENDED to their own
424
- * built-in `MediaConsoleGuard` (session-cookie) gate, so a request must pass BOTH. Deliberately
425
- * NOT applied to `MediaConsoleAuthController`: it MINTS the session cookie the built-in gate (and
426
- * any host guard reusing that same cookie) checks for, so it can't be made to require the very
427
- * auth it grants — mirrors `@dudousxd/nestjs-telescope`'s own auth controller staying outside its
428
- * `stampGuards` call.
490
+ * Who may use the console API — see `MediaDashboardOptions.authorize`. Omitted: open outside
491
+ * production, every request answers 403 in production, and one warning is logged at boot.
492
+ */
493
+ authorize?: ConsoleAuthorize;
494
+ /** The registration named in the boot warning when `authorize` is omitted. Default
495
+ * `'MediaConsoleApiModule.register()'`. */
496
+ moduleName?: string;
497
+ /**
498
+ * Class decorators applied to EVERY controller this module mounts (read, actions) — see
499
+ * `MediaDashboardOptions.decorators`. Each call decorates fresh subclasses, never the shared
500
+ * classes, so decorators never leak from one registration into the next.
429
501
  */
430
- guards?: Array<Type<CanActivate> | CanActivate>;
502
+ decorators?: ClassDecorator[];
503
+ /**
504
+ * `false` registers no controller, only the providers (exported), for a host that mounts the
505
+ * controllers itself — see `MediaDashboardOptions.routes`. Default `true`.
506
+ */
507
+ routes?: boolean;
431
508
  }
432
509
  /**
433
- * Holds the console's JSON API: the read controller (always), the actions controller (only when
434
- * `actions: true`), and the auth controller (always — it mints the session the guard checks, so it
435
- * is never itself guarded). The read/action controllers carry `MediaConsoleGuard`, a no-op unless
436
- * the host configured `auth`, plus any host `guards` (see {@link ApiModuleOptions.guards}) — both
437
- * gates must pass.
510
+ * Holds the console's JSON API: the read controller (always) and the actions controller (only when
511
+ * `actions: true`). Who reaches them is decided by `authorize` (`ConsoleAuthorizeGuard`).
438
512
  */
439
513
  declare class MediaConsoleApiModule {
440
- static register(options: ApiModuleOptions): DynamicModule;
514
+ static register(options: MediaConsoleApiModuleOptions): DynamicModule;
441
515
  }
442
516
 
443
517
  /**
444
- * A range as the CLIENT asked for it, before the object's size is known.
445
- *
446
- * Either an absolute {@link ReadRangeOptions}, or HTTP's suffix form (`Range: bytes=-500`, "the last
447
- * 500 bytes") — which cannot be made absolute without the size, and the size is `stat`'s to give.
448
- * Resolving it here rather than in the controller saves a second `stat` on the request path.
449
- */
450
- type RequestedReadRange = ReadRangeOptions | {
451
- suffixLength: number;
452
- };
453
- /** What {@link MediaConsoleService.objectStream} hands the controller. `range` is present only when
454
- * a range was requested AND satisfied, already absolute and clamped to the object — so the
455
- * controller can write `Content-Range`/`Content-Length` without re-deriving anything. */
456
- interface ObjectStreamResult {
457
- stream: Readable;
458
- contentType: string;
459
- /** Total size of the OBJECT, not of the slice — the `/<size>` in `Content-Range`. */
460
- size: number;
461
- /** Absolute inclusive bounds actually being streamed. Absent on a full-body read. */
462
- range?: {
463
- start: number;
464
- end: number;
465
- };
466
- }
467
- /**
468
- * Read + action logic behind the console's JSON API. Resolves the three by-value media tokens
469
- * with `@Optional()` so a host without a `MediaStore`/upload store still boots — every method
470
- * degrades to an empty shape instead of throwing when its capability is absent.
518
+ * Read + action logic behind the console's JSON API, on the disks of `@nestjs/storage` (`Storage`).
519
+ * Resolves the store, upload store and multipart side-cars with `@Optional()` so a host without
520
+ * them still boots — every method degrades to an empty shape instead of throwing.
471
521
  */
472
522
  declare class MediaConsoleService {
473
523
  private readonly storage;
@@ -476,16 +526,25 @@ declare class MediaConsoleService {
476
526
  private readonly actionsEnabled;
477
527
  private readonly insightProviders;
478
528
  private readonly objectUrls;
479
- constructor(storage: StorageManager | null, store: MediaStore | null, uploads: UploadSessionStore | null, actionsEnabled: boolean | null, insightProviders?: ObjectInsightProvider[] | null, objectUrls?: ObjectUrlConfig | null);
529
+ /** `MediaModuleOptions.multipart`: native folder listing and the multipart badge. */
530
+ private readonly multipart;
531
+ constructor(storage: Storage | null, store: MediaStore | null, uploads: UploadSessionStore | null, actionsEnabled: boolean | null, insightProviders?: ObjectInsightProvider[] | null, objectUrls?: ObjectUrlConfig | null,
532
+ /** `MediaModuleOptions.multipart`: native folder listing and the multipart badge. */
533
+ multipart?: MultipartDrivers | null);
534
+ private readonly logger;
480
535
  /**
481
536
  * The URL to report for an object's bytes, under the host's {@link ObjectUrlStrategy}. `auto`
482
- * prefers a presigned URL straight to the store; `proxy` routes through this server, for a host
483
- * whose browser cannot reach the store at all.
537
+ * prefers the disk's public URL, then a signed one (S3 presigns; a local disk signs with
538
+ * `signedUrls`), and falls back to this server's own route for a disk that can do neither.
539
+ * `proxy` always routes through this server.
484
540
  */
485
541
  private objectUrl;
486
- private readonly logger;
487
542
  private diskOrThrow;
488
- listDisks(): DiskListResponse;
543
+ listDisks(): Promise<DiskListResponse>;
544
+ /**
545
+ * One folder level. `StorageDisk.list()` is flat; a disk whose multipart side-car lists with a
546
+ * delimiter (S3) asks the store for folders, any other disk folds its flat listing.
547
+ */
489
548
  listObjects(disk: string, options: {
490
549
  prefix?: string;
491
550
  cursor?: string;
@@ -493,76 +552,55 @@ declare class MediaConsoleService {
493
552
  }): Promise<ObjectListResponse>;
494
553
  objectDetail(disk: string, key: string): Promise<ObjectDetailResponse>;
495
554
  /**
496
- * What the HOST knows about this object, from every registered provider.
497
- *
498
- * Its own call rather than a field on `objectDetail`, for the same reason the RAG index-status
499
- * endpoint is separate from its document listing: `objectDetail` is on the path of every preview
500
- * and every navigation, and providers reach into host databases. Folding them in would put a host
501
- * query on reads that only wanted a presigned URL.
502
- *
503
- * Failures are contained per provider. A provider that throws is logged and dropped, and the
504
- * others still render — annotation must never be able to stop an admin opening a file, and one
505
- * host lookup being down says nothing about the rest.
506
- *
507
- * Providers run concurrently: they are independent host lookups, and running them in series would
508
- * make the panel as slow as their sum for no gain.
555
+ * What the HOST knows about this object, from every registered provider. Its own call rather
556
+ * than a field on `objectDetail`, so host lookups stay off the preview path. A provider that
557
+ * throws is logged and dropped; providers run concurrently.
509
558
  */
510
559
  objectInsights(disk: string, key: string): Promise<ObjectInsightsResponse>;
511
560
  /**
512
- * The object's raw byte stream plus the metadata the controller needs to serve it inline. Used by
513
- * the console's same-origin preview proxy so text/PDF render in the browser instead of downloading
514
- * (and so a CORS-locked bucket is still previewable).
515
- *
516
- * With `range`, only that slice — which is what makes previewing a multi-hundred-MB object (a
517
- * SQLite database read a page at a time) possible at all. The range is resolved HERE, not in the
518
- * driver, because it has to be checked against the object's size and the `stat` for that is
519
- * already on this path: the suffix form is made absolute, an overrunning `end` is clamped to the
520
- * last byte, and anything genuinely unsatisfiable becomes a {@link RangeNotSatisfiableException}
521
- * (HTTP 416) instead of a surprise empty body.
522
- *
523
- * A driver that reports `ranged: false` REFUSES a range request rather than quietly returning the
524
- * whole object: the caller asked for 64 KB out of 400 MB, and silently answering with all 400 MB
525
- * — which the caller would then read as if it were the slice — is the exact failure the `ranged`
526
- * capability exists to make impossible.
561
+ * Streams an object as the response through `@nestjs/storage`'s `serveFile()`: a single `Range`
562
+ * gets a 206 with `Content-Range` (how a SQLite/ZIP/Parquet preview reads a few KB out of a huge
563
+ * object), `If-None-Match` a 304, a range past the end a 416, a missing file a 404. `inline`
564
+ * applies only to types that can't run script in this origin; others download.
565
+ */
566
+ serveObject(disk: string, key: string, options: ServeFileOptions): Promise<StreamableFile | undefined>;
567
+ /**
568
+ * Writes an object from a byte stream (a browser upload), streamed onto the disk: S3Disk turns a
569
+ * body of unknown length into a multipart upload itself, so nothing is buffered here. With the
570
+ * request's `Content-Length`, a body that ends short fails and nothing is written.
571
+ */
572
+ putObject(disk: string, key: string, body: Readable, contentType?: string, contentLength?: number): Promise<void>;
573
+ /**
574
+ * Creates a folder: a zero-byte `<prefix>/.keep` file (hidden from listings), since
575
+ * `@nestjs/storage` keys can't end with `/`. Listings show the folder because a key lives in it.
527
576
  */
528
- objectStream(disk: string, key: string, range?: RequestedReadRange): Promise<ObjectStreamResult>;
529
- /** Writes an object from a byte stream (a browser upload). The caller supplies the full key
530
- * (prefix + filename); an unknown disk 404s via {@link diskOrThrow}. Actions-gated.
531
- *
532
- * The request stream is buffered before the write: S3's PutObject needs a known Content-Length,
533
- * which a raw request stream doesn't carry, so passing the stream straight through fails. Bounded
534
- * at {@link MAX_UPLOAD_BYTES} so a runaway upload can't exhaust the pod's heap — larger files
535
- * belong on the resumable upload path, not this console convenience upload. */
536
- putObject(disk: string, key: string, body: Readable, contentType?: string): Promise<void>;
537
- /** Creates a "folder" — a zero-byte marker object at `<prefix>/`, which S3-style listing surfaces
538
- * as a navigable folder. Normalizes to exactly one trailing slash; rejects an empty prefix. */
539
577
  createFolder(disk: string, prefix: string): Promise<void>;
540
578
  deleteObject(disk: string, key: string): Promise<void>;
541
- /** Recursively deletes a "folder": every object under `<prefix>/` (nested included) plus the
542
- * zero-byte marker itself. Passes an empty delimiter so the driver lists keys FLAT — the default
543
- * `/` delimiter would group nested keys into CommonPrefixes and the sweep would miss them, only
544
- * deleting direct children. Paginates until the disk reports no more. The marker's own key equals
545
- * the sweep prefix, which `list` filters out (Key === Prefix), so it's deleted explicitly at the
546
- * end. Actions-gated. */
579
+ /**
580
+ * Recursively deletes a folder: every file under `<prefix>/`, nested included (its `.keep`
581
+ * too), a batch of keys per `delete()`. A legacy `prefix/` marker object written by another tool
582
+ * can't be addressed through `@nestjs/storage`; it is skipped.
583
+ */
547
584
  deleteFolder(disk: string, prefix: string): Promise<void>;
548
585
  copyObject(fromDisk: string, from: string, toDisk: string, to: string): Promise<void>;
549
586
  moveObject(fromDisk: string, from: string, toDisk: string, to: string): Promise<void>;
550
- /** Copies or moves a single object, on the same disk or across disks. Same-disk uses the driver's
551
- * native `copy`/`move` (server-side, no bytes through the pod). Cross-disk has no driver primitive,
552
- * so the object is streamed through the pod (get→put, buffered), preserving its content type and
553
- * bounded at {@link MAX_CROSS_DISK_BYTES}; a `move` then deletes the source. */
587
+ /**
588
+ * Copies or moves one object. Same disk: the disk's own `copy`/`move` (server-side on S3, a
589
+ * rename on a local disk). Across disks: the source's body streams into the destination's
590
+ * `put()` (no size limit, nothing buffered), keeping its content type, cache control,
591
+ * disposition and metadata; a `move` then deletes the source.
592
+ */
554
593
  private transferKey;
555
- /** Moves a whole "folder" (every object under `<from>/`, nested included) to `<to>/`, on the same
556
- * disk or across disks, preserving each key's path relative to the source. */
594
+ /** Moves a whole folder (every file under `<from>/`, nested included) to `<to>/`. */
557
595
  moveFolder(fromDisk: string, from: string, toDisk: string, to: string): Promise<void>;
558
- /** Copies a whole "folder" (every object under `<from>/`, nested included) to `<to>/`, same disk or
559
- * across disks, preserving each key's path relative to the source. */
596
+ /** Copies a whole folder (every file under `<from>/`, nested included) to `<to>/`. */
560
597
  copyFolder(fromDisk: string, from: string, toDisk: string, to: string): Promise<void>;
561
- /** Shared engine for {@link moveFolder}/{@link copyFolder}. Same flat-listing sweep as
562
- * {@link deleteFolder}; each key is relocated via {@link transferKey} (so cross-disk transfers work
563
- * transparently). On the SAME disk, rejects a destination inside the source (which would recurse
564
- * forever); across disks that constraint doesn't apply. The destination folder marker is written,
565
- * and for a move the source marker is removed, so both listings stay consistent. Actions-gated. */
598
+ /**
599
+ * Shared engine for {@link moveFolder}/{@link copyFolder}: a flat sweep of the source, each key
600
+ * relocated via {@link transferKey}, keeping its path relative to the folder (an empty folder's
601
+ * `.keep` goes along). On the SAME disk, a destination inside the source is refused (it would
602
+ * recurse forever).
603
+ */
566
604
  private transferFolder;
567
605
  listUploads(filter: {
568
606
  disk?: string;
@@ -589,11 +627,96 @@ declare class MediaConsoleService {
589
627
  topology(): Topology;
590
628
  }
591
629
 
630
+ /**
631
+ * Serves the bundled /media console SPA at the configured base (+ hashed assets at `<base>/assets`).
632
+ * The path comes from `RouterModule` (set by `MediaDashboardModule.forRoot({ basePath })`), so the
633
+ * controller routes are relative.
634
+ */
635
+ declare class MediaDashboardUiController {
636
+ private readonly basePath;
637
+ private readonly apiBasePath;
638
+ private readonly dir;
639
+ constructor(basePath: string, apiBasePath: string);
640
+ index(): string;
641
+ asset(file: string): StreamableFile;
642
+ }
643
+
644
+ /**
645
+ * Read-only JSON API for the /media console. Bare `@Controller()` — the path prefix is applied by
646
+ * `RouterModule` (set in `MediaDashboardModule.forRoot({ apiBasePath })`). Always mounted.
647
+ */
648
+ declare class MediaConsoleReadController {
649
+ private readonly service;
650
+ constructor(service: MediaConsoleService);
651
+ disks(): Promise<DiskListResponse>;
652
+ objects(disk: string, prefix?: string, cursor?: string, limit?: string): Promise<ObjectListResponse>;
653
+ object(disk: string, key: string): Promise<ObjectDetailResponse>;
654
+ /** What the host knows about this object, from its registered `objectInsights` providers. Empty
655
+ * when none are registered — the console renders nothing for an empty list. */
656
+ objectInsights(disk: string, key: string): Promise<ObjectInsightsResponse>;
657
+ /**
658
+ * Streams the object's bytes from the same origin (`serveFile()`), so the SPA can render previews
659
+ * past CORS: `inline` for types that can't run script here (images, PDF, audio, video, plain
660
+ * text), `attachment` for the rest — the text previews read the body with `fetch`, which doesn't
661
+ * care. A single `Range` gets a 206, which is how SQLite/ZIP/Parquet previews read a few KB out
662
+ * of a multi-hundred-MB object.
663
+ */
664
+ objectRaw(disk: string, key: string, req: unknown, res: unknown): Promise<StreamableFile | undefined>;
665
+ /**
666
+ * The same bytes as `/raw`, but as a SAVE: `Content-Disposition: attachment` with the object's
667
+ * own name. Same-origin and unconditional, so it works where the browser can't reach the store.
668
+ */
669
+ objectDownload(disk: string, key: string, req: unknown, res: unknown): Promise<StreamableFile | undefined>;
670
+ uploads(disk?: string, prefix?: string): Promise<UploadListResponse>;
671
+ upload(id: string): Promise<UploadDetailResponse>;
672
+ collections(): Promise<CollectionsResponse>;
673
+ library(collection?: string, disk?: string, cursor?: string, limit?: string): Promise<LibraryListResponse>;
674
+ libraryRecord(id: string): Promise<LibraryDetailResponse>;
675
+ topology(): Topology;
676
+ }
677
+
678
+ interface CopyMoveBody {
679
+ from: string;
680
+ to: string;
681
+ /** Destination disk for a cross-disk copy/move. Omitted → same disk as the source (`:disk`). */
682
+ toDisk?: string;
683
+ }
684
+ interface CreateFolderBody {
685
+ prefix: string;
686
+ }
687
+ /**
688
+ * Destructive JSON API for the /media console. Only registered when the host opts in with
689
+ * `MediaDashboardModule.forRoot({ actions: true })` (default off). Bare `@Controller()` — the path
690
+ * prefix comes from `RouterModule`, sharing the API base with the read controller.
691
+ */
692
+ declare class MediaConsoleActionsController {
693
+ private readonly service;
694
+ constructor(service: MediaConsoleService);
695
+ deleteObject(disk: string, key: string): Promise<void>;
696
+ copyObject(disk: string, body: CopyMoveBody): Promise<void>;
697
+ moveObject(disk: string, body: CopyMoveBody): Promise<void>;
698
+ moveFolder(disk: string, body: CopyMoveBody): Promise<void>;
699
+ copyFolder(disk: string, body: CopyMoveBody): Promise<void>;
700
+ /**
701
+ * Uploads a file to `key` on the disk. The body is the raw bytes sent as `application/octet-stream`
702
+ * so the host's JSON/urlencoded body parsers never consume the stream; the real MIME rides as the
703
+ * `type` query param and becomes the stored object's Content-Type. `@Req()` is the request stream,
704
+ * streamed onto the disk (no size cap, nothing buffered).
705
+ */
706
+ uploadObject(disk: string, key: string, request: Readable & {
707
+ headers?: Record<string, string | string[] | undefined>;
708
+ }, type?: string): Promise<void>;
709
+ createFolder(disk: string, body: CreateFolderBody): Promise<void>;
710
+ deleteFolder(disk: string, prefix: string): Promise<void>;
711
+ abortUpload(id: string): Promise<void>;
712
+ deleteLibraryRecord(id: string): Promise<void>;
713
+ }
714
+
592
715
  /** The configured `MediaStore` (or `null`), provided by `MediaModule`. */
593
716
  declare const MEDIA_STORE: symbol;
594
717
  /** The configured `UploadSessionStore` (or `null`), provided by `MediaModule`. */
595
718
  declare const MEDIA_UPLOAD_SESSIONS: symbol;
596
- /** The `StorageManager` (alias of `MediaModule`'s internal `MEDIA_STORAGE` token). */
597
- declare const MEDIA_STORAGE_SHARED: symbol;
719
+ /** The multipart side-cars by disk name (`MediaModuleOptions.multipart`), provided by `MediaModule`. */
720
+ declare const MEDIA_MULTIPART: symbol;
598
721
 
599
- export { type CollectionInfo, type CollectionsResponse, type ConsoleAuthOptions, type ConsoleSessionUser, type DiskCapabilities, type DiskInfo, type DiskListResponse, type LibraryDetailResponse, type LibraryListResponse, type LibraryRecord, type LibraryVariant, type LoginHook, MEDIA_STORAGE_SHARED, MEDIA_STORE, MEDIA_UPLOAD_SESSIONS, MediaConsoleApiModule, MediaConsoleService, type MediaDashboardAsyncOptions, MediaDashboardModule, type MediaDashboardOptions, type ObjectDetailResponse, type ObjectEntry, type ObjectFolder, type ObjectInsight, type ObjectInsightContext, type ObjectInsightFact, type ObjectInsightLink, type ObjectInsightProvider, type ObjectInsightsResponse, type ObjectListResponse, type ObjectUrlConfig, type ObjectUrlStrategy, type SessionHook, type Topology, type UploadDetailResponse, type UploadInfo, type UploadListResponse, type UploadPart };
722
+ export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, type CollectionInfo, type CollectionsResponse, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, type DiskCapabilities, type DiskInfo, type DiskListResponse, type LibraryDetailResponse, type LibraryListResponse, type LibraryRecord, type LibraryVariant, MEDIA_MULTIPART, MEDIA_STORE, MEDIA_UPLOAD_SESSIONS, MediaConsoleActionsController, MediaConsoleApiModule, type MediaConsoleApiModuleOptions, MediaConsoleReadController, MediaConsoleService, type MediaDashboardAsyncOptions, MediaDashboardModule, type MediaDashboardOptions, MediaDashboardUiController, type ObjectDetailResponse, type ObjectEntry, type ObjectFolder, type ObjectInsight, type ObjectInsightContext, type ObjectInsightFact, type ObjectInsightLink, type ObjectInsightProvider, type ObjectInsightsResponse, type ObjectListResponse, type ObjectUrlConfig, type ObjectUrlStrategy, type Topology, type UploadDetailResponse, type UploadInfo, type UploadListResponse, type UploadPart, UseConsoleAuthorize, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };