@ai-matrx/media 0.9.0 → 0.9.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,11 @@
|
|
|
1
1
|
# @ai-matrx/media
|
|
2
2
|
|
|
3
|
+
## 0.9.1
|
|
4
|
+
|
|
5
|
+
`configureFilesHost` keeps the host on a `globalThis` `Symbol.for("ai-matrx.media.files-host")`
|
|
6
|
+
slot instead of module scope, so a test runner's module reset (or a second loader graph) still
|
|
7
|
+
sees the one configured host. No consumer action required.
|
|
8
|
+
|
|
3
9
|
## 0.9.0
|
|
4
10
|
|
|
5
11
|
New subpath `@ai-matrx/media/files/engine` plus one subpath per engine module
|
|
@@ -6,19 +6,20 @@ class FilesHostNotConfiguredError extends Error {
|
|
|
6
6
|
this.name = "FilesHostNotConfiguredError";
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
const SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.files-host");
|
|
10
10
|
function configureFilesHost(next) {
|
|
11
|
-
|
|
11
|
+
globalThis[SLOT] = next;
|
|
12
12
|
}
|
|
13
13
|
function isFilesHostConfigured() {
|
|
14
|
-
return
|
|
14
|
+
return Boolean(globalThis[SLOT]);
|
|
15
15
|
}
|
|
16
16
|
function getFilesHost(member) {
|
|
17
|
+
const host = globalThis[SLOT];
|
|
17
18
|
if (!host) throw new FilesHostNotConfiguredError(member);
|
|
18
19
|
return host;
|
|
19
20
|
}
|
|
20
21
|
function __resetFilesHostForTests() {
|
|
21
|
-
|
|
22
|
+
globalThis[SLOT] = null;
|
|
22
23
|
}
|
|
23
24
|
export {
|
|
24
25
|
FilesHostNotConfiguredError,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/files/engine/host/configure.ts"],"sourcesContent":["/**\n * The files engine's host port — the ONE place a client hands the engine what\n * only it can own: its database client, its AI Matrx server client, its Redux\n * store, its share-link calls, its organization chooser and its notifications.\n *\n * Everything else the engine needs (the slice, thunks, middlewares, uploads,\n * the resolver, the typed REST verbs, session retry, claims) is package code.\n *\n * Configure once, before the store is created:\n *\n * configureFilesHost({ db, server, store: () => store, ... });\n *\n * Every seam module reads the host at CALL time, never at import time, so the\n * order of module loading never matters. Used before configuration, a call\n * throws `FilesHostNotConfiguredError`, which names the remedy.\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\nexport interface FilesRequestOptions {\n requestId?: string;\n signal?: AbortSignal;\n baseUrlOverride?: string;\n timeoutMs?: number;\n captureErrors?: boolean;\n guestFingerprint?: string;\n idempotencyKey?: string;\n organizationId?: string;\n bodyCarriedRead?: boolean;\n cloudFilesBypass?: string;\n}\n\nexport interface FilesResponseMeta {\n requestId: string;\n status: number;\n serverRequestId: string | null;\n}\n\nexport interface FilesUploadProgressEvent {\n loaded: number;\n total: number;\n}\n\nexport interface FilesDownloadProgressEvent {\n loaded: number;\n total: number | null;\n}\n\ntype Envelope<T> = Promise<{ data: T; meta: FilesResponseMeta }>;\ntype BlobEnvelope = Promise<{ blob: Blob; meta: FilesResponseMeta; filename: string | null }>;\n\n/** The host's AI Matrx server client (matrx-frontend: `lib/python-client`). */\nexport interface FilesServerClient {\n resolveFilesBaseUrl(override?: string): string;\n resolveBaseUrlForPath(path: string, override?: string, method?: string): string;\n buildHeaders(\n opts: FilesRequestOptions,\n includeContentType: boolean,\n method?: string,\n ): Promise<{ headers: Record<string, string>; requestId: string }>;\n getJson<T>(path: string, opts?: FilesRequestOptions): Envelope<T>;\n postJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n patchJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n putJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n del<T = null>(path: string, opts?: FilesRequestOptions): Promise<{ data: T | null; meta: FilesResponseMeta }>;\n delJson<T = null, B = unknown>(\n path: string,\n body: B,\n opts?: FilesRequestOptions,\n ): Promise<{ data: T | null; meta: FilesResponseMeta }>;\n postMultipart<T>(path: string, form: FormData, opts?: FilesRequestOptions): Envelope<T>;\n uploadWithProgress<T>(\n path: string,\n form: FormData,\n onProgress: (event: FilesUploadProgressEvent) => void,\n opts?: FilesRequestOptions,\n ): Envelope<T>;\n downloadBlob(path: string, opts?: FilesRequestOptions): BlobEnvelope;\n downloadBlobWithProgress(\n path: string,\n onProgress: (event: FilesDownloadProgressEvent) => void,\n opts?: FilesRequestOptions,\n ): BlobEnvelope;\n}\n\nexport interface FilesShareLink {\n id: string;\n token: string;\n shortToken: string | null;\n permissionLevel: string;\n label: string | null;\n expiresAt: string | null;\n maxUses: number | null;\n useCount: number;\n isActive: boolean;\n createdAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface FilesCreateShareLinkOptions {\n resourceType: string;\n resourceId: string;\n permissionLevel?: string;\n expiresAt?: string | null;\n maxUses?: number | null;\n label?: string | null;\n}\n\n/** The host's share-link store (matrx-frontend: `utils/permissions/shareLinks`). */\nexport interface FilesShareLinks {\n shareLinkUrl(token: string): string;\n createShareLink(options: FilesCreateShareLinkOptions): Promise<{\n success: boolean;\n token?: string;\n shortToken?: string | null;\n url?: string;\n error?: string;\n }>;\n listShareLinks(resourceType: string, resourceId: string): Promise<FilesShareLink[]>;\n revokeShareLink(linkId: string): Promise<{ success: boolean; error?: string }>;\n}\n\n/** The store the engine's slice is mounted in (under `cloudFiles`). */\nexport interface FilesStore {\n getState(): unknown;\n dispatch(action: unknown): unknown;\n}\n\n/** Where new uploads land by default: the person's current organization / project / task. */\nexport interface FilesScope {\n /** The signed-in person (ownership checks). */\n userId?: string | null;\n organizationId: string | null;\n projectId: string | null;\n taskId: string | null;\n}\n\nexport interface FilesNotifyPort {\n success(message: string, options?: { description?: string; id?: string | number; duration?: number }): void;\n error(message: string, options?: { description?: string; id?: string | number; duration?: number }): void;\n}\n\nexport interface FilesHost {\n /** A supabase-js client for the signed-in person (the engine reads the `files` and `iam` schemas). */\n db: SupabaseClient<any, any, any>;\n server: FilesServerClient;\n /** The store the `cloudFiles` reducer is mounted in; null until it exists. */\n store(): FilesStore | null;\n /** The person's current organization / project / task, read from `state`. Default: none. */\n scope?(state: unknown): FilesScope;\n /**\n * Resolve the organization a write lands in. Given a declared id, return it;\n * otherwise ask the person (the host's chooser). Default: the declared id,\n * else the scope's organization, else a named error.\n */\n ensureOrganizationContext?(options: { organizationId?: string | null }): Promise<string>;\n shareLinks?: FilesShareLinks;\n notify?: FilesNotifyPort;\n}\n\nexport class FilesHostNotConfiguredError extends Error {\n constructor(member: string) {\n super(\n `@ai-matrx/media/files-engine: \"${member}\" was used before configureFilesHost(...) ran. ` +\n \"Call configureFilesHost({ db, server, store }) once at startup, before the store is created.\",\n );\n this.name = \"FilesHostNotConfiguredError\";\n }\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../../../src/files/engine/host/configure.ts"],"sourcesContent":["/**\n * The files engine's host port — the ONE place a client hands the engine what\n * only it can own: its database client, its AI Matrx server client, its Redux\n * store, its share-link calls, its organization chooser and its notifications.\n *\n * Everything else the engine needs (the slice, thunks, middlewares, uploads,\n * the resolver, the typed REST verbs, session retry, claims) is package code.\n *\n * Configure once, before the store is created:\n *\n * configureFilesHost({ db, server, store: () => store, ... });\n *\n * Every seam module reads the host at CALL time, never at import time, so the\n * order of module loading never matters. Used before configuration, a call\n * throws `FilesHostNotConfiguredError`, which names the remedy.\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\nexport interface FilesRequestOptions {\n requestId?: string;\n signal?: AbortSignal;\n baseUrlOverride?: string;\n timeoutMs?: number;\n captureErrors?: boolean;\n guestFingerprint?: string;\n idempotencyKey?: string;\n organizationId?: string;\n bodyCarriedRead?: boolean;\n cloudFilesBypass?: string;\n}\n\nexport interface FilesResponseMeta {\n requestId: string;\n status: number;\n serverRequestId: string | null;\n}\n\nexport interface FilesUploadProgressEvent {\n loaded: number;\n total: number;\n}\n\nexport interface FilesDownloadProgressEvent {\n loaded: number;\n total: number | null;\n}\n\ntype Envelope<T> = Promise<{ data: T; meta: FilesResponseMeta }>;\ntype BlobEnvelope = Promise<{ blob: Blob; meta: FilesResponseMeta; filename: string | null }>;\n\n/** The host's AI Matrx server client (matrx-frontend: `lib/python-client`). */\nexport interface FilesServerClient {\n resolveFilesBaseUrl(override?: string): string;\n resolveBaseUrlForPath(path: string, override?: string, method?: string): string;\n buildHeaders(\n opts: FilesRequestOptions,\n includeContentType: boolean,\n method?: string,\n ): Promise<{ headers: Record<string, string>; requestId: string }>;\n getJson<T>(path: string, opts?: FilesRequestOptions): Envelope<T>;\n postJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n patchJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n putJson<T, B = unknown>(path: string, body: B, opts?: FilesRequestOptions): Envelope<T>;\n del<T = null>(path: string, opts?: FilesRequestOptions): Promise<{ data: T | null; meta: FilesResponseMeta }>;\n delJson<T = null, B = unknown>(\n path: string,\n body: B,\n opts?: FilesRequestOptions,\n ): Promise<{ data: T | null; meta: FilesResponseMeta }>;\n postMultipart<T>(path: string, form: FormData, opts?: FilesRequestOptions): Envelope<T>;\n uploadWithProgress<T>(\n path: string,\n form: FormData,\n onProgress: (event: FilesUploadProgressEvent) => void,\n opts?: FilesRequestOptions,\n ): Envelope<T>;\n downloadBlob(path: string, opts?: FilesRequestOptions): BlobEnvelope;\n downloadBlobWithProgress(\n path: string,\n onProgress: (event: FilesDownloadProgressEvent) => void,\n opts?: FilesRequestOptions,\n ): BlobEnvelope;\n}\n\nexport interface FilesShareLink {\n id: string;\n token: string;\n shortToken: string | null;\n permissionLevel: string;\n label: string | null;\n expiresAt: string | null;\n maxUses: number | null;\n useCount: number;\n isActive: boolean;\n createdAt: string | null;\n lastUsedAt: string | null;\n}\n\nexport interface FilesCreateShareLinkOptions {\n resourceType: string;\n resourceId: string;\n permissionLevel?: string;\n expiresAt?: string | null;\n maxUses?: number | null;\n label?: string | null;\n}\n\n/** The host's share-link store (matrx-frontend: `utils/permissions/shareLinks`). */\nexport interface FilesShareLinks {\n shareLinkUrl(token: string): string;\n createShareLink(options: FilesCreateShareLinkOptions): Promise<{\n success: boolean;\n token?: string;\n shortToken?: string | null;\n url?: string;\n error?: string;\n }>;\n listShareLinks(resourceType: string, resourceId: string): Promise<FilesShareLink[]>;\n revokeShareLink(linkId: string): Promise<{ success: boolean; error?: string }>;\n}\n\n/** The store the engine's slice is mounted in (under `cloudFiles`). */\nexport interface FilesStore {\n getState(): unknown;\n dispatch(action: unknown): unknown;\n}\n\n/** Where new uploads land by default: the person's current organization / project / task. */\nexport interface FilesScope {\n /** The signed-in person (ownership checks). */\n userId?: string | null;\n organizationId: string | null;\n projectId: string | null;\n taskId: string | null;\n}\n\nexport interface FilesNotifyPort {\n success(message: string, options?: { description?: string; id?: string | number; duration?: number }): void;\n error(message: string, options?: { description?: string; id?: string | number; duration?: number }): void;\n}\n\nexport interface FilesHost {\n /** A supabase-js client for the signed-in person (the engine reads the `files` and `iam` schemas). */\n db: SupabaseClient<any, any, any>;\n server: FilesServerClient;\n /** The store the `cloudFiles` reducer is mounted in; null until it exists. */\n store(): FilesStore | null;\n /** The person's current organization / project / task, read from `state`. Default: none. */\n scope?(state: unknown): FilesScope;\n /**\n * Resolve the organization a write lands in. Given a declared id, return it;\n * otherwise ask the person (the host's chooser). Default: the declared id,\n * else the scope's organization, else a named error.\n */\n ensureOrganizationContext?(options: { organizationId?: string | null }): Promise<string>;\n shareLinks?: FilesShareLinks;\n notify?: FilesNotifyPort;\n}\n\nexport class FilesHostNotConfiguredError extends Error {\n constructor(member: string) {\n super(\n `@ai-matrx/media/files-engine: \"${member}\" was used before configureFilesHost(...) ran. ` +\n \"Call configureFilesHost({ db, server, store }) once at startup, before the store is created.\",\n );\n this.name = \"FilesHostNotConfiguredError\";\n }\n}\n\n// The host lives on a globalThis Symbol.for slot, not module scope: a test\n// runner's module reset, or a second copy of this module in another loader\n// graph, must still see the ONE host the app configured.\nconst SLOT = Symbol.for(\"ai-matrx.media.files-host\");\ntype Slot = { [SLOT]?: FilesHost | null };\n\nexport function configureFilesHost(next: FilesHost): void {\n (globalThis as Slot)[SLOT] = next;\n}\n\nexport function isFilesHostConfigured(): boolean {\n return Boolean((globalThis as Slot)[SLOT]);\n}\n\nexport function getFilesHost(member: string): FilesHost {\n const host = (globalThis as Slot)[SLOT];\n if (!host) throw new FilesHostNotConfiguredError(member);\n return host;\n}\n\n/** For tests: drop the configured host. */\nexport function __resetFilesHostForTests(): void {\n (globalThis as Slot)[SLOT] = null;\n}\n"],"mappings":"AAgKO,MAAM,oCAAoC,MAAM;AAAA,EACrD,YAAY,QAAgB;AAC1B;AAAA,MACE,kCAAkC,MAAM;AAAA,IAE1C;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAKA,MAAM,OAAO,uBAAO,IAAI,2BAA2B;AAG5C,SAAS,mBAAmB,MAAuB;AACxD,EAAC,WAAoB,IAAI,IAAI;AAC/B;AAEO,SAAS,wBAAiC;AAC/C,SAAO,QAAS,WAAoB,IAAI,CAAC;AAC3C;AAEO,SAAS,aAAa,QAA2B;AACtD,QAAM,OAAQ,WAAoB,IAAI;AACtC,MAAI,CAAC,KAAM,OAAM,IAAI,4BAA4B,MAAM;AACvD,SAAO;AACT;AAGO,SAAS,2BAAiC;AAC/C,EAAC,WAAoB,IAAI,IAAI;AAC/B;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/media",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "The AI Matrx media components kit: durable-ref-only renderers for images, video, audio and file thumbnails plus live TTS playback that just work with the Matrx file system — headless core hooks plus DOM bindings, wired to an injected MediaClient. A signed URL is a handoff, never an identity.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -505,11 +505,11 @@
|
|
|
505
505
|
"tailwind-merge": "^3.7.0",
|
|
506
506
|
"tus-js-client": "^4.3.1",
|
|
507
507
|
"@ai-matrx/associations": "latest",
|
|
508
|
-
"@ai-matrx/
|
|
508
|
+
"@ai-matrx/data": "latest",
|
|
509
509
|
"@ai-matrx/design-system": "latest",
|
|
510
510
|
"@ai-matrx/kit": "latest",
|
|
511
|
-
"@ai-matrx/
|
|
512
|
-
"@ai-matrx/
|
|
511
|
+
"@ai-matrx/agents": "latest",
|
|
512
|
+
"@ai-matrx/realtime": "latest"
|
|
513
513
|
},
|
|
514
514
|
"peerDependencies": {
|
|
515
515
|
"@reduxjs/toolkit": ">=2.0.0",
|