Setting the file. One moment.
Confirmed Pictures · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page 192
function mediaSourceRequiresConfirmation
— line 192
This file
Number 45.59
Position 59 of 182
Type JavaScript
Size 7 KB
Lines 212 lib/ confirmed-pictures.js
JavaScript · 212 lines · 7 KB
const
REASON_ORDER
=
[
'no-source-id'
,
'no-source-record'
,
'source-unreadable'
];
11
12 const trustedReads = new WeakSet ();
13 const confirmedGalleries = new WeakSet ();
14 let declaredMediaSource;
15
16 function sourceIdOf ( value ) {
17 const id = Number (value);
18 return Number. isInteger (id) && id > 0 ? id : null ;
19 }
20
21 function isNonEmptyString ( value ) {
22 return typeof value === 'string' && value. trim () !== '' ;
23 }
24
25 function isHttpUrl ( value ) {
26 if ( ! isNonEmptyString (value)) return false ;
27 try {
28 const parsed = new URL (value);
29 return parsed.protocol === 'http:' || parsed.protocol === 'https:' ;
30 } catch {
31 return false ;
32 }
33 }
34
35 function freezeList ( values ) {
36 return Object. freeze (values. slice ());
37 }
38
39 function freezeGallery ( items ) {
40 const gallery = items. map (( item ) => Object. freeze ({ ... item }));
41 Object. freeze (gallery);
42 confirmedGalleries. add (gallery);
43 return gallery;
44 }
45
46 async function readSourceMediaRecords ({ ids , source } = {}) {
47 if ( ! Array. isArray (ids)) throw new TypeError ( 'readSourceMediaRecords: ids must be an array' );
48 if ( ! source || ! isNonEmptyString (source.baseUrl) || typeof source.request !== 'function' ) {
49 throw new TypeError ( 'readSourceMediaRecords: source must provide baseUrl and request' );
50 }
51
52 const distinctIds = Array. from ( new Set (ids. map (sourceIdOf). filter (Boolean))). sort (( a , b ) => a - b);
53 const files = Object. create ( null );
54 const missing = [];
55 const unreadable = [];
56
57 for ( let offset = 0 ; offset < distinctIds. length ; offset += MEDIA_READ_CHUNK_SIZE ) {
58 const chunk = distinctIds. slice (offset, offset + MEDIA_READ_CHUNK_SIZE );
59 let response;
60 try {
61 response = await source. request (source.baseUrl, '/wp/v2/media' , {
62 method: 'GET' ,
63 query: { include: chunk. join ( ',' ), per_page: MEDIA_READ_CHUNK_SIZE },
64 deadlineMs: MEDIA_READ_DEADLINE_MS ,
65 });
66 } catch {
67 unreadable. push ( ... chunk);
68 continue ;
69 }
70
71 if ( ! response || response.ok !== true || ! Array. isArray (response.json)) {
72 unreadable. push ( ... chunk);
73 continue ;
74 }
75
76 const returned = new Map ();
77 for ( const record of response.json) {
78 const id = sourceIdOf (record && record.id);
79 if (id !== null && chunk. includes (id) && isHttpUrl (record.source_url)) {
80 returned. set (id, new URL (record.source_url). toString ());
81 }
82 }
83 for ( const id of chunk) {
84 if (returned. has (id)) files[id] = returned. get (id);
85 else missing. push (id);
86 }
87 }
88
89 const read = Object. freeze ({
90 files: Object. freeze (files),
91 missing: freezeList (missing),
92 unreadable: freezeList (unreadable),
93 });
94 trustedReads. add (read);
95 return read;
96 }
97
98 function sameHostAndPath ( left , right ) {
99 try {
100 const a = new URL (left);
101 const b = new URL (right);
102 return a.host. toLowerCase () === b.host. toLowerCase () && a.pathname === b.pathname;
103 } catch {
104 return false ;
105 }
106 }
107
108 function confirmPictures ({ pictures , read } = {}) {
109 if ( ! trustedReads. has (read)) {
110 throw new Error ( 'confirmPictures: read must be the unmodified result of readSourceMediaRecords' );
111 }
112 if ( ! Array. isArray (pictures)) throw new TypeError ( 'confirmPictures: pictures must be an array' );
113
114 const confirmed = [];
115 const reasons = [];
116 const missing = new Set (read.missing);
117 const unreadable = new Set (read.unreadable);
118
119 for ( const picture of pictures) {
120 const sourceId = sourceIdOf (picture && picture.sourceId);
121 let reason = null ;
122 if (sourceId === null ) reason = 'no-source-id' ;
123 else if (missing. has (sourceId)) reason = 'no-source-record' ;
124 else if (unreadable. has (sourceId)) reason = 'source-unreadable' ;
125 else if ( ! Object . prototype .hasOwnProperty. call (read.files, sourceId)) reason = 'no-source-record' ;
126
127 if (reason) {
128 reasons. push (Object. freeze ({ sourceId, reason }));
129 continue ;
130 }
131
132 const sourceUrl = read.files[sourceId];
133 const url = sameHostAndPath (picture.url, sourceUrl) ? picture.url : sourceUrl;
134 confirmed. push ({ ... picture, sourceId, url });
135 }
136
137 if (reasons. length > 0 ) {
138 const firstReason = REASON_ORDER . find (( candidate ) => reasons. some (( entry ) => entry.reason === candidate));
139 return Object. freeze ({
140 confirmed: false ,
141 pictures: Object. freeze ([]),
142 reasons: freezeList (reasons),
143 reason: firstReason,
144 });
145 }
146
147 return Object. freeze ({
148 confirmed: true ,
149 pictures: freezeGallery (confirmed),
150 reasons: Object. freeze ([]),
151 reason: null ,
152 });
153 }
154
155 function declareMediaSource ( source ) {
156 if ( ! isNonEmptyString (source)) throw new TypeError ( 'declareMediaSource: source must be a non-empty string' );
157 const normalized = source. trim ();
158 if (declaredMediaSource === undefined ) {
159 declaredMediaSource = normalized;
160 return normalized;
161 }
162 if (declaredMediaSource !== normalized) {
163 throw new Error ( `declareMediaSource: source is already ${ declaredMediaSource }; refusing to change it to ${ normalized }` );
164 }
165 return declaredMediaSource;
166 }
167
168 function isConfirmedGallery ( list ) {
169 return confirmedGalleries. has (list);
170 }
171
172 function deriveConfirmed ( source , items ) {
173 if ( ! confirmedGalleries. has (source)) {
174 throw new Error ( 'deriveConfirmed: source is not a confirmed gallery' );
175 }
176 if ( ! Array. isArray (items) || items. length !== source. length ) {
177 throw new Error ( 'deriveConfirmed: derived gallery must keep every confirmed URL in order' );
178 }
179 for ( let index = 0 ; index < source. length ; index += 1 ) {
180 const item = items[index];
181 if ( ! item || typeof item !== 'object' || Array. isArray (item)
182 || Object. keys (item). some (( key ) => ! [ 'url' , 'altText' , 'displayName' ]. includes (key))) {
183 throw new Error ( 'deriveConfirmed: only altText and displayName may differ from the confirmed URL items' );
184 }
185 if (source[index].url !== item.url) {
186 throw new Error ( 'deriveConfirmed: derived gallery must keep every confirmed URL in order' );
187 }
188 }
189 return freezeGallery (items);
190 }
191
192 function mediaSourceRequiresConfirmation () {
193 return GUARDED_MEDIA_SOURCES . has (declaredMediaSource);
194 }
195
196 // Keep the module's public export list at the five-capability boundary. The
197 // writer may inspect the declaration through the declaration function, but gains no additional
198 // way to mutate it or to bless a gallery. The inspection hook is immutable: changing a property
199 // on the declaration function must not be another way to switch the run-wide guard off.
200 Object. defineProperty (declareMediaSource, 'requiresConfirmation' , {
201 value: mediaSourceRequiresConfirmation,
202 writable: false ,
203 configurable: false ,
204 });
205
206 module . exports = {
207 readSourceMediaRecords,
208 confirmPictures,
209 declareMediaSource,
210 isConfirmedGallery,
211 deriveConfirmed,
212 };