@anvilkit/plugin-asset-manager 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -19
- package/dist/adapters/data-url.cjs +9 -5
- package/dist/adapters/extract-image-dimensions.cjs +12 -8
- package/dist/adapters/in-memory.cjs +9 -5
- package/dist/adapters/s3-presigned.cjs +9 -5
- package/dist/index.cjs +16 -5
- package/dist/index.d.cts +11 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/plugin.cjs +76 -9
- package/dist/plugin.d.cts +3 -2
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +67 -4
- package/dist/sources/composite-source.cjs +137 -0
- package/dist/sources/composite-source.d.cts +39 -0
- package/dist/sources/composite-source.d.cts.map +1 -0
- package/dist/sources/composite-source.d.ts +39 -0
- package/dist/sources/composite-source.d.ts.map +1 -0
- package/dist/sources/composite-source.js +99 -0
- package/dist/sources/federated-search.cjs +163 -0
- package/dist/sources/federated-search.d.cts +33 -0
- package/dist/sources/federated-search.d.cts.map +1 -0
- package/dist/sources/federated-search.d.ts +33 -0
- package/dist/sources/federated-search.d.ts.map +1 -0
- package/dist/sources/federated-search.js +113 -0
- package/dist/sources/provider.cjs +18 -0
- package/dist/sources/provider.d.cts +51 -0
- package/dist/sources/provider.d.cts.map +1 -0
- package/dist/sources/provider.d.ts +51 -0
- package/dist/sources/provider.d.ts.map +1 -0
- package/dist/sources/provider.js +1 -0
- package/dist/sources/unsplash/client.cjs +189 -0
- package/dist/sources/unsplash/client.d.cts +87 -0
- package/dist/sources/unsplash/client.d.cts.map +1 -0
- package/dist/sources/unsplash/client.d.ts +87 -0
- package/dist/sources/unsplash/client.d.ts.map +1 -0
- package/dist/sources/unsplash/client.js +151 -0
- package/dist/sources/unsplash/index.cjs +192 -0
- package/dist/sources/unsplash/index.d.cts +16 -0
- package/dist/sources/unsplash/index.d.cts.map +1 -0
- package/dist/sources/unsplash/index.d.ts +16 -0
- package/dist/sources/unsplash/index.d.ts.map +1 -0
- package/dist/sources/unsplash/index.js +148 -0
- package/dist/sources/unsplash/themes.cjs +141 -0
- package/dist/sources/unsplash/themes.d.cts +18 -0
- package/dist/sources/unsplash/themes.d.cts.map +1 -0
- package/dist/sources/unsplash/themes.d.ts +18 -0
- package/dist/sources/unsplash/themes.d.ts.map +1 -0
- package/dist/sources/unsplash/themes.js +93 -0
- package/dist/sources/unsplash/throttle-cache.cjs +86 -0
- package/dist/sources/unsplash/throttle-cache.d.cts +25 -0
- package/dist/sources/unsplash/throttle-cache.d.cts.map +1 -0
- package/dist/sources/unsplash/throttle-cache.d.ts +25 -0
- package/dist/sources/unsplash/throttle-cache.d.ts.map +1 -0
- package/dist/sources/unsplash/throttle-cache.js +45 -0
- package/dist/testing/index.cjs +9 -5
- package/dist/types/categories.cjs +18 -0
- package/dist/types/categories.d.cts +48 -0
- package/dist/types/categories.d.cts.map +1 -0
- package/dist/types/categories.d.ts +48 -0
- package/dist/types/categories.d.ts.map +1 -0
- package/dist/types/categories.js +1 -0
- package/dist/types/data-source.cjs +18 -0
- package/dist/types/data-source.d.cts +59 -0
- package/dist/types/data-source.d.cts.map +1 -0
- package/dist/types/data-source.d.ts +59 -0
- package/dist/types/data-source.d.ts.map +1 -0
- package/dist/types/data-source.js +1 -0
- package/dist/types/filter.cjs +18 -0
- package/dist/types/filter.d.cts +55 -0
- package/dist/types/filter.d.cts.map +1 -0
- package/dist/types/filter.d.ts +55 -0
- package/dist/types/filter.d.ts.map +1 -0
- package/dist/types/filter.js +1 -0
- package/dist/types/folders.cjs +42 -0
- package/dist/types/folders.d.cts +46 -0
- package/dist/types/folders.d.cts.map +1 -0
- package/dist/types/folders.d.ts +46 -0
- package/dist/types/folders.d.ts.map +1 -0
- package/dist/types/folders.js +4 -0
- package/dist/types/options.cjs +18 -0
- package/dist/types/options.d.cts +68 -0
- package/dist/types/options.d.cts.map +1 -0
- package/dist/types/options.d.ts +68 -0
- package/dist/types/options.d.ts.map +1 -0
- package/dist/types/options.js +1 -0
- package/dist/types/types.d.cts +15 -27
- package/dist/types/types.d.cts.map +1 -1
- package/dist/types/types.d.ts +15 -27
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/unsplash.cjs +18 -0
- package/dist/types/unsplash.d.cts +60 -0
- package/dist/types/unsplash.d.cts.map +1 -0
- package/dist/types/unsplash.d.ts +60 -0
- package/dist/types/unsplash.d.ts.map +1 -0
- package/dist/types/unsplash.js +1 -0
- package/dist/ui/AssetBrowser.cjs +69 -104
- package/dist/ui/AssetBrowser.d.cts +17 -7
- package/dist/ui/AssetBrowser.d.cts.map +1 -1
- package/dist/ui/AssetBrowser.d.ts +17 -7
- package/dist/ui/AssetBrowser.d.ts.map +1 -1
- package/dist/ui/AssetBrowser.js +60 -99
- package/dist/ui/AssetCommandPalette.cjs +9 -5
- package/dist/ui/AssetManagerUI.cjs +17 -7
- package/dist/ui/AssetManagerUI.d.cts +19 -3
- package/dist/ui/AssetManagerUI.d.cts.map +1 -1
- package/dist/ui/AssetManagerUI.d.ts +19 -3
- package/dist/ui/AssetManagerUI.d.ts.map +1 -1
- package/dist/ui/AssetManagerUI.js +8 -2
- package/dist/ui/DeleteAssetDialog.cjs +9 -5
- package/dist/ui/DeleteFolderDialog.cjs +78 -0
- package/dist/ui/DeleteFolderDialog.d.cts +11 -0
- package/dist/ui/DeleteFolderDialog.d.cts.map +1 -0
- package/dist/ui/DeleteFolderDialog.d.ts +11 -0
- package/dist/ui/DeleteFolderDialog.d.ts.map +1 -0
- package/dist/ui/DeleteFolderDialog.js +40 -0
- package/dist/ui/EmptyFolderState.cjs +53 -0
- package/dist/ui/EmptyFolderState.d.cts +6 -0
- package/dist/ui/EmptyFolderState.d.cts.map +1 -0
- package/dist/ui/EmptyFolderState.d.ts +6 -0
- package/dist/ui/EmptyFolderState.d.ts.map +1 -0
- package/dist/ui/EmptyFolderState.js +15 -0
- package/dist/ui/FolderBreadcrumb.cjs +73 -0
- package/dist/ui/FolderBreadcrumb.d.cts +9 -0
- package/dist/ui/FolderBreadcrumb.d.cts.map +1 -0
- package/dist/ui/FolderBreadcrumb.d.ts +9 -0
- package/dist/ui/FolderBreadcrumb.d.ts.map +1 -0
- package/dist/ui/FolderBreadcrumb.js +35 -0
- package/dist/ui/FolderNameDialog.cjs +98 -0
- package/dist/ui/FolderNameDialog.d.cts +14 -0
- package/dist/ui/FolderNameDialog.d.cts.map +1 -0
- package/dist/ui/FolderNameDialog.d.ts +14 -0
- package/dist/ui/FolderNameDialog.d.ts.map +1 -0
- package/dist/ui/FolderNameDialog.js +60 -0
- package/dist/ui/FolderTree.cjs +83 -0
- package/dist/ui/FolderTree.d.cts +13 -0
- package/dist/ui/FolderTree.d.cts.map +1 -0
- package/dist/ui/FolderTree.d.ts +13 -0
- package/dist/ui/FolderTree.d.ts.map +1 -0
- package/dist/ui/FolderTree.js +42 -0
- package/dist/ui/MetadataPanel.cjs +16 -9
- package/dist/ui/MetadataPanel.d.cts.map +1 -1
- package/dist/ui/MetadataPanel.d.ts.map +1 -1
- package/dist/ui/MetadataPanel.js +7 -4
- package/dist/ui/MoveTargetPicker.cjs +84 -0
- package/dist/ui/MoveTargetPicker.d.cts +16 -0
- package/dist/ui/MoveTargetPicker.d.cts.map +1 -0
- package/dist/ui/MoveTargetPicker.d.ts +16 -0
- package/dist/ui/MoveTargetPicker.d.ts.map +1 -0
- package/dist/ui/MoveTargetPicker.js +46 -0
- package/dist/ui/ReplaceAssetDialog.cjs +9 -5
- package/dist/ui/ReplaceAssetDialog.d.cts +2 -1
- package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -1
- package/dist/ui/ReplaceAssetDialog.d.ts +2 -1
- package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -1
- package/dist/ui/UnsplashPanel.cjs +134 -0
- package/dist/ui/UnsplashPanel.d.cts +28 -0
- package/dist/ui/UnsplashPanel.d.cts.map +1 -0
- package/dist/ui/UnsplashPanel.d.ts +28 -0
- package/dist/ui/UnsplashPanel.d.ts.map +1 -0
- package/dist/ui/UnsplashPanel.js +96 -0
- package/dist/ui/UploadButton.cjs +10 -10
- package/dist/ui/UploadButton.d.cts +9 -2
- package/dist/ui/UploadButton.d.cts.map +1 -1
- package/dist/ui/UploadButton.d.ts +9 -2
- package/dist/ui/UploadButton.d.ts.map +1 -1
- package/dist/ui/UploadButton.js +1 -5
- package/dist/ui/index.cjs +46 -5
- package/dist/ui/index.d.cts +14 -0
- package/dist/ui/index.d.cts.map +1 -1
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +7 -0
- package/dist/utils/asset-reference.cjs +12 -8
- package/dist/utils/csp.cjs +12 -8
- package/dist/utils/data-source.cjs +177 -0
- package/dist/utils/data-source.d.cts +63 -0
- package/dist/utils/data-source.d.cts.map +1 -0
- package/dist/utils/data-source.d.ts +63 -0
- package/dist/utils/data-source.d.ts.map +1 -0
- package/dist/utils/data-source.js +136 -0
- package/dist/utils/errors.cjs +31 -9
- package/dist/utils/errors.d.cts +27 -0
- package/dist/utils/errors.d.cts.map +1 -1
- package/dist/utils/errors.d.ts +27 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +16 -1
- package/dist/utils/folders.cjs +261 -0
- package/dist/utils/folders.d.cts +49 -0
- package/dist/utils/folders.d.cts.map +1 -0
- package/dist/utils/folders.d.ts +49 -0
- package/dist/utils/folders.d.ts.map +1 -0
- package/dist/utils/folders.js +223 -0
- package/dist/utils/header-action.cjs +12 -8
- package/dist/utils/infer-kind.cjs +12 -8
- package/dist/utils/registry.cjs +36 -17
- package/dist/utils/registry.d.cts +21 -1
- package/dist/utils/registry.d.cts.map +1 -1
- package/dist/utils/registry.d.ts +21 -1
- package/dist/utils/registry.d.ts.map +1 -1
- package/dist/utils/registry.js +20 -11
- package/dist/utils/resolver.cjs +9 -5
- package/dist/utils/retry.cjs +13 -9
- package/dist/utils/studio-asset-source.cjs +14 -7
- package/dist/utils/studio-asset-source.d.cts +6 -1
- package/dist/utils/studio-asset-source.d.cts.map +1 -1
- package/dist/utils/studio-asset-source.d.ts +6 -1
- package/dist/utils/studio-asset-source.d.ts.map +1 -1
- package/dist/utils/studio-asset-source.js +1 -1
- package/dist/utils/validate-upload-result.cjs +9 -5
- package/dist/utils/validate-upload-result.d.cts +2 -1
- package/dist/utils/validate-upload-result.d.cts.map +1 -1
- package/dist/utils/validate-upload-result.d.ts +2 -1
- package/dist/utils/validate-upload-result.d.ts.map +1 -1
- package/dist/version.cjs +12 -8
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/meta/config.json +1 -1
- package/package.json +19 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federated-search.d.ts","sourceRoot":"","sources":["../../src/sources/federated-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAY1D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAErE;AAED,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,eAAe,CAUjB;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,mBAAmB,EAC7B,MAAM,EAAE,WAAW,GACjB,OAAO,CAKT;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,uBAAuB,EAC/B,QAAQ,EAAE,aAAa,EACvB,KAAK,SAAgC,GACnC,mBAAmB,CAqBrB;AA+DD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,oBAAoB,GACzB,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { inferAssetKind } from "../utils/infer-kind.js";
|
|
2
|
+
function toBase64Url(json) {
|
|
3
|
+
return btoa(json).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
4
|
+
}
|
|
5
|
+
function fromBase64Url(token) {
|
|
6
|
+
const padded = token.length % 4 === 0 ? token : token + "=".repeat(4 - token.length % 4);
|
|
7
|
+
return atob(padded.replace(/-/g, "+").replace(/_/g, "/"));
|
|
8
|
+
}
|
|
9
|
+
function encodeCompositeCursor(cursor) {
|
|
10
|
+
return toBase64Url(JSON.stringify(cursor));
|
|
11
|
+
}
|
|
12
|
+
function decodeCompositeCursor(token) {
|
|
13
|
+
if (void 0 === token || "" === token) return {};
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(fromBase64Url(token));
|
|
16
|
+
return "object" == typeof parsed && null !== parsed ? parsed : {};
|
|
17
|
+
} catch {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function providerCanSatisfy(provider, filter) {
|
|
22
|
+
if (void 0 !== filter.folderId && true !== provider.capabilities.folders) return false;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function createLocalProvider(source, registry, label = "assetManager.source.library") {
|
|
26
|
+
return {
|
|
27
|
+
id: "local",
|
|
28
|
+
label,
|
|
29
|
+
capabilities: {
|
|
30
|
+
searchable: true,
|
|
31
|
+
themed: false,
|
|
32
|
+
mutable: true,
|
|
33
|
+
requiresAttribution: false,
|
|
34
|
+
folders: true
|
|
35
|
+
},
|
|
36
|
+
listThemes: ()=>[],
|
|
37
|
+
search: (filter, page, signal)=>source.list(void 0 !== page ? {
|
|
38
|
+
...filter,
|
|
39
|
+
cursor: page
|
|
40
|
+
} : filter, signal),
|
|
41
|
+
pickResult: async (asset)=>registry.get(asset.id) ?? {
|
|
42
|
+
id: asset.id,
|
|
43
|
+
url: asset.url
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function sortKey(entry, field) {
|
|
48
|
+
switch(field){
|
|
49
|
+
case "name":
|
|
50
|
+
return (entry.name ?? "").toLowerCase();
|
|
51
|
+
case "size":
|
|
52
|
+
return entry.meta?.size ?? 0;
|
|
53
|
+
case "kind":
|
|
54
|
+
return inferAssetKind(entry);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function compareEntries(a, b, field) {
|
|
58
|
+
const ka = sortKey(a, field);
|
|
59
|
+
const kb = sortKey(b, field);
|
|
60
|
+
if ("number" == typeof ka && "number" == typeof kb) return ka - kb;
|
|
61
|
+
return String(ka).localeCompare(String(kb));
|
|
62
|
+
}
|
|
63
|
+
function mergePages(pages, filter) {
|
|
64
|
+
const field = filter.sort?.field ?? "recent";
|
|
65
|
+
const comparable = "name" === field || "size" === field || "kind" === field;
|
|
66
|
+
const items = pages.flatMap((p)=>[
|
|
67
|
+
...p.page.items
|
|
68
|
+
]);
|
|
69
|
+
if (comparable) {
|
|
70
|
+
const dir = filter.sort?.direction ?? ("name" === field ? "asc" : "desc");
|
|
71
|
+
const sign = "asc" === dir ? 1 : -1;
|
|
72
|
+
items.sort((a, b)=>compareEntries(a, b, field) * sign);
|
|
73
|
+
}
|
|
74
|
+
const total = pages.reduce((n, p)=>n + p.page.total, 0);
|
|
75
|
+
const sourceCursors = {};
|
|
76
|
+
const next = {};
|
|
77
|
+
let hasNext = false;
|
|
78
|
+
for (const { provider, page } of pages){
|
|
79
|
+
sourceCursors[provider.id] = page.nextCursor;
|
|
80
|
+
if (void 0 !== page.nextCursor) {
|
|
81
|
+
next[provider.id] = page.nextCursor;
|
|
82
|
+
hasNext = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
items: Object.freeze(items),
|
|
87
|
+
total,
|
|
88
|
+
nextCursor: hasNext ? encodeCompositeCursor(next) : void 0,
|
|
89
|
+
sourceCursors
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function federatedSearch(input) {
|
|
93
|
+
const { providers, filter, signal } = input;
|
|
94
|
+
const eligible = providers.filter((p)=>providerCanSatisfy(p, filter));
|
|
95
|
+
const targets = filter.sources && filter.sources.length > 0 ? eligible.filter((p)=>filter.sources?.includes(p.id)) : eligible;
|
|
96
|
+
if (0 === targets.length) return {
|
|
97
|
+
items: Object.freeze([]),
|
|
98
|
+
total: 0,
|
|
99
|
+
nextCursor: void 0
|
|
100
|
+
};
|
|
101
|
+
const cursors = decodeCompositeCursor(filter.cursor);
|
|
102
|
+
const settled = await Promise.allSettled(targets.map((p)=>p.search(filter, cursors[p.id], signal)));
|
|
103
|
+
const ok = [];
|
|
104
|
+
settled.forEach((result, index)=>{
|
|
105
|
+
const provider = targets[index];
|
|
106
|
+
if (void 0 !== provider && "fulfilled" === result.status) ok.push({
|
|
107
|
+
provider,
|
|
108
|
+
page: result.value
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
return mergePages(ok, filter);
|
|
112
|
+
}
|
|
113
|
+
export { createLocalProvider, decodeCompositeCursor, encodeCompositeCursor, federatedSearch, providerCanSatisfy };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.r = (exports1)=>{
|
|
5
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
6
|
+
value: 'Module'
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
var __webpack_exports__ = {};
|
|
14
|
+
__webpack_require__.r(__webpack_exports__);
|
|
15
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
16
|
+
Object.defineProperty(exports, '__esModule', {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file External read-only source provider contract (PRD 0002 §3.4, §8).
|
|
3
|
+
*
|
|
4
|
+
* Unsplash (and any host-defined stock source) implements this. All providers
|
|
5
|
+
* compose behind the single `StudioAssetSource` the plugin registers; the
|
|
6
|
+
* built-in `local` provider wraps the resolved `AssetDataSource`. These are
|
|
7
|
+
* types only — erased at runtime, zero bytes in any chunk.
|
|
8
|
+
*/
|
|
9
|
+
import type { StudioAsset } from "@anvilkit/core/types";
|
|
10
|
+
import type { AssetFilter, AssetListPage, AssetSourceId } from "../types/filter.js";
|
|
11
|
+
import type { UploadResult } from "../types/types.js";
|
|
12
|
+
export interface AssetSourceCapabilities {
|
|
13
|
+
readonly searchable: boolean;
|
|
14
|
+
readonly themed: boolean;
|
|
15
|
+
/** Unsplash = false (browse + insert only). */
|
|
16
|
+
readonly mutable: boolean;
|
|
17
|
+
/** Unsplash = true. */
|
|
18
|
+
readonly requiresAttribution: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Supports folder scoping. Only the local library provider sets this; a
|
|
21
|
+
* folder-scoped query (`AssetFilter.folderId`) drops providers without it
|
|
22
|
+
* from the federation. Defaults to `false` when omitted.
|
|
23
|
+
*/
|
|
24
|
+
readonly folders?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface AssetTheme {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
/** i18n message key (not inline copy). */
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AssetSourceProvider {
|
|
33
|
+
/** `"unsplash"` | host id. The built-in `"local"` provider wraps the data source. */
|
|
34
|
+
readonly id: AssetSourceId;
|
|
35
|
+
readonly label: string;
|
|
36
|
+
readonly capabilities: AssetSourceCapabilities;
|
|
37
|
+
/** Content-Security-Policy hosts this provider needs (e.g. Unsplash img/connect src). */
|
|
38
|
+
readonly requiredCsp?: () => {
|
|
39
|
+
readonly connectSrc?: readonly string[];
|
|
40
|
+
readonly imgSrc?: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
listThemes(): readonly AssetTheme[] | Promise<readonly AssetTheme[]>;
|
|
43
|
+
search(query: AssetFilter, page: string | undefined, signal?: AbortSignal): Promise<AssetListPage>;
|
|
44
|
+
/**
|
|
45
|
+
* Convert a browsed result into a real asset (fires the source's
|
|
46
|
+
* download-trigger / attribution work) and feed it into the existing
|
|
47
|
+
* registry → IR → `asset://` pipeline.
|
|
48
|
+
*/
|
|
49
|
+
pickResult(asset: StudioAsset, signal?: AbortSignal): Promise<UploadResult>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=provider.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.cts","sourceRoot":"","sources":["../../src/sources/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uBAAuB;IACvB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IACnC,qFAAqF;IACrF,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,yFAAyF;IACzF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM;QAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,UAAU,IAAI,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACrE,MAAM,CACL,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5E"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file External read-only source provider contract (PRD 0002 §3.4, §8).
|
|
3
|
+
*
|
|
4
|
+
* Unsplash (and any host-defined stock source) implements this. All providers
|
|
5
|
+
* compose behind the single `StudioAssetSource` the plugin registers; the
|
|
6
|
+
* built-in `local` provider wraps the resolved `AssetDataSource`. These are
|
|
7
|
+
* types only — erased at runtime, zero bytes in any chunk.
|
|
8
|
+
*/
|
|
9
|
+
import type { StudioAsset } from "@anvilkit/core/types";
|
|
10
|
+
import type { AssetFilter, AssetListPage, AssetSourceId } from "../types/filter.js";
|
|
11
|
+
import type { UploadResult } from "../types/types.js";
|
|
12
|
+
export interface AssetSourceCapabilities {
|
|
13
|
+
readonly searchable: boolean;
|
|
14
|
+
readonly themed: boolean;
|
|
15
|
+
/** Unsplash = false (browse + insert only). */
|
|
16
|
+
readonly mutable: boolean;
|
|
17
|
+
/** Unsplash = true. */
|
|
18
|
+
readonly requiresAttribution: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Supports folder scoping. Only the local library provider sets this; a
|
|
21
|
+
* folder-scoped query (`AssetFilter.folderId`) drops providers without it
|
|
22
|
+
* from the federation. Defaults to `false` when omitted.
|
|
23
|
+
*/
|
|
24
|
+
readonly folders?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface AssetTheme {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
/** i18n message key (not inline copy). */
|
|
29
|
+
readonly label: string;
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AssetSourceProvider {
|
|
33
|
+
/** `"unsplash"` | host id. The built-in `"local"` provider wraps the data source. */
|
|
34
|
+
readonly id: AssetSourceId;
|
|
35
|
+
readonly label: string;
|
|
36
|
+
readonly capabilities: AssetSourceCapabilities;
|
|
37
|
+
/** Content-Security-Policy hosts this provider needs (e.g. Unsplash img/connect src). */
|
|
38
|
+
readonly requiredCsp?: () => {
|
|
39
|
+
readonly connectSrc?: readonly string[];
|
|
40
|
+
readonly imgSrc?: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
listThemes(): readonly AssetTheme[] | Promise<readonly AssetTheme[]>;
|
|
43
|
+
search(query: AssetFilter, page: string | undefined, signal?: AbortSignal): Promise<AssetListPage>;
|
|
44
|
+
/**
|
|
45
|
+
* Convert a browsed result into a real asset (fires the source's
|
|
46
|
+
* download-trigger / attribution work) and feed it into the existing
|
|
47
|
+
* registry → IR → `asset://` pipeline.
|
|
48
|
+
*/
|
|
49
|
+
pickResult(asset: StudioAsset, signal?: AbortSignal): Promise<UploadResult>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/sources/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uBAAuB;IACvB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IACnC,qFAAqF;IACrF,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,yFAAyF;IACzF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM;QAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,UAAU,IAAI,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACrE,MAAM,CACL,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
createUnsplashClient: ()=>createUnsplashClient
|
|
32
|
+
});
|
|
33
|
+
const errors_cjs_namespaceObject = require("../../utils/errors.cjs");
|
|
34
|
+
function createTimedSignal(timeoutMs, caller) {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
let didTimeout = false;
|
|
37
|
+
const timer = setTimeout(()=>{
|
|
38
|
+
didTimeout = true;
|
|
39
|
+
controller.abort();
|
|
40
|
+
}, timeoutMs);
|
|
41
|
+
timer.unref?.();
|
|
42
|
+
const onCallerAbort = ()=>controller.abort();
|
|
43
|
+
if (caller) if (caller.aborted) controller.abort();
|
|
44
|
+
else caller.addEventListener("abort", onCallerAbort, {
|
|
45
|
+
once: true
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
timedOut: ()=>didTimeout,
|
|
50
|
+
dispose: ()=>{
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
caller?.removeEventListener("abort", onCallerAbort);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function isAbortError(error) {
|
|
57
|
+
return "object" == typeof error && null !== error && "AbortError" === error.name;
|
|
58
|
+
}
|
|
59
|
+
function parseRetryAfterMs(headers) {
|
|
60
|
+
const retryAfter = headers.get("retry-after");
|
|
61
|
+
if (null !== retryAfter) {
|
|
62
|
+
const seconds = Number(retryAfter);
|
|
63
|
+
if (Number.isFinite(seconds)) return 1000 * seconds;
|
|
64
|
+
}
|
|
65
|
+
const reset = headers.get("x-ratelimit-reset");
|
|
66
|
+
if (null !== reset) {
|
|
67
|
+
const epochSeconds = Number(reset);
|
|
68
|
+
if (Number.isFinite(epochSeconds)) {
|
|
69
|
+
const ms = 1000 * epochSeconds - Date.now();
|
|
70
|
+
if (ms > 0) return ms;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function mapHttpError(response) {
|
|
75
|
+
if (429 === response.status) {
|
|
76
|
+
const retryAfterMs = parseRetryAfterMs(response.headers);
|
|
77
|
+
return new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_RATE_LIMITED", "Unsplash rate limit reached.", {
|
|
78
|
+
retryable: true,
|
|
79
|
+
status: 429,
|
|
80
|
+
...void 0 !== retryAfterMs ? {
|
|
81
|
+
retryAfterMs
|
|
82
|
+
} : {}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (401 === response.status || 403 === response.status) return new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_UNAUTHORIZED", "Unsplash rejected the request — check the access key / proxy.", {
|
|
86
|
+
status: response.status
|
|
87
|
+
});
|
|
88
|
+
return new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_BAD_RESPONSE", `Unsplash returned HTTP ${response.status}.`, {
|
|
89
|
+
status: response.status,
|
|
90
|
+
retryable: response.status >= 500
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function createUnsplashClient(options) {
|
|
94
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
95
|
+
const usingProxy = void 0 !== options.proxyEndpoint;
|
|
96
|
+
const base = (usingProxy ? String(options.proxyEndpoint) : "https://api.unsplash.com").replace(/\/$/, "");
|
|
97
|
+
const timeoutMs = options.timeoutMs ?? 15000;
|
|
98
|
+
const trackDownloadTimeoutMs = options.trackDownloadTimeoutMs ?? 6000;
|
|
99
|
+
const authHeaders = ()=>!usingProxy && options.accessKey ? {
|
|
100
|
+
Authorization: `Client-ID ${options.accessKey}`
|
|
101
|
+
} : {};
|
|
102
|
+
const readJson = async (response)=>{
|
|
103
|
+
try {
|
|
104
|
+
return await response.json();
|
|
105
|
+
} catch (cause) {
|
|
106
|
+
throw new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_BAD_RESPONSE", "Unsplash returned an unreadable response.", {
|
|
107
|
+
cause
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const requestJson = async (path, params, signal)=>{
|
|
112
|
+
const search = new URLSearchParams();
|
|
113
|
+
for (const [key, value] of Object.entries(params))if (void 0 !== value) search.set(key, String(value));
|
|
114
|
+
const query = search.toString();
|
|
115
|
+
const url = `${base}${path}${query ? `?${query}` : ""}`;
|
|
116
|
+
const timed = createTimedSignal(timeoutMs, signal);
|
|
117
|
+
try {
|
|
118
|
+
let response;
|
|
119
|
+
try {
|
|
120
|
+
response = await doFetch(url, {
|
|
121
|
+
headers: authHeaders(),
|
|
122
|
+
signal: timed.signal
|
|
123
|
+
});
|
|
124
|
+
} catch (cause) {
|
|
125
|
+
if (timed.timedOut()) throw new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_NETWORK", `Unsplash request timed out after ${timeoutMs} ms.`, {
|
|
126
|
+
retryable: true,
|
|
127
|
+
cause
|
|
128
|
+
});
|
|
129
|
+
if (isAbortError(cause)) throw cause;
|
|
130
|
+
throw new errors_cjs_namespaceObject.AssetSourceError("PROVIDER_NETWORK", "Network error contacting Unsplash.", {
|
|
131
|
+
retryable: true,
|
|
132
|
+
cause
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (!response.ok) throw mapHttpError(response);
|
|
136
|
+
return await readJson(response);
|
|
137
|
+
} finally{
|
|
138
|
+
timed.dispose();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
async searchPhotos (params, signal) {
|
|
143
|
+
const body = await requestJson("/search/photos", {
|
|
144
|
+
query: params.query,
|
|
145
|
+
page: params.page,
|
|
146
|
+
per_page: params.perPage,
|
|
147
|
+
orientation: params.orientation,
|
|
148
|
+
content_filter: params.contentFilter
|
|
149
|
+
}, signal);
|
|
150
|
+
return {
|
|
151
|
+
total: body.total ?? 0,
|
|
152
|
+
results: body.results ?? []
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
async topicPhotos (slug, params, signal) {
|
|
156
|
+
return await requestJson(`/topics/${encodeURIComponent(slug)}/photos`, {
|
|
157
|
+
page: params.page,
|
|
158
|
+
per_page: params.perPage,
|
|
159
|
+
orientation: params.orientation
|
|
160
|
+
}, signal) ?? [];
|
|
161
|
+
},
|
|
162
|
+
async listTopics (signal) {
|
|
163
|
+
return await requestJson("/topics", {
|
|
164
|
+
per_page: 30
|
|
165
|
+
}, signal) ?? [];
|
|
166
|
+
},
|
|
167
|
+
async getPhoto (id, signal) {
|
|
168
|
+
return requestJson(`/photos/${encodeURIComponent(id)}`, {}, signal);
|
|
169
|
+
},
|
|
170
|
+
async trackDownload (downloadLocation, signal) {
|
|
171
|
+
const timed = createTimedSignal(trackDownloadTimeoutMs, signal);
|
|
172
|
+
try {
|
|
173
|
+
await doFetch(downloadLocation, {
|
|
174
|
+
headers: authHeaders(),
|
|
175
|
+
signal: timed.signal
|
|
176
|
+
});
|
|
177
|
+
} catch {} finally{
|
|
178
|
+
timed.dispose();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
exports.createUnsplashClient = __webpack_exports__.createUnsplashClient;
|
|
184
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
185
|
+
"createUnsplashClient"
|
|
186
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
187
|
+
Object.defineProperty(exports, '__esModule', {
|
|
188
|
+
value: true
|
|
189
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Dependency-free Unsplash REST client (PRD 0002 §8.6). Uses only
|
|
3
|
+
* `globalThis.fetch` — no `unsplash-js`, no new dependency or peer (which would
|
|
4
|
+
* fail the repo's `check:peer-deps` gate). Lazy-loaded, so it never enters the
|
|
5
|
+
* headless entry chunk.
|
|
6
|
+
*
|
|
7
|
+
* Base URL is the host's server-side proxy when supplied (which injects the
|
|
8
|
+
* `Client-ID`), else `https://api.unsplash.com` with a `Client-ID` header.
|
|
9
|
+
*/
|
|
10
|
+
export interface UnsplashClientOptions {
|
|
11
|
+
readonly proxyEndpoint?: string | URL;
|
|
12
|
+
readonly accessKey?: string;
|
|
13
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
14
|
+
/**
|
|
15
|
+
* Per-request ceiling for search / topic / photo lookups (ms). A request
|
|
16
|
+
* that exceeds it aborts and surfaces as a retryable `PROVIDER_NETWORK`
|
|
17
|
+
* error instead of hanging on the platform's default socket timeout — so a
|
|
18
|
+
* blocked or flaky network path (e.g. a VPN/proxy black-holing
|
|
19
|
+
* `api.unsplash.com`) lets the sidebar fall back to a clean "unavailable"
|
|
20
|
+
* state. Default 15000. When the proxy route is used it has its own,
|
|
21
|
+
* tighter timeout + retry, so this is mainly a backstop for the direct path.
|
|
22
|
+
*/
|
|
23
|
+
readonly timeoutMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Ceiling for the best-effort, fire-and-forget download trigger (ms). It
|
|
26
|
+
* pings the absolute `download_location` URL DIRECTLY (never through the
|
|
27
|
+
* host proxy), so without a bound a stalled connection would leak a hanging
|
|
28
|
+
* request per insert. Default 6000.
|
|
29
|
+
*/
|
|
30
|
+
readonly trackDownloadTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface UnsplashPhotoUrls {
|
|
33
|
+
readonly raw?: string;
|
|
34
|
+
readonly full?: string;
|
|
35
|
+
readonly regular: string;
|
|
36
|
+
readonly small: string;
|
|
37
|
+
readonly thumb: string;
|
|
38
|
+
}
|
|
39
|
+
export interface UnsplashPhoto {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly width?: number;
|
|
42
|
+
readonly height?: number;
|
|
43
|
+
readonly description?: string | null;
|
|
44
|
+
readonly alt_description?: string | null;
|
|
45
|
+
readonly urls: UnsplashPhotoUrls;
|
|
46
|
+
readonly links: {
|
|
47
|
+
readonly html: string;
|
|
48
|
+
readonly download_location: string;
|
|
49
|
+
};
|
|
50
|
+
readonly user: {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly links: {
|
|
53
|
+
readonly html: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface UnsplashSearchResult {
|
|
58
|
+
readonly total: number;
|
|
59
|
+
readonly results: readonly UnsplashPhoto[];
|
|
60
|
+
}
|
|
61
|
+
export interface UnsplashTopicSummary {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly slug: string;
|
|
64
|
+
readonly title: string;
|
|
65
|
+
}
|
|
66
|
+
export interface UnsplashSearchParams {
|
|
67
|
+
readonly query: string;
|
|
68
|
+
readonly page?: number;
|
|
69
|
+
readonly perPage?: number;
|
|
70
|
+
readonly orientation?: string;
|
|
71
|
+
readonly contentFilter?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface UnsplashClient {
|
|
74
|
+
searchPhotos(params: UnsplashSearchParams, signal?: AbortSignal): Promise<UnsplashSearchResult>;
|
|
75
|
+
topicPhotos(slug: string, params: {
|
|
76
|
+
page?: number;
|
|
77
|
+
perPage?: number;
|
|
78
|
+
orientation?: string;
|
|
79
|
+
}, signal?: AbortSignal): Promise<readonly UnsplashPhoto[]>;
|
|
80
|
+
listTopics(signal?: AbortSignal): Promise<readonly UnsplashTopicSummary[]>;
|
|
81
|
+
/** Fetch a single photo by id — used to recover `download_location` on a cache miss. */
|
|
82
|
+
getPhoto(id: string, signal?: AbortSignal): Promise<UnsplashPhoto>;
|
|
83
|
+
/** Fires the MANDATORY download trigger. Non-throwing — best-effort. */
|
|
84
|
+
trackDownload(downloadLocation: string, signal?: AbortSignal): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
export declare function createUnsplashClient(options: UnsplashClientOptions): UnsplashClient;
|
|
87
|
+
//# sourceMappingURL=client.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.cts","sourceRoot":"","sources":["../../../src/sources/unsplash/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Dependency-free Unsplash REST client (PRD 0002 §8.6). Uses only
|
|
3
|
+
* `globalThis.fetch` — no `unsplash-js`, no new dependency or peer (which would
|
|
4
|
+
* fail the repo's `check:peer-deps` gate). Lazy-loaded, so it never enters the
|
|
5
|
+
* headless entry chunk.
|
|
6
|
+
*
|
|
7
|
+
* Base URL is the host's server-side proxy when supplied (which injects the
|
|
8
|
+
* `Client-ID`), else `https://api.unsplash.com` with a `Client-ID` header.
|
|
9
|
+
*/
|
|
10
|
+
export interface UnsplashClientOptions {
|
|
11
|
+
readonly proxyEndpoint?: string | URL;
|
|
12
|
+
readonly accessKey?: string;
|
|
13
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
14
|
+
/**
|
|
15
|
+
* Per-request ceiling for search / topic / photo lookups (ms). A request
|
|
16
|
+
* that exceeds it aborts and surfaces as a retryable `PROVIDER_NETWORK`
|
|
17
|
+
* error instead of hanging on the platform's default socket timeout — so a
|
|
18
|
+
* blocked or flaky network path (e.g. a VPN/proxy black-holing
|
|
19
|
+
* `api.unsplash.com`) lets the sidebar fall back to a clean "unavailable"
|
|
20
|
+
* state. Default 15000. When the proxy route is used it has its own,
|
|
21
|
+
* tighter timeout + retry, so this is mainly a backstop for the direct path.
|
|
22
|
+
*/
|
|
23
|
+
readonly timeoutMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Ceiling for the best-effort, fire-and-forget download trigger (ms). It
|
|
26
|
+
* pings the absolute `download_location` URL DIRECTLY (never through the
|
|
27
|
+
* host proxy), so without a bound a stalled connection would leak a hanging
|
|
28
|
+
* request per insert. Default 6000.
|
|
29
|
+
*/
|
|
30
|
+
readonly trackDownloadTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface UnsplashPhotoUrls {
|
|
33
|
+
readonly raw?: string;
|
|
34
|
+
readonly full?: string;
|
|
35
|
+
readonly regular: string;
|
|
36
|
+
readonly small: string;
|
|
37
|
+
readonly thumb: string;
|
|
38
|
+
}
|
|
39
|
+
export interface UnsplashPhoto {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly width?: number;
|
|
42
|
+
readonly height?: number;
|
|
43
|
+
readonly description?: string | null;
|
|
44
|
+
readonly alt_description?: string | null;
|
|
45
|
+
readonly urls: UnsplashPhotoUrls;
|
|
46
|
+
readonly links: {
|
|
47
|
+
readonly html: string;
|
|
48
|
+
readonly download_location: string;
|
|
49
|
+
};
|
|
50
|
+
readonly user: {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly links: {
|
|
53
|
+
readonly html: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface UnsplashSearchResult {
|
|
58
|
+
readonly total: number;
|
|
59
|
+
readonly results: readonly UnsplashPhoto[];
|
|
60
|
+
}
|
|
61
|
+
export interface UnsplashTopicSummary {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly slug: string;
|
|
64
|
+
readonly title: string;
|
|
65
|
+
}
|
|
66
|
+
export interface UnsplashSearchParams {
|
|
67
|
+
readonly query: string;
|
|
68
|
+
readonly page?: number;
|
|
69
|
+
readonly perPage?: number;
|
|
70
|
+
readonly orientation?: string;
|
|
71
|
+
readonly contentFilter?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface UnsplashClient {
|
|
74
|
+
searchPhotos(params: UnsplashSearchParams, signal?: AbortSignal): Promise<UnsplashSearchResult>;
|
|
75
|
+
topicPhotos(slug: string, params: {
|
|
76
|
+
page?: number;
|
|
77
|
+
perPage?: number;
|
|
78
|
+
orientation?: string;
|
|
79
|
+
}, signal?: AbortSignal): Promise<readonly UnsplashPhoto[]>;
|
|
80
|
+
listTopics(signal?: AbortSignal): Promise<readonly UnsplashTopicSummary[]>;
|
|
81
|
+
/** Fetch a single photo by id — used to recover `download_location` on a cache miss. */
|
|
82
|
+
getPhoto(id: string, signal?: AbortSignal): Promise<UnsplashPhoto>;
|
|
83
|
+
/** Fires the MANDATORY download trigger. Non-throwing — best-effort. */
|
|
84
|
+
trackDownload(downloadLocation: string, signal?: AbortSignal): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
export declare function createUnsplashClient(options: UnsplashClientOptions): UnsplashClient;
|
|
87
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/sources/unsplash/client.ts"],"names":[],"mappings":"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"}
|