@refkit/provider-flickr 0.1.1 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -7,6 +7,37 @@ interface FlickrConfig {
7
7
  * CC/PD/CC0 set — a relevance hint only; mapFlickrLicense below is authoritative. */
8
8
  licenseFilter?: string;
9
9
  }
10
+ interface FlickrSearchOptions {
11
+ licenseFilter?: string;
12
+ sort?: 'date-posted-asc' | 'date-posted-desc' | 'date-taken-asc' | 'date-taken-desc' | 'interestingness-desc' | 'interestingness-asc' | 'relevance';
13
+ safeSearch?: 1 | 2 | 3;
14
+ tags?: string | readonly string[];
15
+ tagMode?: 'any' | 'all';
16
+ userId?: string;
17
+ minUploadDate?: string | number;
18
+ maxUploadDate?: string | number;
19
+ minTakenDate?: string | number;
20
+ maxTakenDate?: string | number;
21
+ bbox?: string;
22
+ accuracy?: number;
23
+ machineTags?: string | readonly string[];
24
+ machineTagMode?: 'any' | 'all';
25
+ groupId?: string;
26
+ woeId?: string;
27
+ placeId?: string;
28
+ hasGeo?: boolean;
29
+ geoContext?: 0 | 1 | 2;
30
+ lat?: string;
31
+ lon?: string;
32
+ radius?: number;
33
+ radiusUnits?: 'mi' | 'km';
34
+ isCommons?: boolean;
35
+ inGallery?: boolean;
36
+ isGetty?: boolean;
37
+ extras?: string | readonly string[];
38
+ page?: number;
39
+ perPage?: number;
40
+ }
10
41
  /** Map a Flickr numeric license id (string or number) to our license + CC version. */
11
42
  declare function mapFlickrLicense(id: string | number): {
12
43
  license: LicenseId;
@@ -14,4 +45,4 @@ declare function mapFlickrLicense(id: string | number): {
14
45
  };
15
46
  declare function flickr(config: FlickrConfig): _refkit_core.ReferenceProvider;
16
47
 
17
- export { type FlickrConfig, flickr, mapFlickrLicense };
48
+ export { type FlickrConfig, type FlickrSearchOptions, flickr, mapFlickrLicense };
package/dist/index.js CHANGED
@@ -44,6 +44,67 @@ function mapFlickrLicense(id) {
44
44
  return FLICKR_LICENSE[n] ?? { license: "unknown" };
45
45
  }
46
46
  var DEFAULT_LICENSE_FILTER = "4,5,7,8,9,10,11,12";
47
+ function setIfString(url, key, value, allowed) {
48
+ if (typeof value !== "string") return;
49
+ if (allowed && !allowed.includes(value)) return;
50
+ url.searchParams.set(key, value);
51
+ }
52
+ function setIfSafeSearch(url, value) {
53
+ if (value !== 1 && value !== 2 && value !== 3) return;
54
+ url.searchParams.set("safe_search", String(value));
55
+ }
56
+ function setTags(url, value) {
57
+ if (typeof value === "string" && value) url.searchParams.set("tags", value);
58
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) url.searchParams.set("tags", value.join(","));
59
+ }
60
+ function setStringOrNumber(url, key, value) {
61
+ if (typeof value === "string" && value) url.searchParams.set(key, value);
62
+ if (typeof value === "number" && Number.isFinite(value)) url.searchParams.set(key, String(value));
63
+ }
64
+ function setStringList(url, key, value) {
65
+ if (typeof value === "string" && value) url.searchParams.set(key, value);
66
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) url.searchParams.set(key, value.join(","));
67
+ }
68
+ function setIfInt(url, key, value, options) {
69
+ if (typeof value !== "number" || !Number.isInteger(value)) return;
70
+ if (options?.min !== void 0 && value < options.min) return;
71
+ if (options?.max !== void 0 && value > options.max) return;
72
+ url.searchParams.set(key, String(value));
73
+ }
74
+ function setBooleanFlag(url, key, value) {
75
+ if (typeof value !== "boolean") return;
76
+ url.searchParams.set(key, value ? "1" : "0");
77
+ }
78
+ function flickrExtras(value) {
79
+ const required = ["license", "owner_name", "url_t", "url_m", "url_l"];
80
+ const extras = new Set(required);
81
+ if (typeof value === "string") {
82
+ for (const item of value.split(",")) if (item.trim()) extras.add(item.trim());
83
+ }
84
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
85
+ for (const item of value) if (item) extras.add(item);
86
+ }
87
+ return Array.from(extras).join(",");
88
+ }
89
+ function flickrLicenseForControls(license) {
90
+ if (!license) return void 0;
91
+ if (license.allowUnknown) return DEFAULT_LICENSE_FILTER;
92
+ if (license.commercial && license.modification) return "4,5,9,10,11,12";
93
+ if (license.commercial) return "4,5,8,9,10,11,12";
94
+ return void 0;
95
+ }
96
+ function flickrSort(sort) {
97
+ if (sort === "interesting") return "interestingness-desc";
98
+ if (sort === "latest") return "date-posted-desc";
99
+ if (sort === "relevance") return "relevance";
100
+ return void 0;
101
+ }
102
+ function flickrSafeSearch(safety) {
103
+ if (safety === "strict") return 1;
104
+ if (safety === "moderate") return 2;
105
+ if (safety === "off") return 3;
106
+ return void 0;
107
+ }
47
108
  function toReference(p) {
48
109
  const { license, version } = mapFlickrLicense(p.license);
49
110
  const canonicalUrl = `https://www.flickr.com/photos/${p.owner}/${p.id}`;
@@ -78,17 +139,46 @@ function flickr(config) {
78
139
  id: "flickr",
79
140
  modalities: ["image"],
80
141
  queryFeatures: ["keyword"],
142
+ capabilities: { controls: ["sort", "safety", "license.commercial", "license.modification", "license.allowUnknown", "creator.id"] },
81
143
  async search(q, ctx) {
144
+ const opts = q.providerOptions;
82
145
  const url = new URL("https://api.flickr.com/services/rest/");
83
146
  url.searchParams.set("method", "flickr.photos.search");
84
147
  url.searchParams.set("api_key", config.apiKey);
85
148
  url.searchParams.set("text", q.text);
86
- url.searchParams.set("license", config.licenseFilter ?? DEFAULT_LICENSE_FILTER);
149
+ url.searchParams.set("license", opts?.licenseFilter ?? flickrLicenseForControls(q.controls?.license) ?? config.licenseFilter ?? DEFAULT_LICENSE_FILTER);
87
150
  url.searchParams.set("content_type", "1");
88
151
  url.searchParams.set("media", "photos");
89
152
  url.searchParams.set("sort", "relevance");
90
- url.searchParams.set("extras", "license,owner_name,url_t,url_m,url_l");
153
+ setIfString(url, "sort", opts?.sort ?? flickrSort(q.controls?.sort), ["date-posted-asc", "date-posted-desc", "date-taken-asc", "date-taken-desc", "interestingness-desc", "interestingness-asc", "relevance"]);
154
+ setIfSafeSearch(url, opts?.safeSearch ?? flickrSafeSearch(q.controls?.safety));
155
+ setTags(url, opts?.tags);
156
+ setIfString(url, "tag_mode", opts?.tagMode, ["any", "all"]);
157
+ setIfString(url, "user_id", opts?.userId ?? q.controls?.creator?.id);
158
+ setStringOrNumber(url, "min_upload_date", opts?.minUploadDate);
159
+ setStringOrNumber(url, "max_upload_date", opts?.maxUploadDate);
160
+ setStringOrNumber(url, "min_taken_date", opts?.minTakenDate);
161
+ setStringOrNumber(url, "max_taken_date", opts?.maxTakenDate);
162
+ setIfString(url, "bbox", opts?.bbox);
163
+ setIfInt(url, "accuracy", opts?.accuracy, { min: 1, max: 16 });
164
+ setStringList(url, "machine_tags", opts?.machineTags);
165
+ setIfString(url, "machine_tag_mode", opts?.machineTagMode, ["any", "all"]);
166
+ setIfString(url, "group_id", opts?.groupId);
167
+ setIfString(url, "woe_id", opts?.woeId);
168
+ setIfString(url, "place_id", opts?.placeId);
169
+ setBooleanFlag(url, "has_geo", opts?.hasGeo);
170
+ setIfInt(url, "geo_context", opts?.geoContext, { min: 0, max: 2 });
171
+ setIfString(url, "lat", opts?.lat);
172
+ setIfString(url, "lon", opts?.lon);
173
+ setStringOrNumber(url, "radius", opts?.radius);
174
+ setIfString(url, "radius_units", opts?.radiusUnits, ["mi", "km"]);
175
+ setBooleanFlag(url, "is_commons", opts?.isCommons);
176
+ setBooleanFlag(url, "in_gallery", opts?.inGallery);
177
+ setBooleanFlag(url, "is_getty", opts?.isGetty);
178
+ url.searchParams.set("extras", flickrExtras(opts?.extras));
179
+ setIfInt(url, "page", opts?.page, { min: 1 });
91
180
  url.searchParams.set("per_page", String(q.limit ?? 20));
181
+ setIfInt(url, "per_page", opts?.perPage, { min: 1, max: 500 });
92
182
  url.searchParams.set("format", "json");
93
183
  url.searchParams.set("nojsoncallback", "1");
94
184
  const res = await ctx.fetch(url.toString(), { signal: ctx.signal });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n defineProvider, referenceId,\n type Reference, type RightsRecord, type LicenseId,\n type NormalizedQuery, type ProviderContext,\n} from '@refkit/core'\n\nexport interface FlickrConfig {\n apiKey: string\n /** Server-side license filter (comma-separated Flickr license ids). Defaults to the\n * CC/PD/CC0 set — a relevance hint only; mapFlickrLicense below is authoritative. */\n licenseFilter?: string\n}\n\n// Flickr numeric license id → our LicenseId (+ CC version). See\n// flickr.photos.licenses.getInfo. All Rights Reserved (0) and every NC/ND\n// variant map to 'proprietary' (→ denied for commercial/AI use).\nconst FLICKR_LICENSE: Record<number, { license: LicenseId; version?: string }> = {\n 0: { license: 'proprietary' }, // All Rights Reserved\n 1: { license: 'proprietary' }, // CC BY-NC-SA 2.0\n 2: { license: 'proprietary' }, // CC BY-NC 2.0\n 3: { license: 'proprietary' }, // CC BY-NC-ND 2.0\n 4: { license: 'CC-BY', version: '2.0' }, // CC BY 2.0\n 5: { license: 'CC-BY-SA', version: '2.0' }, // CC BY-SA 2.0\n 6: { license: 'proprietary' }, // CC BY-ND 2.0\n 7: { license: 'unknown' }, // \"No known copyright restrictions\" — NOT a guaranteed-free grant → needs-review\n 8: { license: 'PD' }, // United States Government Work\n 9: { license: 'CC0-1.0' }, // Public Domain Dedication (CC0)\n 10: { license: 'PD' }, // Public Domain Mark\n 11: { license: 'CC-BY', version: '4.0' }, // CC BY 4.0\n 12: { license: 'CC-BY-SA', version: '4.0' }, // CC BY-SA 4.0\n 13: { license: 'proprietary' }, // CC BY-ND 4.0\n 14: { license: 'proprietary' }, // CC BY-NC 4.0\n 15: { license: 'proprietary' }, // CC BY-NC-SA 4.0\n 16: { license: 'proprietary' }, // CC BY-NC-ND 4.0\n}\n\n/** Map a Flickr numeric license id (string or number) to our license + CC version. */\nexport function mapFlickrLicense(id: string | number): { license: LicenseId; version?: string } {\n const n = typeof id === 'string' ? Number(id) : id\n return FLICKR_LICENSE[n] ?? { license: 'unknown' }\n}\n\n// Usable-license relevance hint (CC-BY/BY-SA 2.0+4.0, PD ×3, CC0). Flickr is mostly\n// All-Rights-Reserved, so without this most results would gate straight to denied.\nconst DEFAULT_LICENSE_FILTER = '4,5,7,8,9,10,11,12'\n\ninterface FlickrPhoto {\n id: string\n owner: string\n title: string\n license: string\n ownername?: string\n url_t?: string; width_t?: number; height_t?: number\n url_m?: string; width_m?: number; height_m?: number\n url_l?: string; width_l?: number; height_l?: number\n}\ninterface FlickrResponse { photos?: { photo: FlickrPhoto[] }; stat: string }\n\nfunction toReference(p: FlickrPhoto): Reference {\n const { license, version } = mapFlickrLicense(p.license)\n const canonicalUrl = `https://www.flickr.com/photos/${p.owner}/${p.id}`\n const rights: RightsRecord = {\n license,\n licenseVersion: license === 'CC-BY' || license === 'CC-BY-SA' ? version : undefined,\n author: p.ownername || undefined,\n rehostPolicy: 'cache-allowed',\n raw: { sourceTerms: 'https://www.flickr.com/help/terms', sourceUrl: canonicalUrl },\n }\n const previewUrl = p.url_l ?? p.url_m\n const previewW = p.url_l ? p.width_l : p.width_m\n const previewH = p.url_l ? p.height_l : p.height_m\n const thumbUrl = p.url_t ?? p.url_m\n return {\n id: referenceId('flickr', canonicalUrl),\n modality: 'image',\n title: p.title || undefined,\n source: { providerId: 'flickr', sourceUrl: canonicalUrl },\n canonicalUrl,\n rights,\n verifiedAt: new Date().toISOString(),\n ...(thumbUrl ? { thumbnail: { url: thumbUrl } } : {}),\n ...(previewUrl ? { preview: { url: previewUrl, mediaType: 'image/jpeg', width: previewW, height: previewH } } : {}),\n ...(previewW && previewH ? { visual: { width: previewW, height: previewH } } : {}),\n relevance: 0,\n raw: p,\n }\n}\n\nexport function flickr(config: FlickrConfig) {\n return defineProvider({\n id: 'flickr',\n modalities: ['image'],\n queryFeatures: ['keyword'],\n async search(q: NormalizedQuery, ctx: ProviderContext): Promise<Reference[]> {\n const url = new URL('https://api.flickr.com/services/rest/')\n url.searchParams.set('method', 'flickr.photos.search')\n url.searchParams.set('api_key', config.apiKey)\n url.searchParams.set('text', q.text)\n url.searchParams.set('license', config.licenseFilter ?? DEFAULT_LICENSE_FILTER)\n url.searchParams.set('content_type', '1') // photos only (no screenshots/other)\n url.searchParams.set('media', 'photos')\n url.searchParams.set('sort', 'relevance')\n url.searchParams.set('extras', 'license,owner_name,url_t,url_m,url_l')\n url.searchParams.set('per_page', String(q.limit ?? 20))\n url.searchParams.set('format', 'json')\n url.searchParams.set('nojsoncallback', '1')\n const res = await ctx.fetch(url.toString(), { signal: ctx.signal })\n if (!res.ok) throw new Error(`flickr search failed: ${res.status}`)\n const json = (await res.json()) as FlickrResponse\n if (json.stat !== 'ok' || !json.photos) throw new Error(`flickr search error: stat=${json.stat}`)\n return json.photos.photo.map(toReference)\n },\n })\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n defineProvider, referenceId,\n type Reference, type RightsRecord, type LicenseId, type SearchLicenseControls,\n type NormalizedQuery, type ProviderContext,\n} from '@refkit/core'\n\nexport interface FlickrConfig {\n apiKey: string\n /** Server-side license filter (comma-separated Flickr license ids). Defaults to the\n * CC/PD/CC0 set — a relevance hint only; mapFlickrLicense below is authoritative. */\n licenseFilter?: string\n}\n\nexport interface FlickrSearchOptions {\n licenseFilter?: string\n sort?: 'date-posted-asc' | 'date-posted-desc' | 'date-taken-asc' | 'date-taken-desc' | 'interestingness-desc' | 'interestingness-asc' | 'relevance'\n safeSearch?: 1 | 2 | 3\n tags?: string | readonly string[]\n tagMode?: 'any' | 'all'\n userId?: string\n minUploadDate?: string | number\n maxUploadDate?: string | number\n minTakenDate?: string | number\n maxTakenDate?: string | number\n bbox?: string\n accuracy?: number\n machineTags?: string | readonly string[]\n machineTagMode?: 'any' | 'all'\n groupId?: string\n woeId?: string\n placeId?: string\n hasGeo?: boolean\n geoContext?: 0 | 1 | 2\n lat?: string\n lon?: string\n radius?: number\n radiusUnits?: 'mi' | 'km'\n isCommons?: boolean\n inGallery?: boolean\n isGetty?: boolean\n extras?: string | readonly string[]\n page?: number\n perPage?: number\n}\n\n// Flickr numeric license id → our LicenseId (+ CC version). See\n// flickr.photos.licenses.getInfo. All Rights Reserved (0) and every NC/ND\n// variant map to 'proprietary' (→ denied for commercial/AI use).\nconst FLICKR_LICENSE: Record<number, { license: LicenseId; version?: string }> = {\n 0: { license: 'proprietary' }, // All Rights Reserved\n 1: { license: 'proprietary' }, // CC BY-NC-SA 2.0\n 2: { license: 'proprietary' }, // CC BY-NC 2.0\n 3: { license: 'proprietary' }, // CC BY-NC-ND 2.0\n 4: { license: 'CC-BY', version: '2.0' }, // CC BY 2.0\n 5: { license: 'CC-BY-SA', version: '2.0' }, // CC BY-SA 2.0\n 6: { license: 'proprietary' }, // CC BY-ND 2.0\n 7: { license: 'unknown' }, // \"No known copyright restrictions\" — NOT a guaranteed-free grant → needs-review\n 8: { license: 'PD' }, // United States Government Work\n 9: { license: 'CC0-1.0' }, // Public Domain Dedication (CC0)\n 10: { license: 'PD' }, // Public Domain Mark\n 11: { license: 'CC-BY', version: '4.0' }, // CC BY 4.0\n 12: { license: 'CC-BY-SA', version: '4.0' }, // CC BY-SA 4.0\n 13: { license: 'proprietary' }, // CC BY-ND 4.0\n 14: { license: 'proprietary' }, // CC BY-NC 4.0\n 15: { license: 'proprietary' }, // CC BY-NC-SA 4.0\n 16: { license: 'proprietary' }, // CC BY-NC-ND 4.0\n}\n\n/** Map a Flickr numeric license id (string or number) to our license + CC version. */\nexport function mapFlickrLicense(id: string | number): { license: LicenseId; version?: string } {\n const n = typeof id === 'string' ? Number(id) : id\n return FLICKR_LICENSE[n] ?? { license: 'unknown' }\n}\n\n// Usable-license relevance hint (CC-BY/BY-SA 2.0+4.0, PD ×3, CC0). Flickr is mostly\n// All-Rights-Reserved, so without this most results would gate straight to denied.\nconst DEFAULT_LICENSE_FILTER = '4,5,7,8,9,10,11,12'\n\ninterface FlickrPhoto {\n id: string\n owner: string\n title: string\n license: string\n ownername?: string\n url_t?: string; width_t?: number; height_t?: number\n url_m?: string; width_m?: number; height_m?: number\n url_l?: string; width_l?: number; height_l?: number\n}\ninterface FlickrResponse { photos?: { photo: FlickrPhoto[] }; stat: string }\n\nfunction setIfString(url: URL, key: string, value: unknown, allowed?: readonly string[]) {\n if (typeof value !== 'string') return\n if (allowed && !allowed.includes(value)) return\n url.searchParams.set(key, value)\n}\n\nfunction setIfSafeSearch(url: URL, value: unknown) {\n if (value !== 1 && value !== 2 && value !== 3) return\n url.searchParams.set('safe_search', String(value))\n}\n\nfunction setTags(url: URL, value: unknown) {\n if (typeof value === 'string' && value) url.searchParams.set('tags', value)\n if (Array.isArray(value) && value.every(v => typeof v === 'string')) url.searchParams.set('tags', value.join(','))\n}\n\nfunction setStringOrNumber(url: URL, key: string, value: unknown) {\n if (typeof value === 'string' && value) url.searchParams.set(key, value)\n if (typeof value === 'number' && Number.isFinite(value)) url.searchParams.set(key, String(value))\n}\n\nfunction setStringList(url: URL, key: string, value: unknown) {\n if (typeof value === 'string' && value) url.searchParams.set(key, value)\n if (Array.isArray(value) && value.every(v => typeof v === 'string')) url.searchParams.set(key, value.join(','))\n}\n\nfunction setIfInt(url: URL, key: string, value: unknown, options?: { min?: number; max?: number }) {\n if (typeof value !== 'number' || !Number.isInteger(value)) return\n if (options?.min !== undefined && value < options.min) return\n if (options?.max !== undefined && value > options.max) return\n url.searchParams.set(key, String(value))\n}\n\nfunction setBooleanFlag(url: URL, key: string, value: unknown) {\n if (typeof value !== 'boolean') return\n url.searchParams.set(key, value ? '1' : '0')\n}\n\nfunction flickrExtras(value: unknown): string {\n const required = ['license', 'owner_name', 'url_t', 'url_m', 'url_l']\n const extras = new Set(required)\n if (typeof value === 'string') {\n for (const item of value.split(',')) if (item.trim()) extras.add(item.trim())\n }\n if (Array.isArray(value) && value.every(v => typeof v === 'string')) {\n for (const item of value) if (item) extras.add(item)\n }\n return Array.from(extras).join(',')\n}\n\nfunction flickrLicenseForControls(license: SearchLicenseControls | undefined): string | undefined {\n if (!license) return undefined\n if (license.allowUnknown) return DEFAULT_LICENSE_FILTER\n if (license.commercial && license.modification) return '4,5,9,10,11,12'\n if (license.commercial) return '4,5,8,9,10,11,12'\n return undefined\n}\n\nfunction flickrSort(sort: string | undefined): string | undefined {\n if (sort === 'interesting') return 'interestingness-desc'\n if (sort === 'latest') return 'date-posted-desc'\n if (sort === 'relevance') return 'relevance'\n return undefined\n}\n\nfunction flickrSafeSearch(safety: string | undefined): 1 | 2 | 3 | undefined {\n if (safety === 'strict') return 1\n if (safety === 'moderate') return 2\n if (safety === 'off') return 3\n return undefined\n}\n\nfunction toReference(p: FlickrPhoto): Reference {\n const { license, version } = mapFlickrLicense(p.license)\n const canonicalUrl = `https://www.flickr.com/photos/${p.owner}/${p.id}`\n const rights: RightsRecord = {\n license,\n licenseVersion: license === 'CC-BY' || license === 'CC-BY-SA' ? version : undefined,\n author: p.ownername || undefined,\n rehostPolicy: 'cache-allowed',\n raw: { sourceTerms: 'https://www.flickr.com/help/terms', sourceUrl: canonicalUrl },\n }\n const previewUrl = p.url_l ?? p.url_m\n const previewW = p.url_l ? p.width_l : p.width_m\n const previewH = p.url_l ? p.height_l : p.height_m\n const thumbUrl = p.url_t ?? p.url_m\n return {\n id: referenceId('flickr', canonicalUrl),\n modality: 'image',\n title: p.title || undefined,\n source: { providerId: 'flickr', sourceUrl: canonicalUrl },\n canonicalUrl,\n rights,\n verifiedAt: new Date().toISOString(),\n ...(thumbUrl ? { thumbnail: { url: thumbUrl } } : {}),\n ...(previewUrl ? { preview: { url: previewUrl, mediaType: 'image/jpeg', width: previewW, height: previewH } } : {}),\n ...(previewW && previewH ? { visual: { width: previewW, height: previewH } } : {}),\n relevance: 0,\n raw: p,\n }\n}\n\nexport function flickr(config: FlickrConfig) {\n return defineProvider({\n id: 'flickr',\n modalities: ['image'],\n queryFeatures: ['keyword'],\n capabilities: { controls: ['sort', 'safety', 'license.commercial', 'license.modification', 'license.allowUnknown', 'creator.id'] },\n async search(q: NormalizedQuery, ctx: ProviderContext): Promise<Reference[]> {\n const opts = q.providerOptions as FlickrSearchOptions | undefined\n const url = new URL('https://api.flickr.com/services/rest/')\n url.searchParams.set('method', 'flickr.photos.search')\n url.searchParams.set('api_key', config.apiKey)\n url.searchParams.set('text', q.text)\n url.searchParams.set('license', opts?.licenseFilter ?? flickrLicenseForControls(q.controls?.license) ?? config.licenseFilter ?? DEFAULT_LICENSE_FILTER)\n url.searchParams.set('content_type', '1') // photos only (no screenshots/other)\n url.searchParams.set('media', 'photos')\n url.searchParams.set('sort', 'relevance')\n setIfString(url, 'sort', opts?.sort ?? flickrSort(q.controls?.sort), ['date-posted-asc', 'date-posted-desc', 'date-taken-asc', 'date-taken-desc', 'interestingness-desc', 'interestingness-asc', 'relevance'])\n setIfSafeSearch(url, opts?.safeSearch ?? flickrSafeSearch(q.controls?.safety))\n setTags(url, opts?.tags)\n setIfString(url, 'tag_mode', opts?.tagMode, ['any', 'all'])\n setIfString(url, 'user_id', opts?.userId ?? q.controls?.creator?.id)\n setStringOrNumber(url, 'min_upload_date', opts?.minUploadDate)\n setStringOrNumber(url, 'max_upload_date', opts?.maxUploadDate)\n setStringOrNumber(url, 'min_taken_date', opts?.minTakenDate)\n setStringOrNumber(url, 'max_taken_date', opts?.maxTakenDate)\n setIfString(url, 'bbox', opts?.bbox)\n setIfInt(url, 'accuracy', opts?.accuracy, { min: 1, max: 16 })\n setStringList(url, 'machine_tags', opts?.machineTags)\n setIfString(url, 'machine_tag_mode', opts?.machineTagMode, ['any', 'all'])\n setIfString(url, 'group_id', opts?.groupId)\n setIfString(url, 'woe_id', opts?.woeId)\n setIfString(url, 'place_id', opts?.placeId)\n setBooleanFlag(url, 'has_geo', opts?.hasGeo)\n setIfInt(url, 'geo_context', opts?.geoContext, { min: 0, max: 2 })\n setIfString(url, 'lat', opts?.lat)\n setIfString(url, 'lon', opts?.lon)\n setStringOrNumber(url, 'radius', opts?.radius)\n setIfString(url, 'radius_units', opts?.radiusUnits, ['mi', 'km'])\n setBooleanFlag(url, 'is_commons', opts?.isCommons)\n setBooleanFlag(url, 'in_gallery', opts?.inGallery)\n setBooleanFlag(url, 'is_getty', opts?.isGetty)\n url.searchParams.set('extras', flickrExtras(opts?.extras))\n setIfInt(url, 'page', opts?.page, { min: 1 })\n url.searchParams.set('per_page', String(q.limit ?? 20))\n setIfInt(url, 'per_page', opts?.perPage, { min: 1, max: 500 })\n url.searchParams.set('format', 'json')\n url.searchParams.set('nojsoncallback', '1')\n const res = await ctx.fetch(url.toString(), { signal: ctx.signal })\n if (!res.ok) throw new Error(`flickr search failed: ${res.status}`)\n const json = (await res.json()) as FlickrResponse\n if (json.stat !== 'ok' || !json.photos) throw new Error(`flickr search error: stat=${json.stat}`)\n return json.photos.photo.map(toReference)\n },\n })\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@refkit/provider-flickr",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Flickr provider satellite for refkit.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@refkit/core": "0.2.0"
27
+ "@refkit/core": "0.4.0"
28
28
  },
29
29
  "files": [
30
30
  "dist",