your-ai-workflow-firebase-os 2.0.8 → 2.0.9
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/{Assets-Dd_96avv.js → Assets-COH4Ji57.js} +5 -5
- package/dist/{Assets-CBBNIHrw.cjs → Assets-D7fDckSi.cjs} +1 -1
- package/dist/{Attachments-C6QrTcNt.cjs → Attachments-BMuQ0CD4.cjs} +1 -1
- package/dist/{Attachments-YyjQmhBx.js → Attachments-Co1UkdWB.js} +2 -2
- package/dist/{Billing-B6f49cGH.cjs → Billing-DkuJdls5.cjs} +1 -1
- package/dist/{Billing-BZafAizg.js → Billing-kk4ZMd3z.js} +4 -4
- package/dist/{BookingCalendar-DYsA71vl.cjs → BookingCalendar-BcvJiyaq.cjs} +1 -1
- package/dist/{BookingCalendar-_xoRa2tL.js → BookingCalendar-Bej__mIs.js} +1 -1
- package/dist/{Button-DSldWACF.js → Button-BGN6q7lY.js} +1 -1
- package/dist/{Button-PyTaaoyu.cjs → Button-Ccy4wFZ1.cjs} +1 -1
- package/dist/{ConfirmModal-CQ73WYUg.cjs → ConfirmModal-BUNtss4a.cjs} +1 -1
- package/dist/{ConfirmModal-C1dM-bzk.js → ConfirmModal-BW9e6Y7d.js} +3 -3
- package/dist/{ContactPopup-D5Hh0bfE.cjs → ContactPopup-D8aSSlmS.cjs} +1 -1
- package/dist/{ContactPopup-CL4AMwK9.js → ContactPopup-Fs7wzrOX.js} +7 -7
- package/dist/{CopyLink-BnYpVy6H.cjs → CopyLink-C5lKLyOU.cjs} +1 -1
- package/dist/{CopyLink-rLr_5eNM.js → CopyLink-CNjDLzOl.js} +2 -2
- package/dist/{DashboardNav-B0G943ux.js → DashboardNav-CEd8gGPm.js} +3 -3
- package/dist/{DashboardNav-DOl9gJuu.cjs → DashboardNav-GgEUpyTa.cjs} +1 -1
- package/dist/{EmailAdmin-CWHMMybh.cjs → EmailAdmin-d_gWd48U.cjs} +1 -1
- package/dist/{EmailAdmin-humrSTzI.js → EmailAdmin-pbQ-f9Ge.js} +9 -9
- package/dist/{EventCard-CrPswwyG.js → EventCard-BSGLYD64.js} +2 -2
- package/dist/{EventCard-6eCfvOA-.cjs → EventCard-DAsGsicK.cjs} +1 -1
- package/dist/{FileViewerPopup-DQmhXMSg.cjs → FileViewerPopup-CkLUYCNo.cjs} +1 -1
- package/dist/{FileViewerPopup-Dnl3td2o.js → FileViewerPopup-gqhRT22v.js} +2 -2
- package/dist/{FormNote-BWGLs9lx.js → FormNote-DJ9HBokL.js} +1 -1
- package/dist/{FormNote-CX2rSBDB.cjs → FormNote-DP0GK9ya.cjs} +1 -1
- package/dist/{FormsAdmin-Tg20B0iA.cjs → FormsAdmin-5KTTp6zo.cjs} +1 -1
- package/dist/{FormsAdmin-CHwmKnYU.js → FormsAdmin-BNI63vEk.js} +9 -9
- package/dist/{Inbox-Cgq4MALU.js → Inbox-D9Y5K8Se.js} +9 -9
- package/dist/{Inbox-BPwIiThg.cjs → Inbox-DjslBakx.cjs} +1 -1
- package/dist/{Loader-DP_dL5qC.js → Loader-CVoz1Zf0.js} +1 -1
- package/dist/{Loader-p3eNQDUU.cjs → Loader-QHqQsXLr.cjs} +1 -1
- package/dist/{ManagePlanButton-BRW7Sevd.cjs → ManagePlanButton-Buwfs2bU.cjs} +1 -1
- package/dist/{ManagePlanButton-CNLCxK-o.js → ManagePlanButton-CE4uyh_7.js} +2 -2
- package/dist/{MyBookings-C_pqsSEk.js → MyBookings-B4DndHV6.js} +7 -7
- package/dist/{MyBookings-BC5sn_fE.cjs → MyBookings-CkEioQIj.cjs} +1 -1
- package/dist/{PageTitle-DFGW71q8.js → PageTitle-DCoUQWw6.js} +2 -2
- package/dist/{PageTitle-BWmE74WQ.cjs → PageTitle-DQkvCB11.cjs} +1 -1
- package/dist/PagesAdmin-D0qgoZtW.js +3 -0
- package/dist/{PagesAdmin-jjnDLYmF.js → PagesAdmin-DzLJWKBJ.js} +21 -21
- package/dist/PagesAdmin-KUxCI1Mr.cjs +1 -0
- package/dist/{PagesAdmin-BZZ3by92.cjs → PagesAdmin-YdYgmuEB.cjs} +1 -1
- package/dist/{PaymentsAdmin-CI0zXraR.cjs → PaymentsAdmin-B5l-f0mp.cjs} +1 -1
- package/dist/{PaymentsAdmin-DeBMmFFo.js → PaymentsAdmin-C3cEOeIs.js} +15 -15
- package/dist/{Popup-DmPEk98h.js → Popup-Bi63lzoS.js} +1 -1
- package/dist/{Popup-nq7YNM4N.cjs → Popup-COnHpbrY.cjs} +1 -1
- package/dist/{ProviderMark-DJ5mYjYn.cjs → ProviderMark--adeeajP.cjs} +1 -1
- package/dist/{ProviderMark-CAeOnBsB.js → ProviderMark-BvEMlZAm.js} +1 -1
- package/dist/{PublishApp-mMMwf_in.js → PublishApp-B9Fo6xN0.js} +7 -7
- package/dist/{PublishApp-DI96gX9b.cjs → PublishApp-Bc82Ijkq.cjs} +1 -1
- package/dist/{Said-DAD7Ctp_.js → Said-BvSRaEfZ.js} +1 -1
- package/dist/{Said-DlJOjNMN.cjs → Said-W3KFi5uX.cjs} +1 -1
- package/dist/{ScheduleAdmin-dWj_N_tf.cjs → ScheduleAdmin-Dh_hW6p4.cjs} +1 -1
- package/dist/{ScheduleAdmin-C4W8Ls0D.js → ScheduleAdmin-bk3GgxKP.js} +14 -14
- package/dist/{SchedulePicker-CwKId9JF.cjs → SchedulePicker-CN0755Ek.cjs} +1 -1
- package/dist/{SchedulePicker-BOSJEcfL.js → SchedulePicker-DO4PewPJ.js} +2 -2
- package/dist/{Setup-DcZzh6Xd.cjs → Setup-R4r6_tbV.cjs} +1 -1
- package/dist/{Setup-NOvtplUS.js → Setup-_G-s5y5Q.js} +3 -3
- package/dist/{SetupPayments-DrXK0LQa.js → SetupPayments-CkolcOtv.js} +5 -5
- package/dist/{SetupPayments-CZ0KqXdR.cjs → SetupPayments-CuFk9REC.cjs} +1 -1
- package/dist/{SignInAdmin-CwIUAJrv.cjs → SignInAdmin-B1yLLaQu.cjs} +1 -1
- package/dist/{SignInAdmin-Cb65ChHL.js → SignInAdmin-BLEn0rId.js} +9 -9
- package/dist/{SupportBar-528kXe-Z.js → SupportBar-DI_XbEsG.js} +4 -4
- package/dist/{SupportBar-CNVXSYwT.cjs → SupportBar-DLWruzQb.cjs} +1 -1
- package/dist/{SystemTabContent-EegzTuLd.cjs → SystemTabContent-BWhRHoXx.cjs} +1 -1
- package/dist/{SystemTabContent-C4azXKMs.js → SystemTabContent-CMyT7mDN.js} +2 -2
- package/dist/{TemplateDrive-XDRPkxIj.cjs → TemplateDrive-BJN-ReA_.cjs} +1 -1
- package/dist/{TemplateDrive-CfPXKMKX.js → TemplateDrive-DNBwL0oy.js} +10 -10
- package/dist/{ThemeAdmin-XLbC9fX7.js → ThemeAdmin-Cnqefrz6.js} +6 -6
- package/dist/ThemeAdmin-CoE_c5-P.cjs +1 -0
- package/dist/ThemeAdmin-DI7QaZ-0.js +2 -0
- package/dist/{ThemeAdmin-BrEozdpZ.cjs → ThemeAdmin-DkgmjW3g.cjs} +1 -1
- package/dist/TimeZoneField-Ckn1pKAL.cjs +11 -0
- package/dist/{TimeZoneField-Dv389mAe.js → TimeZoneField-oIMwb5_X.js} +119 -107
- package/dist/{TransactionsAdmin-X-NjI4NB.js → TransactionsAdmin-Brs-CEcG.js} +11 -11
- package/dist/{TransactionsAdmin-DgsG629u.cjs → TransactionsAdmin-C5wQRR1g.cjs} +1 -1
- package/dist/{Trouble-qjWYeKzC.js → Trouble-CtFq7tjk.js} +1 -1
- package/dist/{Trouble-DokPNHQZ.cjs → Trouble-KQZyf6aY.cjs} +1 -1
- package/dist/{Users-BEjl_Vjc.cjs → Users-B16L9cKI.cjs} +1 -1
- package/dist/{Users-BUGb7-Kz.js → Users-CbI4K_Le.js} +15 -15
- package/dist/{VideoPlayer-BVOkrJBR.cjs → VideoPlayer-CggwpVXH.cjs} +1 -1
- package/dist/{VideoPlayer-DB0oz6lM.js → VideoPlayer-QI1Jc3bS.js} +1 -1
- package/dist/appIcons-C90OKlV5.cjs +130 -0
- package/dist/{appIcons-CUXAX_gJ.js → appIcons-CCArfCDe.js} +761 -735
- package/dist/{arrivalRoads-BZk39p8Z.js → arrivalRoads-CGXm9Ntm.js} +0 -0
- package/dist/{arrivalRoads-CD7pt03E.cjs → arrivalRoads-Cc6AZM-d.cjs} +0 -0
- package/dist/{bookingPages-BxqkyZ-M.js → bookingPages-BFSY3Riv.js} +1 -1
- package/dist/{bookingPages-2fn9xPiA.cjs → bookingPages-Dx1ViiwW.cjs} +1 -1
- package/dist/{contentFallback-C6xLwXTs.js → contentFallback-BGGpI1Ts.js} +2 -2
- package/dist/{contentFallback-DLm_iHo9.cjs → contentFallback-DVCrIUvK.cjs} +1 -1
- package/dist/{email-DxxovRtf.js → email-PBHbXXgR.js} +1 -1
- package/dist/{email-DD48BP42.cjs → email-Sya6gl4F.cjs} +1 -1
- package/dist/lib/basket.d.ts +29 -9
- package/dist/lib/recordPrice.d.ts +1 -1
- package/dist/{notices-Cwon5Q0Y.cjs → notices-CKm9-Pqt.cjs} +1 -1
- package/dist/{notices-C4yqYzBw.js → notices-CysS7BLF.js} +1 -1
- package/dist/{ownerMail-CGmDk7aP.cjs → ownerMail-BS9SjJ2u.cjs} +1 -1
- package/dist/{ownerMail-Chzy5Ehy.js → ownerMail-DepG5-Qw.js} +2 -2
- package/dist/{pageFiles-BClshvXs.cjs → pageFiles-Cn0YQ4Ur.cjs} +1 -1
- package/dist/{pageFiles-D-GYqRgZ.js → pageFiles-ZFPSCDZz.js} +1 -1
- package/dist/{paidInFull-KuStok9_.cjs → paidInFull-CZywIs-V.cjs} +1 -1
- package/dist/{paidInFull-D14Eh0QV.js → paidInFull-mBOh9iwN.js} +1 -1
- package/dist/{paidSessions-Cr_93LGa.js → paidSessions-B8e5RHi4.js} +7 -7
- package/dist/{paidSessions-QXv7SHeb.cjs → paidSessions-D-zHC4oG.cjs} +1 -1
- package/dist/{people-D8nPPMlM.cjs → people-BwWVVLfy.cjs} +1 -1
- package/dist/{people-sk2DcHPi.js → people-Wk2opW4G.js} +1 -1
- package/dist/{publishSteps-DB1Hxr6H.js → publishSteps-Cf5pSB_H.js} +1 -1
- package/dist/{publishSteps-CaZRpC3C.cjs → publishSteps-DvWlF0p_.cjs} +1 -1
- package/dist/{recordLock-Dnz61J-_.js → recordLock-CMOiezNC.js} +1 -1
- package/dist/{recordLock-CLUp3QNh.cjs → recordLock-CiulizO1.cjs} +1 -1
- package/dist/{recordsKey-R5BjLoGQ.cjs → recordsKey-C4E2vOti.cjs} +1 -1
- package/dist/{recordsKey-DHJJWWOo.js → recordsKey-Cd216nq1.js} +2 -2
- package/dist/{registrationFields-COv8jW4n.cjs → registrationFields-DHHTptcl.cjs} +1 -1
- package/dist/{registrationFields-CeTZSerx.js → registrationFields-DdHoMPAQ.js} +1 -1
- package/dist/renderer.cjs.js +1 -1
- package/dist/renderer.es.js +1 -1
- package/dist/{roles-5juCr5sY.cjs → roles-BBDIJzox.cjs} +1 -1
- package/dist/{roles-hzcMnn_M.js → roles-DiwP6-WQ.js} +3 -3
- package/dist/{scheduleData-BpkfnBjP.cjs → scheduleData-BXjNXVHY.cjs} +1 -1
- package/dist/{scheduleData-BLCUm84s.js → scheduleData-DR6A-Q7Z.js} +1 -1
- package/dist/server.cjs.js +1 -1
- package/dist/server.es.js +1 -1
- package/dist/{serviceStatus-5sk8MPjW.js → serviceStatus-C9izcuQz.js} +2 -2
- package/dist/{serviceStatus-1pGLLilF.cjs → serviceStatus-Cde96_OO.cjs} +1 -1
- package/dist/{serviceSwitches-BWnZBooS.js → serviceSwitches-CZHwvVfP.js} +1 -1
- package/dist/{serviceSwitches-Do_pkFCu.cjs → serviceSwitches-mhJbQBrV.cjs} +1 -1
- package/dist/vite.cjs.js +1 -1
- package/dist/vite.es.js +1 -1
- package/dist/your-ai-workflow-firebase-os.cjs.js +8 -8
- package/dist/your-ai-workflow-firebase-os.css +1 -1
- package/dist/your-ai-workflow-firebase-os.es.js +358 -316
- package/package.json +1 -1
- package/src/index.css +41 -6
- package/dist/PagesAdmin-B-XopEi9.js +0 -3
- package/dist/PagesAdmin-nL5ZltFn.cjs +0 -1
- package/dist/ThemeAdmin-CwCiTp2I.js +0 -2
- package/dist/ThemeAdmin-Db5edTYj.cjs +0 -1
- package/dist/TimeZoneField-zR8N2Fj0.cjs +0 -11
- package/dist/appIcons-BNovDw6_.cjs +0 -130
|
@@ -136,23 +136,23 @@ var S = (e) => e.filter((e) => e.kind !== "other" && e.kind !== "helper"), te =
|
|
|
136
136
|
"md",
|
|
137
137
|
"dedent",
|
|
138
138
|
"outdent"
|
|
139
|
-
]),
|
|
139
|
+
]), ne = (e, t) => {
|
|
140
140
|
if (!t || T.has(t)) return !1;
|
|
141
141
|
let n = t.replace(/\$/g, "\\$");
|
|
142
142
|
return RegExp(`\\bimport\\b[^;'"\\\`]*?[{,\\s]${n}\\b[^;'"\\\`]*?\\bfrom\\s*['"]|\\b(?:const|let|var)\\s+(?:[{[][^=]*?\\b)?${n}\\b|\\bfunction\\s+${n}\\b`).test(e);
|
|
143
|
-
},
|
|
143
|
+
}, D = (e, t) => {
|
|
144
144
|
let { char: n, word: r, place: i } = w(e, t);
|
|
145
145
|
return !!n && "(=:[!&|?{;".includes(n) || n === ">" && e[i - 1] === "=" || T.has(r);
|
|
146
|
-
},
|
|
146
|
+
}, re = (e, t, n = -1, r = -1, i = () => e) => {
|
|
147
147
|
if (/[\w$]/.test(e[t - 1] || "")) {
|
|
148
148
|
let { word: n } = w(e, t), r = t - n.length;
|
|
149
|
-
return e[r - 1] === "." || E.has(n) ||
|
|
149
|
+
return e[r - 1] === "." || E.has(n) || ne(e, n) && D(e, r);
|
|
150
150
|
}
|
|
151
151
|
let a = e, o = w(e, t);
|
|
152
152
|
o.place >= n && o.place < r && (a = i(), o = w(a, a.length));
|
|
153
153
|
let { char: s, word: c, place: l } = o, u = l + 1 - c.length;
|
|
154
|
-
return !/[\w$]/.test(s) || T.has(c) || (E.has(c) ||
|
|
155
|
-
},
|
|
154
|
+
return !/[\w$]/.test(s) || T.has(c) || (E.has(c) || ne(e, c)) && D(a, u);
|
|
155
|
+
}, O = (e, t) => {
|
|
156
156
|
let n = e[t + 1];
|
|
157
157
|
if (n === "/" || n === "*" || n === ">") return -1;
|
|
158
158
|
let { char: r, word: i, lineStart: a, place: o } = w(e, t), s = r === ">" && e[o - 1] === "=";
|
|
@@ -177,13 +177,13 @@ var S = (e) => e.filter((e) => e.kind !== "other" && e.kind !== "helper"), te =
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
return -1;
|
|
180
|
-
},
|
|
180
|
+
}, ie = (e, t) => {
|
|
181
181
|
let { char: n, word: r, place: i } = w(e, t < 0 ? e.length : t);
|
|
182
182
|
return r === "from" || r === "import" ? !0 : n === "(" && w(e, i).word === "import";
|
|
183
|
-
},
|
|
183
|
+
}, ae = (e, t, n, r) => {
|
|
184
184
|
let i = t.slice(Math.max(n, r - 200), r);
|
|
185
185
|
return i.length >= 200 || n <= r - 200 ? i : e.slice(-(200 - i.length)) + i;
|
|
186
|
-
},
|
|
186
|
+
}, oe = (e, t) => {
|
|
187
187
|
let n = "", r = 0, i = "", a = -1, o = -1, s = (t, a, o = "") => {
|
|
188
188
|
let s = e.slice(r, t) + o;
|
|
189
189
|
n += s, i = (i + s).slice(-200), r = a;
|
|
@@ -218,20 +218,20 @@ var S = (e) => e.filter((e) => e.kind !== "other" && e.kind !== "helper"), te =
|
|
|
218
218
|
continue;
|
|
219
219
|
}
|
|
220
220
|
if (p === "/") {
|
|
221
|
-
let t =
|
|
221
|
+
let t = O(e, f);
|
|
222
222
|
t > 0 && (f = t);
|
|
223
223
|
continue;
|
|
224
224
|
}
|
|
225
225
|
if (p === "`") {
|
|
226
|
-
if (!
|
|
226
|
+
if (!re(e, f, a, o, () => n)) continue;
|
|
227
227
|
c.push(0);
|
|
228
228
|
} else if (p === "'" || p === "\"") {
|
|
229
229
|
if (/[A-Za-z0-9]/.test(e[f - 1] || "")) continue;
|
|
230
|
-
l = p, u = f, d = t && !
|
|
230
|
+
l = p, u = f, d = t && !ie(ae(i, e, r, f), -1);
|
|
231
231
|
} else c.length && (p === "{" ? c[c.length - 1]++ : p === "}" && c[c.length - 1]--);
|
|
232
232
|
}
|
|
233
233
|
return l && d && s(u + 1, e.length), n + e.slice(r);
|
|
234
|
-
},
|
|
234
|
+
}, se = (e) => [...oe(e, !0).matchAll(/\b(?:from|import)\s*(?:\(\s*)?['"](\.{1,2}\/[^'"]+)['"]/g)].map((e) => e[1]), ce = (e, t) => {
|
|
235
235
|
let i = r.resolve(r.dirname(e), t);
|
|
236
236
|
return [
|
|
237
237
|
i,
|
|
@@ -241,7 +241,7 @@ var S = (e) => e.filter((e) => e.kind !== "other" && e.kind !== "helper"), te =
|
|
|
241
241
|
r.join(i, "index.ts")
|
|
242
242
|
].find((e) => /\.tsx?$/.test(e) && n.existsSync(e) && n.statSync(e).isFile()) ?? null;
|
|
243
243
|
};
|
|
244
|
-
function
|
|
244
|
+
function k(e, t, n) {
|
|
245
245
|
let i = r.join(e, "src"), o = new Set(t.map((e) => r.resolve(e.abs))), u = new Set(["App.tsx", "main.tsx"].map((e) => r.join(i, e)).concat(r.join(e, s))), f = [], p = t.filter((e) => e.kind === "microcomponent" || e.file === c || e.file === l).map((e) => e.abs);
|
|
246
246
|
for (let t = 0; t < te && p.length && f.length < C; t++) {
|
|
247
247
|
let t = [];
|
|
@@ -252,8 +252,8 @@ function A(e, t, n) {
|
|
|
252
252
|
} catch {
|
|
253
253
|
continue;
|
|
254
254
|
}
|
|
255
|
-
for (let n of
|
|
256
|
-
let s =
|
|
255
|
+
for (let n of se(s)) {
|
|
256
|
+
let s = ce(i, n);
|
|
257
257
|
if (!s || o.has(s) || u.has(s)) continue;
|
|
258
258
|
let c = d(r.relative(e, s));
|
|
259
259
|
if (!(!c.startsWith("src/") || c.startsWith(`${a}/`) || c.includes("/node_modules/")) && (o.add(s), f.push({
|
|
@@ -269,24 +269,24 @@ function A(e, t, n) {
|
|
|
269
269
|
}
|
|
270
270
|
return f;
|
|
271
271
|
}
|
|
272
|
-
var
|
|
272
|
+
var A = [{
|
|
273
273
|
test: (e) => /Content\.tsx$/.test(e),
|
|
274
274
|
home: o
|
|
275
275
|
}, {
|
|
276
276
|
test: (e) => /(Tab|Page|Form|Registration)\.config\.tsx?$/.test(e),
|
|
277
277
|
home: a
|
|
278
|
-
}],
|
|
278
|
+
}], le = new Set([
|
|
279
279
|
"node_modules",
|
|
280
280
|
".git",
|
|
281
281
|
"dist",
|
|
282
282
|
"build"
|
|
283
|
-
]),
|
|
283
|
+
]), j = (e, t) => r.relative(e, t).split(r.sep).some((e) => le.has(e)), M = [
|
|
284
284
|
"index.tsx",
|
|
285
285
|
"App.tsx",
|
|
286
286
|
"src/index.tsx",
|
|
287
287
|
"src/main.tsx",
|
|
288
288
|
"src/App.tsx"
|
|
289
|
-
],
|
|
289
|
+
], N = /\.(?:tsx?|jsx?)$/, P = (e, t, i) => {
|
|
290
290
|
let a = i.startsWith("@/") ? [r.join(e, i.slice(2)), r.join(e, "src", i.slice(2))] : i.startsWith("src/") ? [r.join(e, i)] : [r.resolve(r.dirname(t), i)];
|
|
291
291
|
for (let e of a) {
|
|
292
292
|
let t = e.replace(/\.(?:jsx?|tsx?)$/, ""), i = [
|
|
@@ -301,11 +301,11 @@ var j = [{
|
|
|
301
301
|
"index.jsx",
|
|
302
302
|
"index.js"
|
|
303
303
|
].map((t) => r.join(e, t))
|
|
304
|
-
].find((e) =>
|
|
304
|
+
].find((e) => N.test(e) && n.existsSync(e) && n.statSync(e).isFile());
|
|
305
305
|
if (i) return i;
|
|
306
306
|
}
|
|
307
307
|
return null;
|
|
308
|
-
},
|
|
308
|
+
}, F = (e) => [...oe(e, !0).matchAll(/\b(?:from|import)\s*(?:\(\s*)?['"]((?:\.{1,2}|@|src)\/[^'"]+)['"]/g)].map((e) => e[1]), ue = (e) => !/\.[A-Za-z0-9]+$/.test(e) || N.test(e), de = (e, t, i) => {
|
|
309
309
|
if (i.startsWith("@/")) return !0;
|
|
310
310
|
let a = i.startsWith("src/") ? r.join(e, i) : r.resolve(r.dirname(t), i);
|
|
311
311
|
return n.existsSync(a) ? !0 : [
|
|
@@ -315,7 +315,7 @@ var j = [{
|
|
|
315
315
|
".cts"
|
|
316
316
|
].some((e) => n.existsSync(`${a}${e}`));
|
|
317
317
|
}, fe = (e) => {
|
|
318
|
-
let t = [...ee(e).map((e) => r.resolve(e.abs)), ...
|
|
318
|
+
let t = [...ee(e).map((e) => r.resolve(e.abs)), ...M.map((t) => r.resolve(e, t)).filter((e) => n.existsSync(e))], i = new Set(t), a = [...i], o = !0;
|
|
319
319
|
for (; a.length;) {
|
|
320
320
|
let t = a.pop(), r = "";
|
|
321
321
|
try {
|
|
@@ -323,13 +323,13 @@ var j = [{
|
|
|
323
323
|
} catch {
|
|
324
324
|
continue;
|
|
325
325
|
}
|
|
326
|
-
for (let n of
|
|
327
|
-
let r =
|
|
326
|
+
for (let n of F(r)) {
|
|
327
|
+
let r = P(e, t, n);
|
|
328
328
|
if (!r) {
|
|
329
329
|
ue(n) && de(e, t, n) && (o = !1);
|
|
330
330
|
continue;
|
|
331
331
|
}
|
|
332
|
-
i.has(r) ||
|
|
332
|
+
i.has(r) || j(e, r) || (i.add(r), a.push(r));
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
return {
|
|
@@ -341,18 +341,18 @@ var j = [{
|
|
|
341
341
|
if (!(o > 8 || !n.existsSync(a))) for (let s of n.readdirSync(a, { withFileTypes: !0 })) {
|
|
342
342
|
let c = r.join(a, s.name);
|
|
343
343
|
if (s.isDirectory()) {
|
|
344
|
-
|
|
344
|
+
le.has(s.name) || i(c, o + 1);
|
|
345
345
|
continue;
|
|
346
346
|
}
|
|
347
|
-
if (!
|
|
347
|
+
if (!N.test(s.name)) continue;
|
|
348
348
|
let l = "";
|
|
349
349
|
try {
|
|
350
350
|
l = n.readFileSync(c, "utf8");
|
|
351
351
|
} catch {
|
|
352
352
|
continue;
|
|
353
353
|
}
|
|
354
|
-
for (let n of
|
|
355
|
-
let r =
|
|
354
|
+
for (let n of F(l)) {
|
|
355
|
+
let r = P(e, c, n);
|
|
356
356
|
r && t.add(r);
|
|
357
357
|
}
|
|
358
358
|
}
|
|
@@ -364,11 +364,11 @@ function me(e) {
|
|
|
364
364
|
if (!(c > 6 || !n.existsSync(t))) for (let l of n.readdirSync(t, { withFileTypes: !0 })) {
|
|
365
365
|
let n = r.join(t, l.name), u = d(r.relative(e, n));
|
|
366
366
|
if (l.isDirectory()) {
|
|
367
|
-
|
|
367
|
+
le.has(l.name) || s(n, c + 1);
|
|
368
368
|
continue;
|
|
369
369
|
}
|
|
370
370
|
if (u === `${a}/${l.name}` || u === `${o}/${l.name}`) continue;
|
|
371
|
-
let f =
|
|
371
|
+
let f = A.find(({ test: e }) => e(l.name));
|
|
372
372
|
f && i.push({
|
|
373
373
|
abs: n,
|
|
374
374
|
rel: u,
|
|
@@ -384,9 +384,9 @@ function me(e) {
|
|
|
384
384
|
}
|
|
385
385
|
var he = (e) => {
|
|
386
386
|
let t = r.basename(e);
|
|
387
|
-
return
|
|
387
|
+
return A.some((e) => e.test(t));
|
|
388
388
|
}, I = (e) => JSON.stringify(e), ge = "// Self-register globally so the library can read it without props.\n// MERGE, never replace: a plain assignment here or in the library's own\n// registries silently wiped every consumer-defined page, form and\n// microcomponent. The project's entries win, whichever ran first — the\n// library's registries let existing entries win, so they never undo these.\nif (typeof window !== 'undefined') {\n window.__FBOS_PAGES__ = { ...(window.__FBOS_PAGES__ || {}), ...pages };\n window.__FBOS_FORMS__ = { ...(window.__FBOS_FORMS__ || {}), ...forms };\n window.__FBOS_REGISTRATION__ = { ...(window.__FBOS_REGISTRATION__ || {}), ...registration };\n window.__FBOS_MICROCOMPONENTS__ = { ...(window.__FBOS_MICROCOMPONENTS__ || {}), ...microcomponents };\n}";
|
|
389
|
-
function
|
|
389
|
+
function _e(e, t = {}) {
|
|
390
390
|
let n = [], r = [], a = [], o = [], s = [], c = S(e);
|
|
391
391
|
return t.serve && n.push(`const loaded = await Promise.allSettled([
|
|
392
392
|
${c.map((e) => ` import(${I(d(e.abs))}),`).join("\n")}
|
|
@@ -495,7 +495,7 @@ export const registrationRegistry = registration;
|
|
|
495
495
|
export const microcomponentRegistry = microcomponents;
|
|
496
496
|
`;
|
|
497
497
|
}
|
|
498
|
-
var
|
|
498
|
+
var ve = (e) => _e(ee(e)), ye = /\b(?:useAiChat|askAi|useAiEnabled|useAiModels)\b|generativelanguage\.googleapis\.com|api\.anthropic\.com|api\.openai\.com|@google\/genai|@google\/generative-ai|@anthropic-ai\/sdk|from\s+['"]openai['"]|(?:GEMINI_API_KEY|GEMINI_KEY|CLAUDE_KEY|OPENAI_KEY|ANTHROPIC_API_KEY|OPENAI_API_KEY)\b|fetch\(\s*['"`]\/api\/ai/, be = (e, t) => `${e}: ${t.trim()} puts AI on a page, and this app has no AI for pages right now — delete it and everything that draws it, and build the rest of the page as normal. Nothing is exported under useAiChat, askAi, useAiEnabled or useAiModels, and a page never calls Gemini, Claude or OpenAI itself or holds a key: a key in a page is published to everyone who opens it. If the owner asked for AI, tell them the app does not put AI on pages yet.`, xe = (e) => e.match(ye)?.[0] ?? null, L = /* @__PURE__ */ "AddressField.AddressParts.AdminNotifications.ArrivingEntry.ArrivingPayment.Article.ArticleProps.Bars.BarsItem.BarsProps.Basket.BasketButton.BasketButtonProps.BasketItem.BasketLine.BasketProps.BasketState.BlockedDate.Booking.BookingsState.Button.BuyButton.BuyButtonProps.Buyer.BuyerDetails.BuyerDetailsProps.CancelInput.Card.CaseRecord.CaseState.CasesFromFormOptions.CasesState.Checkout.CheckoutExtra.CheckoutItem.CheckoutOptions.CheckoutProps.CheckoutReturn.CheckoutReturnProps.ConfirmModal.ConfirmModalProps.ContactPopup.CountLimits.CustomSelect.DashboardNav.DataTable.DataTableColumn.DataTableProps.DayKey.EMPTY_ADDRESS.EVERYONE.EmailLook.EmailOptions.EmailStatus.EventCard.FileViewer.FileViewerPopup.FileViewerPopupProps.FileViewerProps.FirebaseOS.FirebaseOSConfig.FirebaseOSProps.HandOver.HandOverProps.History.HistoryLine.HistoryMore.HistoryProps.HistoryState.Holder.Input.Lightbox.LightboxPicture.LightboxProps.Loader.LoaderProps.MadeBy.MadeByProps.ManagePlanButton.MemberSchedule.MenuPage.MyPayment.MyPaymentsState.MySubscription.Navbar.NotifyInput.NumberStepper.NumberStepperProps.OrderRecord.PayButton.PaymentConfirmation.PeopleState.Person.PhoneInputField.Picture.PictureProps.Pipeline.PipelineProps.PipelineStage.PlanOnOffer.PlanPrice.PlanRhythm.Plans.PlansProps.PlansState.Popup.PopupProps.PublicMenu.PublicMenuProps.Reveal.RevealList.RevealListProps.RevealProps.SaleCharge.SaleSubscription.SalesState.SayOptions.SchedulesState.SectionHeading.SendTarget.Skeleton.Slot.SlotOptions.Sortable.SortableProps.StepBar.StepBarProps.StepInfo.StepsState.Stock.StockChange.StockFromOrdersOptions.StockOrder.StockVariant.Stocked.Swap.SwapProps.Swatch.SwatchField.SwatchFieldProps.SwatchProps.TALLY_COUNTERS.TakenTime.Tallies.TallyCounter.TallyCounts.TextArea.TextAreaProps.TimeChips.TimeWindow.UploadBox.UploadBoxProps.VideoPlayer.VideoPlayerProps.acceptBooking.auth.availableSlots.availableSlotsFor.barShares.blockedWindows.bookingPath.canEdit.canSee.cancelBooking.composeAddress.confirmPayment.countOf.dayBlocked.daysLine.db.emailAnyone.emailOwner.emailPerson.emailStatus.eventName.forPerson.forRole.hasAddress.historyCollection.holderOf.hoursSummary.isPublished.keysOf.logHistory.madeBy.meetPath.money.notify.oneEach.openBillingPortal.partySlots.payPath.publish.registerMicrocomponent.say.scheduleName.scheduleTitle.sendTo.shadeOf.shadesOf.slotsFor.startCheckout.storage.takeBack.tally.timeZoneLabel.togetherSlots.troubleSentence.unpublish.unsay.useAuth.useBasket.useBookings.useCase.useCases.useCasesFromForm.useConfig.useHistory.useMemberSchedules.useMyBookings.useMyPayments.usePaymentsEnabled.usePeople.usePlans.usePublicCopy.usePublished.useSales.useSteps.useStock.useStockFromOrders.useTakenSlots.useTallies.useTeamHours.useTheme.useVisitorState.whenWords.withAccess.withStockSetAt".split("."), R = (e) => {
|
|
499
499
|
let t = e.split(""), n = [], r = "";
|
|
500
500
|
for (let i = 0; i < e.length; i++) {
|
|
501
501
|
let a = e[i];
|
|
@@ -540,7 +540,7 @@ var _e = (e) => L(ee(e)), ve = /\b(?:useAiChat|askAi|useAiEnabled|useAiModels)\b
|
|
|
540
540
|
}
|
|
541
541
|
}
|
|
542
542
|
return t.join("");
|
|
543
|
-
},
|
|
543
|
+
}, Se = (e) => e.replace(/(?<!=)>([^<>{};=]*)</g, (e, t) => `>${t.replace(/[^\n]/g, " ")}<`), z = (e, t) => {
|
|
544
544
|
let n = {
|
|
545
545
|
"(": ")",
|
|
546
546
|
"[": "]",
|
|
@@ -554,7 +554,7 @@ var _e = (e) => L(ee(e)), ve = /\b(?:useAiChat|askAi|useAiEnabled|useAiModels)\b
|
|
|
554
554
|
else if ((t === ")" || t === "]" || t === "}") && (r--, r === 0)) return t === n ? i : -1;
|
|
555
555
|
}
|
|
556
556
|
return -1;
|
|
557
|
-
},
|
|
557
|
+
}, Ce = /[A-Za-z_$][\w$]*/g, we = String.raw`(?:<(?:[^<>()]|<[^<>()]*>)*>\s*)?`, Te = new Set([
|
|
558
558
|
"return",
|
|
559
559
|
"await",
|
|
560
560
|
"typeof",
|
|
@@ -568,7 +568,7 @@ var _e = (e) => L(ee(e)), ve = /\b(?:useAiChat|askAi|useAiEnabled|useAiModels)\b
|
|
|
568
568
|
"throw",
|
|
569
569
|
"delete",
|
|
570
570
|
"void"
|
|
571
|
-
]),
|
|
571
|
+
]), Ee = (e, t, n) => {
|
|
572
572
|
let r = t - 1;
|
|
573
573
|
for (; r >= 0 && /[ \t]/.test(e[r]);) r--;
|
|
574
574
|
if (e[r] === "\n" || e[r] === "\r") {
|
|
@@ -576,26 +576,26 @@ var _e = (e) => L(ee(e)), ve = /\b(?:useAiChat|askAi|useAiEnabled|useAiModels)\b
|
|
|
576
576
|
for (; r >= 0 && /\s/.test(e[r]);) r--;
|
|
577
577
|
}
|
|
578
578
|
let i = /[A-Za-z_$][\w$]*$/.exec(e.slice(Math.max(0, r - 40), r + 1));
|
|
579
|
-
return !!i && !
|
|
580
|
-
},
|
|
579
|
+
return !!i && !Te.has(i[0]);
|
|
580
|
+
}, De = (e, t) => {
|
|
581
581
|
let n = t - 1;
|
|
582
582
|
for (; n >= 0 && /\s/.test(e[n]);) n--;
|
|
583
583
|
if (/[?:=(![&|+\-*/%<>]/.test(e[n] || "") || /\b(?:await|return|yield|typeof|void|new|case|in|of|else|throw|delete)$/.test(e.slice(Math.max(0, n - 9), n + 1))) return !1;
|
|
584
|
-
let r = e.indexOf("(", t), i = r < 0 ? -1 :
|
|
584
|
+
let r = e.indexOf("(", t), i = r < 0 ? -1 : z(e, r);
|
|
585
585
|
return i > 0 && /^\s*(?::[^={;]*)?\{/.test(e.slice(i + 1, i + 200));
|
|
586
586
|
};
|
|
587
|
-
function
|
|
587
|
+
function Oe(e) {
|
|
588
588
|
let t = /* @__PURE__ */ new Set(), n = (e) => {
|
|
589
|
-
for (let n of e.match(
|
|
589
|
+
for (let n of e.match(Ce) || []) t.add(n);
|
|
590
590
|
};
|
|
591
591
|
for (let t of e.matchAll(/\bimport\s+(?:type\s+)?([\s\S]*?)\s+from\s*["'`]/g)) n(t[1]);
|
|
592
592
|
for (let n of e.matchAll(/\b(?:const|let|var|function\*?|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/g)) t.add(n[1]);
|
|
593
593
|
for (let t of e.matchAll(/\b(?:const|let|var)\s*([[{])/g)) {
|
|
594
|
-
let r = (t.index ?? 0) + t[0].length - 1, i =
|
|
594
|
+
let r = (t.index ?? 0) + t[0].length - 1, i = z(e, r);
|
|
595
595
|
i > r && n(e.slice(r, i));
|
|
596
596
|
}
|
|
597
597
|
for (let t of e.matchAll(/\bfunction\b[^(]*\(/g)) {
|
|
598
|
-
let r = (t.index ?? 0) + t[0].length - 1, i =
|
|
598
|
+
let r = (t.index ?? 0) + t[0].length - 1, i = z(e, r);
|
|
599
599
|
i > r && n(e.slice(r, i));
|
|
600
600
|
}
|
|
601
601
|
for (let t of e.matchAll(/\)\s*(?::[^=;{]*?)?=>/g)) {
|
|
@@ -611,17 +611,17 @@ function De(e) {
|
|
|
611
611
|
for (let n of e.matchAll(/<\s*([A-Z][\w$]*)\s*(?:extends\b|,|>\s*\()/g)) t.add(n[1]);
|
|
612
612
|
return t;
|
|
613
613
|
}
|
|
614
|
-
var
|
|
614
|
+
var ke = (e) => {
|
|
615
615
|
let t = e;
|
|
616
616
|
for (let e = 0; e < 200; e++) {
|
|
617
617
|
let e = /\b(?:interface\s+[A-Za-z_$][\w$]*[^{=;]*|type\s+[A-Za-z_$][\w$]*(?:<[^>]*>)?\s*=\s*)\{/.exec(t);
|
|
618
618
|
if (!e) break;
|
|
619
|
-
let n = (e.index ?? 0) + e[0].length - 1, r =
|
|
619
|
+
let n = (e.index ?? 0) + e[0].length - 1, r = z(t, n);
|
|
620
620
|
if (r < 0) break;
|
|
621
621
|
t = `${t.slice(0, e.index)}${" ".repeat(r + 1 - (e.index ?? 0))}${t.slice(r + 1)}`;
|
|
622
622
|
}
|
|
623
623
|
return t;
|
|
624
|
-
},
|
|
624
|
+
}, Ae = new Set([
|
|
625
625
|
"useState",
|
|
626
626
|
"useEffect",
|
|
627
627
|
"useMemo",
|
|
@@ -636,7 +636,7 @@ var Oe = (e) => {
|
|
|
636
636
|
"Fragment",
|
|
637
637
|
"Suspense",
|
|
638
638
|
"React"
|
|
639
|
-
]),
|
|
639
|
+
]), je = /(?<![\w$.])React\s*\.\s*(?:use[A-Z][\w$]*|use|createElement|cloneElement|memo|forwardRef|lazy|Fragment|StrictMode|Suspense|Children|Component|PureComponent|createContext|createRef|isValidElement|startTransition)(?![\w$])/, Me = new Set([
|
|
640
640
|
"Link",
|
|
641
641
|
"NavLink",
|
|
642
642
|
"Navigate",
|
|
@@ -644,7 +644,7 @@ var Oe = (e) => {
|
|
|
644
644
|
"useSearchParams",
|
|
645
645
|
"useParams",
|
|
646
646
|
"useLocation"
|
|
647
|
-
]),
|
|
647
|
+
]), Ne = new Set([
|
|
648
648
|
"collection",
|
|
649
649
|
"doc",
|
|
650
650
|
"query",
|
|
@@ -666,32 +666,32 @@ var Oe = (e) => {
|
|
|
666
666
|
"writeBatch",
|
|
667
667
|
"runTransaction",
|
|
668
668
|
"documentId"
|
|
669
|
-
]),
|
|
670
|
-
function
|
|
669
|
+
]), Pe = new Set(L), Fe = (e) => Ae.has(e) ? e === "React" ? "import React from 'react'" : "'react'" : Me.has(e) ? "'react-router-dom'" : Ne.has(e) ? "'firebase/firestore'" : Pe.has(e) ? "'your-ai-workflow-firebase-os'" : H.has(e) ? "'lucide-react'" : "";
|
|
670
|
+
function Ie(e) {
|
|
671
671
|
if (!/^\s*import\b/m.test(e)) return [];
|
|
672
|
-
let t =
|
|
672
|
+
let t = ke(R(e)), n = Oe(t), r = Se(t), i = /* @__PURE__ */ new Set();
|
|
673
673
|
for (let e of t.matchAll(/<([A-Z][\w$]*)(?=[\s/>.])/g)) {
|
|
674
674
|
let r = t.slice(Math.max(0, (e.index ?? 0) - 12), e.index).replace(/\s+$/, "");
|
|
675
675
|
/[\w$.)\]]$/.test(r) && !/\b(?:return|yield|case|default|else|do|in|of)$/.test(r) || n.has(e[1]) || i.add(e[1]);
|
|
676
676
|
}
|
|
677
|
-
for (let e of r.matchAll(new RegExp(String.raw`(?<![\w$.])(use[A-Z][\w$]*)\s*${
|
|
678
|
-
|
|
679
|
-
let a = (r.match(/;[ \t\r]*$/gm) || []).length >= 3, o = [...
|
|
680
|
-
for (let e of o) n.has(e) || (e === "db" ? /(?<![\w$.])db(?![\w$])(?!\s*:)/.test(r) : [...r.matchAll(RegExp(`(?<![\\w$.])${e}\\s*\\(`, "g"))].some((e) => !
|
|
677
|
+
for (let e of r.matchAll(new RegExp(String.raw`(?<![\w$.])(use[A-Z][\w$]*)\s*${we}\(`, "g"))) !n.has(e[1]) && !De(r, e.index ?? 0) && i.add(e[1]);
|
|
678
|
+
je.test(r) && !n.has("React") && i.add("React");
|
|
679
|
+
let a = (r.match(/;[ \t\r]*$/gm) || []).length >= 3, o = [...Ne, ...[...Pe].filter((e) => /^[a-z]/.test(e))];
|
|
680
|
+
for (let e of o) n.has(e) || (e === "db" ? /(?<![\w$.])db(?![\w$])(?!\s*:)/.test(r) : [...r.matchAll(RegExp(`(?<![\\w$.])${e}\\s*\\(`, "g"))].some((e) => !Ee(r, e.index ?? 0, a) && !De(r, e.index ?? 0))) && i.add(e);
|
|
681
681
|
return [...i];
|
|
682
682
|
}
|
|
683
|
-
var
|
|
684
|
-
let n = t.filter((e) =>
|
|
683
|
+
var Le = (e, t) => {
|
|
684
|
+
let n = t.filter((e) => Fe(e)), r = t.filter((e) => !Fe(e)), i = n.map((e) => e === "React" ? Fe(e) : `${e} from ${Fe(e)}`);
|
|
685
685
|
return `${e}: ${t.map((e) => /^[A-Z]/.test(e) && e !== "React" ? `<${e}>` : e).join(", ")} ${t.length > 1 ? "are" : "is"} used here and never imported or defined — the page stops with "${t[0]} is not defined" the moment that line runs, and the owner sees "This part stopped". ${i.length ? `Add ${i.length > 1 ? "each" : "it"} to the import line it belongs to: ${i.join("; ")}.` : ""}${r.length ? ` ${r.map((e) => `<${e}>`).join(", ")} ${r.length > 1 ? "are" : "is"} not a piece of the app: define ${r.length > 1 ? "each" : "it"} in this file, above the page, or draw what it was meant to draw inline.` : ""} Change nothing else.`;
|
|
686
686
|
};
|
|
687
|
-
function
|
|
687
|
+
function Re(e) {
|
|
688
688
|
let t = [];
|
|
689
689
|
for (let n of e.matchAll(/\b(?:function\s+([A-Z][\w$]*)\s*(?:<[^>]*>)?\s*\(|(?:const|let)\s+([A-Z][\w$]*)\s*(?::[^=]*)?=\s*(?:React\.)?(?:memo\s*\(\s*|forwardRef\s*(?:<[^>]*>)?\s*\(\s*)?(?:async\s*)?(?:function\s*\w*\s*)?\()/g)) {
|
|
690
|
-
let r = n[1] || n[2], i = (n.index ?? 0) + n[0].length - 1, a =
|
|
690
|
+
let r = n[1] || n[2], i = (n.index ?? 0) + n[0].length - 1, a = z(e, i);
|
|
691
691
|
if (a < 0) continue;
|
|
692
692
|
let o = e.slice(a + 1), s = /^\s*(?::[^={]*)?(?:=>)?\s*\{/.exec(o) || (n[2] ? /^\s*(?::[^={]*)?=>\s*\(/.exec(o) : null);
|
|
693
693
|
if (!s) continue;
|
|
694
|
-
let c = a + 1 + s[0].length - 1, l =
|
|
694
|
+
let c = a + 1 + s[0].length - 1, l = z(e, c);
|
|
695
695
|
l < 0 || t.push({
|
|
696
696
|
name: r,
|
|
697
697
|
start: c + 1,
|
|
@@ -701,7 +701,7 @@ function Le(e) {
|
|
|
701
701
|
}
|
|
702
702
|
return t;
|
|
703
703
|
}
|
|
704
|
-
var
|
|
704
|
+
var ze = (e) => {
|
|
705
705
|
let t = [], n = 0;
|
|
706
706
|
for (let r of e) {
|
|
707
707
|
if (r === "(" || r === "[" || r === "{") {
|
|
@@ -712,7 +712,7 @@ var Re = (e) => {
|
|
|
712
712
|
}
|
|
713
713
|
return t;
|
|
714
714
|
};
|
|
715
|
-
function
|
|
715
|
+
function Be(e, t) {
|
|
716
716
|
for (let n of e.matchAll(/\breturn\b/g)) {
|
|
717
717
|
let r = n.index ?? 0;
|
|
718
718
|
if (t[r] === 0) {
|
|
@@ -724,23 +724,23 @@ function ze(e, t) {
|
|
|
724
724
|
let n = e.lastIndexOf("{", r);
|
|
725
725
|
if (n < 0 || t[n] !== 0) continue;
|
|
726
726
|
let i = e.slice(Math.max(e.lastIndexOf(";", n), e.lastIndexOf("}", n - 1)) + 1, n);
|
|
727
|
-
if (/^\s*(?:else\s+)?if\s*\([\s\S]*\)\s*$|^\s*else\s*$/.test(i)) return
|
|
727
|
+
if (/^\s*(?:else\s+)?if\s*\([\s\S]*\)\s*$|^\s*else\s*$/.test(i)) return z(e, n);
|
|
728
728
|
}
|
|
729
729
|
}
|
|
730
730
|
return -1;
|
|
731
731
|
}
|
|
732
|
-
function
|
|
733
|
-
let t =
|
|
734
|
-
for (let { name: e, body: r } of
|
|
735
|
-
let t =
|
|
736
|
-
for (let a of r.matchAll(new RegExp(String.raw`(?<![\w$.])(?:React\s*\.\s*)?(use[A-Z][\w$]*)\s*${
|
|
732
|
+
function Ve(e) {
|
|
733
|
+
let t = R(e), n = /* @__PURE__ */ new Set();
|
|
734
|
+
for (let { name: e, body: r } of Re(t)) {
|
|
735
|
+
let t = ze(r), i = Be(r, t);
|
|
736
|
+
for (let a of r.matchAll(new RegExp(String.raw`(?<![\w$.])(?:React\s*\.\s*)?(use[A-Z][\w$]*)\s*${we}\(`, "g"))) {
|
|
737
737
|
let o = a.index ?? 0;
|
|
738
|
-
i >= 0 && o > i && t[o] === 0 ? n.add(`${a[1]}() in ${e}, below its early return`) :
|
|
738
|
+
i >= 0 && o > i && t[o] === 0 ? n.add(`${a[1]}() in ${e}, below its early return`) : He(r, o) && n.add(`${a[1]}() inside a .map( in ${e}`);
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
741
|
return [...n];
|
|
742
742
|
}
|
|
743
|
-
var
|
|
743
|
+
var He = (e, t) => {
|
|
744
744
|
let n = 0;
|
|
745
745
|
for (let r = t - 1; r >= 0; r--) {
|
|
746
746
|
let t = e[r];
|
|
@@ -754,28 +754,28 @@ var Ve = (e, t) => {
|
|
|
754
754
|
}
|
|
755
755
|
}
|
|
756
756
|
return !1;
|
|
757
|
-
},
|
|
758
|
-
function
|
|
759
|
-
let t =
|
|
760
|
-
for (let { body: e } of
|
|
761
|
-
let t =
|
|
762
|
-
for (let r of
|
|
757
|
+
}, Ue = (e, t) => `${e}: ${t.join("; ")} — React counts a component's hooks every time it draws it, and ${t.some((e) => e.includes(".map(")) ? "a hook per row is one more the day the list grows" : "a hook below an early return is one more the moment that return stops happening"} — the page stops with "Rendered more hooks than during the previous render". Move every hook (useState, useEffect, useMemo, the app's own use… calls) to the top of the component, above the first return, and draw the loading line after them; a row that needs state of its own is a component of its own, defined above the page and drawn inside the .map(. Change nothing else.`, We = /<(?:input|Input|textarea|TextArea|select|CustomSelect|AddressField|PhoneInputField|NumberStepper|SwatchField|Picture|UploadBox)\b/;
|
|
758
|
+
function Ge(e) {
|
|
759
|
+
let t = R(e), n = /* @__PURE__ */ new Set();
|
|
760
|
+
for (let { body: e } of Re(t)) {
|
|
761
|
+
let t = ze(e);
|
|
762
|
+
for (let r of Re(e)) {
|
|
763
763
|
let i = e.lastIndexOf(r.name, r.start);
|
|
764
|
-
i < 0 || t[i] !== 0 ||
|
|
764
|
+
i < 0 || t[i] !== 0 || We.test(r.body) && RegExp(`<${r.name}[\\s/>]`).test(e) && n.add(r.name);
|
|
765
765
|
}
|
|
766
766
|
}
|
|
767
767
|
return [...n];
|
|
768
768
|
}
|
|
769
|
-
var
|
|
770
|
-
function
|
|
771
|
-
return !/\bactions\s*\??\.\s*records\b/.test(e) || /\b(?:onSnapshot|getDocs?|useCases|usePublished|useCase)\s*\(/.test(e) ? [] : [...new Set([...e.matchAll(/\b(createdAt|editedAt|stockSetAt)\s*(?:\?\.|\.)\s*(toDate\s*\(|seconds\b|toMillis\s*\()/g)].filter((t) => !
|
|
769
|
+
var Ke = (e, t) => `${e}: ${t.map((e) => `<${e}>`).join(", ")} ${t.length > 1 ? "are components" : "is a component"} written INSIDE the page, so React makes ${t.length > 1 ? "each" : "it"} anew every time the page draws — and throws away what was in it: a box loses the cursor after every letter typed, and every picture in it starts loading again at each key pressed. Move ${t.length > 1 ? "each one" : "it"}, whole, above the page's own function, and hand it what it used from the page as props (<${t[0]} value={value} onChange={setValue} />). Change nothing else about what it draws.`;
|
|
770
|
+
function qe(e) {
|
|
771
|
+
return !/\bactions\s*\??\.\s*records\b/.test(e) || /\b(?:onSnapshot|getDocs?|useCases|usePublished|useCase)\s*\(/.test(e) ? [] : [...new Set([...e.matchAll(/\b(createdAt|editedAt|stockSetAt)\s*(?:\?\.|\.)\s*(toDate\s*\(|seconds\b|toMillis\s*\()/g)].filter((t) => !Je(e, t[1])).map((e) => `${e[1]}.${e[2].replace(/\s*\($/, "()")}`))];
|
|
772
772
|
}
|
|
773
|
-
var
|
|
773
|
+
var Je = (e, t) => new RegExp(String.raw`\b${t}\s*\??\.\s*(?:toDate|toMillis)\b(?!\s*\()`).test(e) || new RegExp(String.raw`\b${t}\s*\??\.\s*seconds\s*(?:\?|&&|\|\|)`).test(e) || new RegExp(String.raw`\btypeof\s+[\w$.?[\]]*\b${t}\b|\b${t}\s+instanceof\b|['"](?:toDate|toMillis|seconds)['"]\s+in\s+[\w$.?[\]]*\b${t}\b`).test(e), Ye = (e, t) => {
|
|
774
774
|
let [n, r] = t[0].split("."), i = t.some((e) => /\(\)$/.test(e)) ? "the page stops as soon as there is one record on it" : "every date on it reads \"Invalid Date\"";
|
|
775
775
|
return `${e}: ${t.join(", ")} reads a stamp on the page's records as a Firestore Timestamp — the app hands actions.records with createdAt, editedAt and stockSetAt already in milliseconds, a plain number, and a number has no ${r}: ${i}. Read it as new Date(record.${n}) (or format it with toLocaleDateString on that). Change nothing else.`;
|
|
776
776
|
};
|
|
777
|
-
function
|
|
778
|
-
let t = [], n =
|
|
777
|
+
function Xe(e) {
|
|
778
|
+
let t = [], n = R(e);
|
|
779
779
|
for (let r of n.matchAll(/<(BuyButton|PayButton)\b/g)) {
|
|
780
780
|
let i = (r.index ?? 0) + r[0].length;
|
|
781
781
|
for (let e = 0; i < n.length; i++) if (n[i] === "{") e++;
|
|
@@ -783,14 +783,14 @@ function Ye(e) {
|
|
|
783
783
|
else if (n[i] === ">" && e === 0) break;
|
|
784
784
|
let a = /\bamount\s*=\s*\{/.exec(n.slice(r.index, i));
|
|
785
785
|
if (!a) continue;
|
|
786
|
-
let o = (r.index ?? 0) + a.index + a[0].length - 1, s =
|
|
786
|
+
let o = (r.index ?? 0) + a.index + a[0].length - 1, s = z(n, o);
|
|
787
787
|
if (s < 0) continue;
|
|
788
788
|
let c = e.slice(o + 1, s).replace(/\s+/g, " ").trim(), l = n.slice(o + 1, s).replace(/\s+/g, " ").trim();
|
|
789
|
-
|
|
789
|
+
et(l) ? t.push(`<${r[1]} amount={${c}}> (in cents)`) : at(l) && t.push(`<${r[1]} amount={${c}}> (clamped)`);
|
|
790
790
|
}
|
|
791
791
|
return [...new Set(t)];
|
|
792
792
|
}
|
|
793
|
-
var
|
|
793
|
+
var Ze = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).map((e) => e.replace(/s$/, "")), Qe = new Set([
|
|
794
794
|
"total",
|
|
795
795
|
"subtotal",
|
|
796
796
|
"price",
|
|
@@ -811,7 +811,7 @@ var Xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^
|
|
|
811
811
|
"pound",
|
|
812
812
|
"fare",
|
|
813
813
|
"tip"
|
|
814
|
-
]),
|
|
814
|
+
]), $e = new Set([
|
|
815
815
|
"count",
|
|
816
816
|
"qty",
|
|
817
817
|
"quantity",
|
|
@@ -833,27 +833,27 @@ var Xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^
|
|
|
833
833
|
"children",
|
|
834
834
|
"number",
|
|
835
835
|
"num"
|
|
836
|
-
]),
|
|
836
|
+
]), B = (e) => {
|
|
837
837
|
if (/^[\w$.?]+$/.test(e)) {
|
|
838
|
-
let t =
|
|
839
|
-
return t.indexOf("per") > 0 && (t = t.slice(0, t.indexOf("per"))), t.some((e) =>
|
|
838
|
+
let t = Ze(e.split(/\??\./).pop() || "");
|
|
839
|
+
return t.indexOf("per") > 0 && (t = t.slice(0, t.indexOf("per"))), t.some((e) => Qe.has(e)) && !$e.has(t[t.length - 1]);
|
|
840
840
|
}
|
|
841
|
-
return (e.match(
|
|
842
|
-
},
|
|
841
|
+
return (e.match(Ce) || []).some((e) => Ze(e).some((e) => Qe.has(e)));
|
|
842
|
+
}, et = (e) => {
|
|
843
843
|
if (/\/\s*100\b/.test(e)) return !1;
|
|
844
844
|
for (let t of e.matchAll(/(?<![\w$.])100(?![\w$.])/g)) {
|
|
845
|
-
let n = t.index ?? 0, r =
|
|
846
|
-
if (!(r.next !== "*" && i.next !== "*") && [...r.operands, ...i.operands].some(
|
|
845
|
+
let n = t.index ?? 0, r = tt(e, n), i = nt(e, n + t[0].length);
|
|
846
|
+
if (!(r.next !== "*" && i.next !== "*") && [...r.operands, ...i.operands].some(B)) return !0;
|
|
847
847
|
}
|
|
848
848
|
return !1;
|
|
849
|
-
},
|
|
849
|
+
}, tt = (e, t) => {
|
|
850
850
|
let n = [], r = "";
|
|
851
851
|
for (let i = t;;) {
|
|
852
852
|
let t = i - 1;
|
|
853
853
|
for (; t >= 0 && e[t] === " ";) t--;
|
|
854
854
|
if (e[t] !== "*" && e[t] !== "/") break;
|
|
855
855
|
r ||= e[t];
|
|
856
|
-
let { operand: a, start: o } =
|
|
856
|
+
let { operand: a, start: o } = rt(e, t);
|
|
857
857
|
if (!a) break;
|
|
858
858
|
n.push(a), i = o;
|
|
859
859
|
}
|
|
@@ -861,14 +861,14 @@ var Xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^
|
|
|
861
861
|
operands: n,
|
|
862
862
|
next: r
|
|
863
863
|
};
|
|
864
|
-
},
|
|
864
|
+
}, nt = (e, t) => {
|
|
865
865
|
let n = [], r = "";
|
|
866
866
|
for (let i = t;;) {
|
|
867
867
|
let t = i;
|
|
868
868
|
for (; t < e.length && e[t] === " ";) t++;
|
|
869
869
|
if (e[t] !== "*" && e[t] !== "/") break;
|
|
870
870
|
r ||= e[t];
|
|
871
|
-
let { operand: a, end: o } =
|
|
871
|
+
let { operand: a, end: o } = it(e, t + 1);
|
|
872
872
|
if (!a) break;
|
|
873
873
|
n.push(a), i = o;
|
|
874
874
|
}
|
|
@@ -876,7 +876,7 @@ var Xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^
|
|
|
876
876
|
operands: n,
|
|
877
877
|
next: r
|
|
878
878
|
};
|
|
879
|
-
},
|
|
879
|
+
}, rt = (e, t) => {
|
|
880
880
|
let n = t - 1;
|
|
881
881
|
for (; n >= 0 && e[n] === " ";) n--;
|
|
882
882
|
let r = n + 1;
|
|
@@ -890,80 +890,80 @@ var Xe = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^
|
|
|
890
890
|
operand: e.slice(n + 1, r),
|
|
891
891
|
start: n + 1
|
|
892
892
|
};
|
|
893
|
-
},
|
|
893
|
+
}, it = (e, t) => {
|
|
894
894
|
let n = t;
|
|
895
895
|
for (; n < e.length && e[n] === " ";) n++;
|
|
896
896
|
let r = n;
|
|
897
897
|
for (; n < e.length && /[\w$.?]/.test(e[n]);) n++;
|
|
898
898
|
if (e[n] === "(") {
|
|
899
|
-
let t =
|
|
899
|
+
let t = z(e, n);
|
|
900
900
|
n = t < 0 ? e.length : t + 1;
|
|
901
901
|
}
|
|
902
902
|
return {
|
|
903
903
|
operand: e.slice(r, n),
|
|
904
904
|
end: n
|
|
905
905
|
};
|
|
906
|
-
},
|
|
906
|
+
}, at = (e) => {
|
|
907
907
|
let t = /^Math\s*\.\s*(?:min|max)\s*\(/.exec(e);
|
|
908
908
|
if (!t) return !1;
|
|
909
909
|
let n = t[0].length - 1;
|
|
910
|
-
return
|
|
911
|
-
},
|
|
912
|
-
function
|
|
913
|
-
let t =
|
|
910
|
+
return z(e, n) === e.length - 1 ? !e.slice(n + 1, -1).split(",").map((e) => e.trim()).some((e) => /^0(?:\.0*)?$/.test(e)) : !1;
|
|
911
|
+
}, ot = (e, t) => `${e}: ${t.join(", ")} — amount is ordinary money, the way it is written on the page: amount={25} takes 25 in the owner's own Stripe currency. Multiplied by 100 it charges a hundred times the price, and a total held under a ceiling or a floor charges something other than the basket shows. Pass the total itself (amount={total}), and change nothing else.`;
|
|
912
|
+
function st(e) {
|
|
913
|
+
let t = R(e), n = [];
|
|
914
914
|
for (let r of t.matchAll(/\bquery\s*\(/g)) {
|
|
915
|
-
let i = (r.index ?? 0) + r[0].length - 1, a =
|
|
915
|
+
let i = (r.index ?? 0) + r[0].length - 1, a = z(t, i);
|
|
916
916
|
if (a < 0) continue;
|
|
917
917
|
let o = e.slice(i + 1, a), s = [...o.matchAll(/\bwhere\s*\(\s*['"`]([\w.]+)['"`]/g)].map((e) => e[1]), c = [...o.matchAll(/\borderBy\s*\(\s*['"`]([\w.]+)['"`]/g)].map((e) => e[1]);
|
|
918
918
|
s.length && c.some((e) => s.some((t) => t !== e)) && n.push(`where('${s[0]}'…) with orderBy('${c.find((e) => e !== s[0]) || c[0]}'…)`);
|
|
919
919
|
}
|
|
920
920
|
return [...new Set(n)];
|
|
921
921
|
}
|
|
922
|
-
var
|
|
923
|
-
function
|
|
922
|
+
var ct = (e, t) => `${e}: ${t.join("; ")} in one query needs an index that is only made in the Firebase console — which the owner never opens — so Firestore refuses the read and the list never loads. Keep the where() and take the orderBy() out of the query; put what comes back in order in the page instead: const sorted = [...list].sort((a, b) => stamp(b) - stamp(a)). Change nothing else.`, lt = /^(?:pub|mem|admin|user|case|sys)_|^role_[a-z][a-z0-9]*_/, ut = new Set(["chat_threads", "users"]);
|
|
923
|
+
function dt(e) {
|
|
924
924
|
let t = /* @__PURE__ */ new Set();
|
|
925
|
-
for (let n of e.matchAll(/\b(?:collection|doc|collectionGroup)\s*\(\s*db\s*,\s*['"`]([A-Za-z][\w-]*)['"`]/g)) !
|
|
925
|
+
for (let n of e.matchAll(/\b(?:collection|doc|collectionGroup)\s*\(\s*db\s*,\s*['"`]([A-Za-z][\w-]*)['"`]/g)) !lt.test(n[1]) && !ut.has(n[1]) && t.add(n[1]);
|
|
926
926
|
return [...t];
|
|
927
927
|
}
|
|
928
|
-
var
|
|
928
|
+
var ft = (e, t) => `${e}: ${t.map((e) => `'${e}'`).join(", ")} ${t.length > 1 ? "have" : "has"} none of the name starts the pasted security rules know — pub_, mem_, admin_, user_, role_…_, case_ — so only an admin can read or write ${t.length > 1 ? "them" : "it"}: it works while the owner tests the app and is refused for every visitor and every member of the team. A page's own records are actions.records, written with actions.saveRecord / updateRecord / deleteRecord; a collection kept BESIDE them takes the start the page's sentence gave (the page's own records' prefix — mem_ for members, pub_ for anyone to read, admin_ for admins only). Rename ${t.length > 1 ? "each" : "it"} with that start everywhere in this file, and change nothing else.`, V = new Set(/* @__PURE__ */ "activeTable.addColumn.addRow.catCollection.categories.collection.confirmDelete.currentDate.currentUser.deleteCategory.deleteEvent.deleteRecord.deleteTable.deleteTask.error.events.goToNext.goToPrev.goToToday.loading.openAddForm.openAddModal.openEditModal.openInlineEditor.records.saveCategory.saveEvent.saveRecord.saveTable.saveTask.setAddForm.setCurrentDate.setFileHandler.setViewingEvent.startEdit.tables.tasks.updateCell.updateRecord.uploadFile.user.sendTo.takeBack.keys.canEdit.canDelete.publish.unpublish.isPublished.draftId.files.deleteFile.openUploadModal.viewFile".split(".")), pt = new Set([
|
|
929
929
|
"config",
|
|
930
930
|
"actions",
|
|
931
931
|
"children",
|
|
932
|
-
...
|
|
932
|
+
...V
|
|
933
933
|
]);
|
|
934
|
-
function
|
|
934
|
+
function mt(e, t) {
|
|
935
935
|
if (!/\bactions\b/.test(e) && !t) return [];
|
|
936
|
-
let n = /* @__PURE__ */ new Set(), r =
|
|
937
|
-
for (let e of r.matchAll(/\bactions\s*\??\.\s*([A-Za-z_$][\w$]*)(\s*(?:\|\||\?\?)\s*actions\s*\??\.\s*([A-Za-z_$][\w$]*))?/g)) a(e.index ?? 0) && (e[3] &&
|
|
936
|
+
let n = /* @__PURE__ */ new Set(), r = Se(R(e)), i = _t(r), a = (e) => !i.some(([t, n]) => e >= t && e < n);
|
|
937
|
+
for (let e of r.matchAll(/\bactions\s*\??\.\s*([A-Za-z_$][\w$]*)(\s*(?:\|\||\?\?)\s*actions\s*\??\.\s*([A-Za-z_$][\w$]*))?/g)) a(e.index ?? 0) && (e[3] && V.has(e[3]) || V.has(e[1]) || n.add(`actions.${e[1]}`));
|
|
938
938
|
for (let e of r.matchAll(/\b(?:const|let|var)\s*\{/g)) {
|
|
939
|
-
let t = (e.index ?? 0) + e[0].length - 1, i =
|
|
940
|
-
if (!(i < 0 || !a(t) || !/^\s*\}?\s*=\s*actions\b(?!\s*\??\.)/.test(r.slice(i, i + 30)))) for (let e of
|
|
939
|
+
let t = (e.index ?? 0) + e[0].length - 1, i = z(r, t);
|
|
940
|
+
if (!(i < 0 || !a(t) || !/^\s*\}?\s*=\s*actions\b(?!\s*\??\.)/.test(r.slice(i, i + 30)))) for (let e of gt(r.slice(t + 1, i))) V.has(e) || n.add(`actions.${e}`);
|
|
941
941
|
}
|
|
942
942
|
if (t) {
|
|
943
943
|
let e = /\bexport\s+default\s+function\s*[\w$]*\s*\(\s*\{/.exec(r) || /\bexport\s+default\s+\(\s*\{/.exec(r);
|
|
944
944
|
if (e) {
|
|
945
|
-
let t = (e.index ?? 0) + e[0].length - 1, i = r.slice(t + 1,
|
|
946
|
-
for (let e of
|
|
947
|
-
for (let e of
|
|
945
|
+
let t = (e.index ?? 0) + e[0].length - 1, i = r.slice(t + 1, z(r, t));
|
|
946
|
+
for (let e of gt(i)) pt.has(e) || n.add(`the prop ${e}`);
|
|
947
|
+
for (let e of ht(i)) {
|
|
948
948
|
let t = /^\s*actions\s*:\s*\{/.exec(e);
|
|
949
949
|
if (!t) continue;
|
|
950
|
-
let r =
|
|
951
|
-
for (let i of
|
|
950
|
+
let r = z(e, t[0].length - 1);
|
|
951
|
+
for (let i of gt(e.slice(t[0].length, r))) V.has(i) || n.add(`actions.${i}`);
|
|
952
952
|
}
|
|
953
953
|
}
|
|
954
954
|
}
|
|
955
955
|
return [...n];
|
|
956
956
|
}
|
|
957
|
-
var
|
|
957
|
+
var ht = (e) => {
|
|
958
958
|
let t = [], n = 0, r = 0;
|
|
959
959
|
for (let i = 0; i < e.length; i++) {
|
|
960
960
|
let a = e[i];
|
|
961
961
|
a === "(" || a === "[" || a === "{" ? n++ : a === ")" || a === "]" || a === "}" ? n-- : a === "," && n === 0 && (t.push(e.slice(r, i)), r = i + 1);
|
|
962
962
|
}
|
|
963
963
|
return t.push(e.slice(r)), t;
|
|
964
|
-
},
|
|
964
|
+
}, gt = (e) => ht(e).map((e) => e.split(/[:=]/)[0].trim()).filter((e) => /^[A-Za-z_$][\w$]*$/.test(e)), _t = (e) => {
|
|
965
965
|
let t = [];
|
|
966
|
-
for (let n of
|
|
966
|
+
for (let n of Re(e)) /(?<![\w$.])actions\b(?!\s*:)/.test(n.params) && [...e.matchAll(new RegExp(String.raw`<${n.name}\b`, "g"))].some((t) => {
|
|
967
967
|
let n = (t.index ?? 0) + t[0].length;
|
|
968
968
|
for (let t = 0; n < e.length; n++) if (e[n] === "{") t++;
|
|
969
969
|
else if (e[n] === "}") t--;
|
|
@@ -971,13 +971,13 @@ var mt = (e) => {
|
|
|
971
971
|
return /\bactions\s*=\s*\{\s*[{[]/.test(e.slice(t.index, n));
|
|
972
972
|
}) && t.push([n.start, n.start + n.body.length]);
|
|
973
973
|
return t;
|
|
974
|
-
},
|
|
974
|
+
}, vt = (e, t) => `${e}: ${t.join(", ")} ${t.length > 1 ? "are" : "is"} never handed to this page — called, it is "not a function" and the press does nothing; read, it is undefined and the list stays empty. A page's content is handed config and actions, nothing else, and actions carries records, loading, error, saveRecord(record), updateRecord(id, change), deleteRecord(id), confirmDelete(id), uploadFile(files), user and collection (and sendTo / takeBack on a page of sent entries). Use the one that does the job — actions.saveRecord(record) to add, actions.records to read — and change nothing else.`, H = /* @__PURE__ */ new Set();
|
|
975
975
|
{
|
|
976
976
|
let n = (e) => e.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("");
|
|
977
|
-
for (let t of e.split("\n")) t.trim() &&
|
|
977
|
+
for (let t of e.split("\n")) t.trim() && H.add(n(t.trim()));
|
|
978
978
|
for (let e of t.split("\n")) {
|
|
979
979
|
let t = e.trim().split(/\s+/)[0];
|
|
980
|
-
t &&
|
|
980
|
+
t && H.add(n(t));
|
|
981
981
|
}
|
|
982
982
|
}
|
|
983
983
|
var yt = new Set([
|
|
@@ -1015,7 +1015,7 @@ function Ct(e, t) {
|
|
|
1015
1015
|
if (/^\s*type\s/.test(r)) continue;
|
|
1016
1016
|
let e = r.trim().split(/\s+as\s+/)[0].trim(), i = e.replace(/^Lucide/, "").replace(/Icon$/, ""), a = xt.get(i.toLowerCase());
|
|
1017
1017
|
if (!a) continue;
|
|
1018
|
-
let o = (e) => t ? t.has(e) : !bt.has(e) ||
|
|
1018
|
+
let o = (e) => t ? t.has(e) : !bt.has(e) || H.has(e);
|
|
1019
1019
|
a !== i && o(a) ? n.push(`the icon ${e} (lucide-react spells it ${e.replace(i, a)})`) : (a !== i || !o(i)) && n.push(`the icon ${e} (the app's lucide-react has no such icon)`);
|
|
1020
1020
|
}
|
|
1021
1021
|
}
|
|
@@ -1314,6 +1314,7 @@ var dn = (e, t) => `${e}: ${t.join(", ")} ${t.length > 1 ? "are" : "is"} sent fr
|
|
|
1314
1314
|
"bare-textarea": "looks",
|
|
1315
1315
|
"own-swatch": "looks",
|
|
1316
1316
|
"own-picture-set": "looks",
|
|
1317
|
+
"swapped-upload": "breaks",
|
|
1317
1318
|
"full-reload": "breaks",
|
|
1318
1319
|
"typed-id": "breaks",
|
|
1319
1320
|
"own-stepper": "looks",
|
|
@@ -1706,24 +1707,45 @@ function _r(e) {
|
|
|
1706
1707
|
let n = e.match(gr);
|
|
1707
1708
|
return n && t.push(`${n[0]}…}]`), t;
|
|
1708
1709
|
}
|
|
1709
|
-
var vr = /\b(isMain|isPrimary|isCover|mainIndex|mainImageIndex|coverIndex|primaryIndex|mainImageId|mainPictureId)\b/, yr = (e) => /<UploadBox\b/.test(e) && !/<UploadBox\b[^>]*\bsortable\b/.test(e) ? e.match(vr)?.[1] ?? null : null, br =
|
|
1710
|
+
var vr = /\b(isMain|isPrimary|isCover|mainIndex|mainImageIndex|coverIndex|primaryIndex|mainImageId|mainPictureId)\b/, yr = (e) => /<UploadBox\b/.test(e) && !/<UploadBox\b[^>]*\bsortable\b/.test(e) ? e.match(vr)?.[1] ?? null : null, br = /variant|option|colou?r|shade|size|style|version/i, xr = /\.find\s*\(|\[\s*[A-Za-z_$][\w$]*\s*\]/;
|
|
1711
|
+
function Sr(e) {
|
|
1712
|
+
let t = [];
|
|
1713
|
+
for (let n of e.matchAll(/<UploadBox\b/g)) {
|
|
1714
|
+
let r = n.index ?? 0;
|
|
1715
|
+
if (Xr(e, r)) continue;
|
|
1716
|
+
let i = ur(Q(e, r), "value").trim(), a = /^\(?\s*([A-Za-z_$][\w$]*)/.exec(i)?.[1];
|
|
1717
|
+
if (!a) continue;
|
|
1718
|
+
let o = [i, new RegExp(String.raw`\b(?:const|let|var)\s+${a.replace(/\$/g, "\\$")}\s*(?::[^=]*)?=\s*([^;]*)`).exec(e)?.[1] || ""].find((e) => xr.test(e) && br.test(`${a} ${e}`));
|
|
1719
|
+
o && t.push({
|
|
1720
|
+
value: i.replace(/\s+/g, " "),
|
|
1721
|
+
list: /([A-Za-z_$][\w$.]*?)\??\.(?:find\s*\(|\[)|([A-Za-z_$][\w$.]*?)\??\s*\[/.exec(o)?.slice(1).find(Boolean) || "variants",
|
|
1722
|
+
chosen: /\.id\s*===\s*([A-Za-z_$][\w$.]*)|([A-Za-z_$][\w$.]*)\s*===\s*[A-Za-z_$][\w$]*\.id\b/.exec(o)?.slice(1).find(Boolean) || "chosenId",
|
|
1723
|
+
root: a
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
return t;
|
|
1727
|
+
}
|
|
1728
|
+
var Cr = (e, t) => {
|
|
1729
|
+
let { value: n, list: r, chosen: i, root: a } = t[0];
|
|
1730
|
+
return `${e}: <UploadBox value={${n.length > 48 ? `${n.slice(0, 48)}…` : n}}> is ONE upload box that follows whichever variant is chosen, so a photo still going up when another variant is picked is saved on THAT variant, and the photos going up move across with it. Draw a box for EVERY variant, each saving to its own, and show only the chosen one — {${r}.map((v) => (<div key={v.id} className={v.id === ${i} ? '' : 'hidden'}><UploadBox … /></div>))} — with the same props it has now and v in place of ${a}, so each box's photos land on its own variant. The variant buttons, the heading and everything else stay exactly where they are.`;
|
|
1731
|
+
}, wr = (e, t) => `${e}: ${t} keeps which picture is the MAIN one beside the list of pictures, and the two fall out of step — remove the main picture and it points at the wrong one, or at none. The set is the app's own upload box with one more word: <UploadBox multiple max={10} sortable value={pictures} onChange={…} label="Photos" />. Its tiles drag into order under a mouse and a finger, the FIRST picture is the main one and says so, and every other tile has "Make main". Keep ONE ordered list of links, read pictures[0] as the main picture everywhere, and delete ${t} and any move-left / move-right or star buttons of your own.`, Tr = "(?:qty|quantit(?:y|ies)|stock|inventory|onhand|instock|guests|seats|adults|children|kids|tickets|nights|rooms|units|portions|servings|copies|howmany)", Er = (e) => RegExp(`\\b[\\w.]*${Tr}[\\w.]*(?<!\\.length)(?:\\s*(?:\\?\\?|\\|\\|)\\s*\\d+)?\\s*\\)?\\s*\\${e}\\s*1\\b|\\bset\\w*${Tr}\\w*\\s*\\(\\s*\\(?\\s*\\w+\\s*\\)?\\s*=>[^;\\n]{0,48}?\\${e}\\s*1\\b`, "i"), Dr = Er("+"), Or = Er("-"), kr = (e) => {
|
|
1710
1732
|
if (/<NumberStepper\b/.test(e) || !/<button\b/i.test(e)) return null;
|
|
1711
|
-
let t = e.match(Cr);
|
|
1712
|
-
return t && wr.test(e) ? t[0].replace(/\s+/g, " ").trim() : null;
|
|
1713
|
-
}, Er = (e, t) => `${e}: "${t}" — a count taken up and down by a − and a + this page drew itself: small under a thumb, nowhere to type a bigger number, and every press a write of its own. It is the app's own piece, imported from 'your-ai-workflow-firebase-os': <NumberStepper label="Stock" name={variant.name} value={variant.stock} min={0} onChange={(stock) => …} onSettled={(stock, from) => …} />. onChange fires on every press and is for the SCREEN (set the page's own state); onSettled fires ONCE when they have stopped, with what it is now and what it was — put the save there, and the history line if the page keeps one, so five presses are one write. Give it max wherever there is a most (what is in stock). Delete the two buttons, the number between them and their handlers; change nothing else on the page.`, Dr = /\bfrom\s+['"](recharts|chart\.js|react-chartjs-2|victory|d3(?:-[\w-]+)?|@nivo\/[\w-]+|apexcharts|react-apexcharts|echarts|echarts-for-react|@visx\/[\w-]+|chartist|plotly\.js|react-plotly\.js)['"]/, Or = /\b(?:width|height)\s*:\s*`\$\{[^`]{1,120}\}%`/, kr = (e) => {
|
|
1714
1733
|
let t = e.match(Dr);
|
|
1734
|
+
return t && Or.test(e) ? t[0].replace(/\s+/g, " ").trim() : null;
|
|
1735
|
+
}, Ar = (e, t) => `${e}: "${t}" — a count taken up and down by a − and a + this page drew itself: small under a thumb, nowhere to type a bigger number, and every press a write of its own. It is the app's own piece, imported from 'your-ai-workflow-firebase-os': <NumberStepper label="Stock" name={variant.name} value={variant.stock} min={0} onChange={(stock) => …} onSettled={(stock, from) => …} />. onChange fires on every press and is for the SCREEN (set the page's own state); onSettled fires ONCE when they have stopped, with what it is now and what it was — put the save there, and the history line if the page keeps one, so five presses are one write. Give it max wherever there is a most (what is in stock). Delete the two buttons, the number between them and their handlers; change nothing else on the page.`, jr = /\bfrom\s+['"](recharts|chart\.js|react-chartjs-2|victory|d3(?:-[\w-]+)?|@nivo\/[\w-]+|apexcharts|react-apexcharts|echarts|echarts-for-react|@visx\/[\w-]+|chartist|plotly\.js|react-plotly\.js)['"]/, Mr = /\b(?:width|height)\s*:\s*`\$\{[^`]{1,120}\}%`/, Nr = (e) => {
|
|
1736
|
+
let t = e.match(jr);
|
|
1715
1737
|
if (t) return t[1];
|
|
1716
1738
|
if (/<Bars\b/.test(e) || !/\buseTallies\s*\(/.test(e)) return null;
|
|
1717
|
-
let n = e.match(
|
|
1739
|
+
let n = e.match(Mr);
|
|
1718
1740
|
return n ? n[0].replace(/\s+/g, " ") : null;
|
|
1719
|
-
},
|
|
1741
|
+
}, Pr = (e, t) => `${e}: "${t}" — a chart this page drew for itself: its colours are not the owner's theme, and a count is one running total, so there are no dates to draw it over. Numbers side by side are the app's own piece, imported from 'your-ai-workflow-firebase-os': <Bars items={[{ label: 'Views', value: counts.of(product.id).views }, { label: 'Added to bag', value: counts.of(product.id).added }]} size="sm" /> — one bar for each number, the number at its tip, format={money} for prices. Delete the chart, its import and whatever worked its lengths out; change nothing else on the page.`, Fr = /\b(?:window\s*\.\s*)?location\s*\.\s*(?:href\s*=(?!=)|assign\s*\(|replace\s*\()\s*['"`]\/(?!\/)[^'"`\s]*/, Ir = (e, t) => `${e}: "${t.replace(/\s+/g, " ")}…" goes to a page of this app by setting the browser's address, which throws the running app away and loads it again — whatever was open or half-typed is gone, and inside a preview the address is not there at all. Go there the app's way: const navigate = useNavigate() from 'react-router-dom' and navigate('/that-address') in the press, or <Link to="/that-address"> round the words. Keep the address, the words and everything else exactly as they are.`, Lr = /\b(?:UIDs?|user(?:'s)? ids?|document ids?|doc ids?|record ids?)\b/i, Rr = /\b(?:label|placeholder|title|aria-label|hint|helper)\s*=\s*(["'])([^"']{0,80})\1|>\s*([^<>{}=;()]{2,80}?)\s*[<{]/g, zr = (e) => {
|
|
1720
1742
|
let t = [];
|
|
1721
|
-
for (let n of e.matchAll(
|
|
1743
|
+
for (let n of e.matchAll(Rr)) {
|
|
1722
1744
|
let e = (n[2] ?? n[3] ?? "").trim();
|
|
1723
|
-
e &&
|
|
1745
|
+
e && Lr.test(e) && !t.includes(e) && t.push(e);
|
|
1724
1746
|
}
|
|
1725
1747
|
return t;
|
|
1726
|
-
},
|
|
1748
|
+
}, Br = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} puts an id in front of people — nobody knows one, nobody can type one, and it means nothing to whoever reads it. A person is CHOSEN by name: a <CustomSelect> of people from 'your-ai-workflow-firebase-os' (my team read from user_profiles on a page for the team, usePeople('<role>') on a page whose entries are sent to people), the pick saved as the uid AND the name on the record, and only the NAME ever drawn. A record is pointed at by pressing it, never by typing its id. Delete every box that asks for an id and every id shown on the page; change nothing else.`, Vr = (e, t) => `${e}: ${G(t)} — a colour that is DATA (a product's shade) picked or drawn by hand.${t.some((e) => e.startsWith("<input")) ? " The browser draws its colour well in its own chrome, a grey rectangle no theme class reaches, with nowhere to type a hex." : ""}${t.some((e) => !e.startsWith("<input")) ? " A class built from a value does not exist when Tailwind compiles the page, so every chip it draws is blank." : ""} Both are the app's own pieces, imported from 'your-ai-workflow-firebase-os': pick with <SwatchField label="Shades" multiple max={6} value={variant.shades} onChange={(shades) => …} /> — it hands back clean hex strings, a list with multiple and one string without — and show with <Swatch color={shade} name={variant.name} selected={…} onClick={…} />. Keep each shade as that one string on the record. This is not the hard-coded colour rule 8 forbids: that is about the DESIGN's colours, which stay the theme's.`, Hr = /\bfit\s*=\s*(?:["']contain["']|\{\s*["'`]contain["'`]\s*\})/, Ur = /(?:^|[\s"'`{:])(?:aspect-[\w[]|h-(?:\d|\[|full|screen|px\b)|inset-0\b)/, Wr = /(?:^|[\s"'`{:])(?:aspect-[\w[]|h-(?:\d|\[|full|screen))/, Gr = /(?:^|[\s"'`{:])w-auto\b/, Kr = /\bsrc\s*=\s*\{[^}]*\[\s*(?:\d+|[A-Za-z_$][\w$]*)\s*\]/, qr = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=[^;\n]*\[\s*(?:\d+|[A-Za-z_$][\w$]*)\s*\]/g, Jr = /\bsrc\s*=\s*\{\s*([A-Za-z_$][\w$]*)\s*\}/, Yr = /logo|brand|partner|sponsor|client|badge|avatar|rounded-full/i, Xr = (e, t) => {
|
|
1727
1749
|
let n = 0;
|
|
1728
1750
|
for (let r = t - 1; r >= 0; r--) {
|
|
1729
1751
|
let t = e[r];
|
|
@@ -1737,10 +1759,10 @@ var vr = /\b(isMain|isPrimary|isCover|mainIndex|mainImageIndex|coverIndex|primar
|
|
|
1737
1759
|
}
|
|
1738
1760
|
}
|
|
1739
1761
|
return !1;
|
|
1740
|
-
},
|
|
1762
|
+
}, Zr = (e) => {
|
|
1741
1763
|
let t = (t) => [...e.matchAll(new RegExp(String.raw`(?:^|[\s"'\x60{:])(?:${t}|size)-(\d+(?:\.5)?|\[[^\]\s]*\]|[a-z]+)(?![\w-])`, "g"))].map((e) => e[1]), n = (e) => e.length > 0 && e.every((e) => /^\d/.test(e) && Number(e) <= 20);
|
|
1742
1764
|
return n(t("w")) && n(t("h"));
|
|
1743
|
-
},
|
|
1765
|
+
}, Qr = (e, t) => {
|
|
1744
1766
|
let n = 0;
|
|
1745
1767
|
for (let r = t > 0 ? e.lastIndexOf("<", t - 1) : -1; r >= 0; r = r > 0 ? e.lastIndexOf("<", r - 1) : -1) {
|
|
1746
1768
|
if (e[r + 1] === "/") {
|
|
@@ -1758,19 +1780,19 @@ var vr = /\b(isMain|isPrimary|isCover|mainIndex|mainImageIndex|coverIndex|primar
|
|
|
1758
1780
|
}
|
|
1759
1781
|
}
|
|
1760
1782
|
return null;
|
|
1761
|
-
},
|
|
1762
|
-
function
|
|
1783
|
+
}, $r = /\b(?:w-full\s+h-full|h-full\s+w-full|inset-0|size-full)\b/, ei = /(?:^|[\s"'`{:])aspect-[\w[]/, ti = /(?:^|[\s"'`{:])h-(?:\d|\[|screen|px\b)/, ni = /["'`]https?:\/\/[^"'`\s]*(?:firebasestorage\.googleapis\.com|\.(?:png|jpe?g|webp|avif|gif|svg)(?=[?#"'`]))/i;
|
|
1784
|
+
function ri(e) {
|
|
1763
1785
|
let t = [], n = /* @__PURE__ */ new Set();
|
|
1764
|
-
for (let t of e.matchAll(
|
|
1765
|
-
let r = !
|
|
1786
|
+
for (let t of e.matchAll(qr)) n.add(t[1]);
|
|
1787
|
+
let r = !ni.test(e);
|
|
1766
1788
|
for (let i of e.matchAll(/<Picture\b/g)) {
|
|
1767
1789
|
let a = i.index ?? 0, o = Q(e, a);
|
|
1768
|
-
if (!/\bsrc\s*=\s*\{/.test(o) ||
|
|
1769
|
-
let s =
|
|
1770
|
-
if (s &&
|
|
1771
|
-
let c =
|
|
1790
|
+
if (!/\bsrc\s*=\s*\{/.test(o) || Yr.test(o) || Zr(o)) continue;
|
|
1791
|
+
let s = $r.test(o) ? Qr(e, a) : null;
|
|
1792
|
+
if (s && Zr(s)) continue;
|
|
1793
|
+
let c = Jr.exec(o)?.[1], l = Kr.test(o) || !!c && n.has(c) || Xr(e, a), u = Hr.test(o) && Ur.test(o), d = !Hr.test(o) && Wr.test(o) && !Gr.test(o) && l;
|
|
1772
1794
|
if (!u && !d) continue;
|
|
1773
|
-
let f = s &&
|
|
1795
|
+
let f = s && Ur.test(s) ? s : "", p = f && !ti.test(o) && !ei.test(o) ? f : o, m = r && ei.test(p) && !ti.test(p) && !ti.test(o);
|
|
1774
1796
|
t.push({
|
|
1775
1797
|
tag: o.replace(/\s+/g, " ").slice(0, 96).trim(),
|
|
1776
1798
|
box: f,
|
|
@@ -1779,13 +1801,13 @@ function $r(e) {
|
|
|
1779
1801
|
}
|
|
1780
1802
|
return t;
|
|
1781
1803
|
}
|
|
1782
|
-
var
|
|
1804
|
+
var ii = (e) => {
|
|
1783
1805
|
let t = /^<([A-Za-z][\w.]*)/.exec(e)?.[1] || "div", n = (/\bclassName\s*=\s*\{?\s*["'`]([^"'`]*)["'`]/.exec(e)?.[1] || "").replace(/\s+/g, " ").trim(), r = n.length > 60 ? `${n.slice(0, 60).replace(/\s+\S*$/, "")} …` : n;
|
|
1784
1806
|
return r ? `<${t} className="${r}">` : `<${t}>`;
|
|
1785
|
-
},
|
|
1786
|
-
let n = t.slice(0, 4).map((e) => `"${e.tag}…"${e.box ? ` in ${
|
|
1807
|
+
}, ai = (e, t) => {
|
|
1808
|
+
let n = t.slice(0, 4).map((e) => `"${e.tag}…"${e.box ? ` in ${ii(e.box)}` : ""}`), r = t.length > 4 ? ` and ${t.length - 4} more like them` : "", i = n.length > 1 ? `${n.slice(0, -1).join(", ")} and ${n[n.length - 1]}${r}` : `${n[0]}${r}`, a = t.some((e) => e.box);
|
|
1787
1809
|
return `${e}: ${i} ${t.length > 1 ? "frame each picture" : "frames each picture"} in a box of a shape you chose — the picture cropped to it, or sat small inside it with the box's tint showing round it — where in a gallery, a shop, a portfolio or any grid or row of pictures every frame is cut to its OWN picture's shape and the picture fills it exactly. Take the aspect-…/h-… off it${a ? " AND off the box named round it — a picture freed inside a box that keeps its shape sits small in it" : " (and out of any box shaped round it)"}, take off fit="contain" if it has one, and give it only its width and rounding — <Picture src={p.photo} alt={p.name} className="w-full rounded-2xl" /> — and it takes the picture's own shape, held open before the picture lands, with nothing cropped. A row of thumbnails is the one place a height goes, with the width left to the picture (className="h-20 w-auto shrink-0"). Lay pictures of different shapes out as columns that keep their own heights (columns-2 md:columns-3 gap-6, every tile break-inside-avoid mb-6), never rows of equal boxes.${K(t.length)}`;
|
|
1788
|
-
},
|
|
1810
|
+
}, oi = /\[[^\][]*?\.\.\.\s*([A-Za-z_$][\w$]*)(?![\w$.(])[^\][]*?\.\.\.\s*\1(?![\w$])[^\][]*\]/g, si = /\b([A-Za-z_$][\w$]*)\.concat\(\s*\1(?![\w$])[^)]*\)/g, ci = "(?:[A-Za-z_$][\\w$]*)?(?:[Pp]roduct|[Ii]tem|[Pp]hoto|[Pp]icture|[Ii]mage|[Gg]aller|[Cc]ard|[Ee]ntr|[Rr]ecord|[Ll]isting|[Ww]ork|[Pp]roject|[Pp]ost|[Ll]ook|[Pp]iece|[Cc]ollection)[\\w$]*", li = RegExp(`(?:\\bArray\\.from\\(\\s*\\{\\s*length\\s*:[^}]*\\}\\s*,|(?:\\[\\s*\\.\\.\\.\\s*Array\\([^)]*\\)\\s*\\]|\\bArray\\([^)]*\\)(?:\\.fill\\([^)]*\\))?)\\s*\\.map\\()\\s*\\(?[^)=]*\\)?\\s*=>\\s*[({\\s.]*(${ci})\\s*\\[[^\\]]*%\\s*\\1\\.length\\s*\\]`, "g"), ui = (e, t) => {
|
|
1789
1811
|
let n = 0;
|
|
1790
1812
|
for (let r = t; r < e.length; r++) {
|
|
1791
1813
|
let t = e[r];
|
|
@@ -1793,23 +1815,23 @@ var ei = (e) => {
|
|
|
1793
1815
|
else if ((t === "]" || t === "}") && --n === 0) return t === "]" ? r : -1;
|
|
1794
1816
|
}
|
|
1795
1817
|
return -1;
|
|
1796
|
-
},
|
|
1818
|
+
}, di = (e) => {
|
|
1797
1819
|
let t = [], n = 0, r = -1;
|
|
1798
1820
|
for (let i = 0; i < e.length; i++) {
|
|
1799
1821
|
let a = e[i];
|
|
1800
1822
|
a === "{" || a === "[" ? (n === 0 && a === "{" && (r = i), n++) : (a === "}" || a === "]") && (n--, n === 0 && a === "}" && r >= 0 && (t.push(e.slice(r, i + 1)), r = -1));
|
|
1801
1823
|
}
|
|
1802
1824
|
return t;
|
|
1803
|
-
},
|
|
1804
|
-
function
|
|
1825
|
+
}, fi = /https?:\/\/|\.(?:jpe?g|png|webp|avif|gif)\b/i, pi = /\b(?:id|_id|key|sku)\s*:\s*(?:'[^']*'|"[^"]*"|`[^`]*`|[\w$.-]+)\s*,?/g;
|
|
1826
|
+
function mi(e) {
|
|
1805
1827
|
let t = [];
|
|
1806
1828
|
for (let n of e.matchAll(/\[\s*\{/g)) {
|
|
1807
|
-
let r = n.index ?? 0, i =
|
|
1829
|
+
let r = n.index ?? 0, i = ui(e, r);
|
|
1808
1830
|
if (i < 0) continue;
|
|
1809
1831
|
let a = /* @__PURE__ */ new Set();
|
|
1810
|
-
for (let n of
|
|
1811
|
-
if (!
|
|
1812
|
-
let e = n.replace(
|
|
1832
|
+
for (let n of di(e.slice(r + 1, i))) {
|
|
1833
|
+
if (!fi.test(n)) continue;
|
|
1834
|
+
let e = n.replace(pi, "").replace(/\s+/g, "");
|
|
1813
1835
|
if (a.has(e)) {
|
|
1814
1836
|
let e = /\b(?:name|title)\s*:\s*(['"`])([^'"`]{1,40})\1/.exec(n);
|
|
1815
1837
|
t.push(e ? e[2] : n.replace(/\s+/g, " ").slice(0, 60));
|
|
@@ -1819,16 +1841,16 @@ function ui(e) {
|
|
|
1819
1841
|
}
|
|
1820
1842
|
return t;
|
|
1821
1843
|
}
|
|
1822
|
-
var
|
|
1823
|
-
function
|
|
1844
|
+
var hi = /^(?:logo|brand|partner|sponsor|press|quote|testimonial|word|letter|tag|client|customer|compan(?:y|ie)|review|award|mention|name|social|stat|by)s?$/i, gi = (e) => (e.match(/[A-Z]{2,}(?![a-z])|[A-Z]?[a-z0-9]+/g) || [e]).pop() || e;
|
|
1845
|
+
function _i(e) {
|
|
1824
1846
|
let t = [
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
].flatMap((t) => [...e.matchAll(t)].filter((e) => t ===
|
|
1829
|
-
return [...new Set([...t, ...
|
|
1847
|
+
oi,
|
|
1848
|
+
si,
|
|
1849
|
+
li
|
|
1850
|
+
].flatMap((t) => [...e.matchAll(t)].filter((e) => t === li || !hi.test(gi(e[1]))).map((e) => e[0].replace(/\s+/g, " ").trim())).map((e) => e.length > 64 ? `${e.slice(0, 64)}…` : e);
|
|
1851
|
+
return [...new Set([...t, ...mi(e)])];
|
|
1830
1852
|
}
|
|
1831
|
-
var
|
|
1853
|
+
var vi = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*(['"`])(https?:\/\/[^'"`\s]+)\2/g, yi = /\bsrc\s*=\s*(?:\{\s*)?(['"`])(https?:\/\/[^'"`\s]+)\1/, bi = /(?:^|[\s"'`{])(?:[a-z-]+:)?hidden(?=[\s"'`}]|$)/, xi = /^<(?:Popup|Lightbox|Modal|Dialog|Sheet|Drawer)\b/, Si = (e, t) => {
|
|
1832
1854
|
let n = 0;
|
|
1833
1855
|
for (let r = t > 0 ? e.lastIndexOf("<", t - 1) : -1; r >= 0; r = r > 0 ? e.lastIndexOf("<", r - 1) : -1) {
|
|
1834
1856
|
if (e[r + 1] === "/") {
|
|
@@ -1846,22 +1868,22 @@ var mi = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*(['"`
|
|
|
1846
1868
|
}
|
|
1847
1869
|
}
|
|
1848
1870
|
return -1;
|
|
1849
|
-
},
|
|
1871
|
+
}, Ci = (e, t) => {
|
|
1850
1872
|
let n = Q(e, t);
|
|
1851
|
-
if (/\bblur-/.test(n) ||
|
|
1852
|
-
for (let n =
|
|
1873
|
+
if (/\bblur-/.test(n) || bi.test(n)) return !0;
|
|
1874
|
+
for (let n = Si(e, t), r = 0; n >= 0 && r < 12; n = Si(e, n), r++) {
|
|
1853
1875
|
let t = Q(e, n);
|
|
1854
|
-
if (
|
|
1876
|
+
if (xi.test(t) || bi.test(t)) return !0;
|
|
1855
1877
|
}
|
|
1856
1878
|
return !1;
|
|
1857
1879
|
};
|
|
1858
|
-
function
|
|
1880
|
+
function wi(e) {
|
|
1859
1881
|
let t = /* @__PURE__ */ new Map();
|
|
1860
|
-
for (let n of e.matchAll(
|
|
1882
|
+
for (let n of e.matchAll(vi)) t.set(n[1], n[3]);
|
|
1861
1883
|
let n = /* @__PURE__ */ new Map();
|
|
1862
1884
|
for (let r of e.matchAll(/<Picture\b/g)) {
|
|
1863
|
-
let i = r.index ?? 0, a = Q(e, i), o =
|
|
1864
|
-
if (!s ||
|
|
1885
|
+
let i = r.index ?? 0, a = Q(e, i), o = Jr.exec(a)?.[1], s = yi.exec(a)?.[2] || (o ? t.get(o) : void 0);
|
|
1886
|
+
if (!s || Xr(e, i) || Ci(e, i)) continue;
|
|
1865
1887
|
let c = decodeURIComponent(s.split("?")[0].split("/").pop() || s).split("/").pop() || s, l = o ? `${o} (${c.replace(/^.*~/, "")})` : c.replace(/^.*~/, ""), u = n.get(s);
|
|
1866
1888
|
n.set(s, {
|
|
1867
1889
|
label: u?.label || l,
|
|
@@ -1870,16 +1892,16 @@ function bi(e) {
|
|
|
1870
1892
|
}
|
|
1871
1893
|
return [...n.values()].filter((e) => e.times > 1).map((e) => e.label);
|
|
1872
1894
|
}
|
|
1873
|
-
var
|
|
1874
|
-
function
|
|
1895
|
+
var Ti = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} drawn by more than one <Picture> — one of the owner's pictures used again to fill another spot. Each picture they gave you is drawn ONCE, in the spot their line for it names (rule 15). Keep that one, and where it was drawn again put a placeholder in its place: one plain frame in a soft tint of the theme (bg-foreground/5) at the shape that spot wants, with a simple inline <svg> line drawing in text-foreground/30 (rule 27) — never another of their pictures moved in to fill it. When they give a picture for that spot, it will take the placeholder's frame.`, Ei = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "show" : "shows"} the same thing again to fill out the page — every product, picture and entry appears ONCE (rule 15). Take the repeat out and let the layout fit however many there are: three products are three cards, in fewer columns or centred, never one of them shown twice. A customer who meets the same product twice sees a shop padded out, and wonders which one to buy.`, Di = /className\s*=\s*(?:\{\s*)?(?:`|"|')(?=[^`"']*\bfixed\b)(?=[^`"']*\binset-0\b)(?![^`"']*(?:pointer-events-none|(?:^|[\s:])-z-))[^`"']*(?:`|"|')/g, Oi = /(?:^|[\s"'`])(?:z-0|z-\[\s*(?:0|-\d+)\s*\])(?=[\s"'`]|$)/, ki = /(?:^|[\s"'`:])-?z-(?:\d|\[)/, Ai = /\bbg-(?:gradient-|\[(?:radial|linear|conic)-gradient|\[url\(|noise\b|grid\b|dots?\b)/;
|
|
1896
|
+
function ji(e) {
|
|
1875
1897
|
let t = 0;
|
|
1876
1898
|
for (let { attrs: n, end: r } of $(e, "div|section|aside|span")) {
|
|
1877
|
-
let i = /\bclassName\s*=/.test(n) ? n.match(
|
|
1878
|
-
i && (
|
|
1899
|
+
let i = /\bclassName\s*=/.test(n) ? n.match(Di) : null;
|
|
1900
|
+
i && (Oi.test(i[0]) || /\/\s*$/.test(n) && !/\bon[A-Z]\w*\s*=/.test(n) && !ki.test(i[0]) && Ai.test(i[0]) || /^\s*<(?:Loader|Skeleton)\b[^<]*?\/>\s*<\/(?:motion\.)?\w+>/.test(e.slice(r + 1)) || (t += 1));
|
|
1879
1901
|
}
|
|
1880
1902
|
return t;
|
|
1881
1903
|
}
|
|
1882
|
-
var
|
|
1904
|
+
var Mi = (e, t) => `${e}: ${t > 1 ? `${t} windows here are` : "a window here is"} drawn by hand — a \`fixed inset-0\` layer of the page's own. Nobody can click out of one, Escape does nothing, the page behind keeps scrolling, and anything else the page draws on top (a confirmation, a second window) lands underneath it. ${K(t)}Replace ${t > 1 ? "each" : "it"} with <Popup open={…} onClose={() => …} title="…"> … </Popup> imported from 'your-ai-workflow-firebase-os' — the same state that opens it now, what is inside it unchanged; side="right" for a basket that slides in from the edge, size="lg" for a product, footer={…} for its one main button; a picture opened large out of a set is <Lightbox pictures={photos} index={at} onIndex={setAt} onClose={() => setAt(null)} /> — and delete the layer, its backdrop, its own close button and the AnimatePresence round it: Popup closes on a click outside, on Escape and on its ✕, and eases in and out by itself (rule 30).`, Ni = /\b(?:set|show|hide|add|push|use)?[A-Za-z]*(?:[Tt]oast|[Ss]nackbar)[A-Za-z]*\b/g, Pi = (e) => e.replace(/'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"/g, "''").replace(/`(?:[^`\\$]|\\.|\$(?!\{))*`/g, "``").replace(/>([^<>{}]*)</g, "><"), Fi = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} a confirmation this page draws for itself — and one drawn by a page lands UNDER the window that was pressed, so nobody sees it: somebody presses Add to bag, sees nothing happen, presses again, and finds five in their basket. Delete it — the state, the timer and what it draws — and at the moment the thing is done call say('Added to your bag', { action: { label: 'View bag', onClick: () => … } }) imported from 'your-ai-workflow-firebase-os': the app's own brief line, above every window, gone by itself. Let the button that was pressed say it too for a moment ("Added"), and keep the number it changed somewhere that is always on screen (rule 30).`, Ii = /['"`]pub_([A-Za-z0-9_]+)['"`]/g, Li = /\b(?:setDoc|addDoc|updateDoc|deleteDoc|writeBatch|runTransaction)\s*\(/, Ri = (e, t) => {
|
|
1883
1905
|
let n = `['"\`]pub_${t}['"\`]`, r = String.raw`(?:setDoc|addDoc|updateDoc|deleteDoc|\.set|\.update|\.delete)\s*\(`;
|
|
1884
1906
|
if (RegExp(`${r}[^;]{0,200}?${n}`).test(e)) return !0;
|
|
1885
1907
|
let i = String.raw`(?:doc\s*\(\s*)?(?:doc|collection)\s*\(\s*db\s*,\s*${n}`;
|
|
@@ -1892,31 +1914,31 @@ var Oi = (e, t) => `${e}: ${t > 1 ? `${t} windows here are` : "a window here is"
|
|
|
1892
1914
|
}
|
|
1893
1915
|
return !1;
|
|
1894
1916
|
};
|
|
1895
|
-
function
|
|
1896
|
-
if (!
|
|
1917
|
+
function zi(e) {
|
|
1918
|
+
if (!Li.test(e)) return [];
|
|
1897
1919
|
let t = /* @__PURE__ */ new Set();
|
|
1898
|
-
for (let n of e.matchAll(
|
|
1899
|
-
return [...t].filter((t) => t !== "bookings" && t !== "files" && RegExp(`['"\`](?:mem|admin|role_[A-Za-z0-9]+)_${t}['"\`]`).test(e) &&
|
|
1920
|
+
for (let n of e.matchAll(Ii)) t.add(n[1]);
|
|
1921
|
+
return [...t].filter((t) => t !== "bookings" && t !== "files" && RegExp(`['"\`](?:mem|admin|role_[A-Za-z0-9]+)_${t}['"\`]`).test(e) && Ri(e, t));
|
|
1900
1922
|
}
|
|
1901
|
-
var
|
|
1902
|
-
let r =
|
|
1923
|
+
var Bi = (e, t) => RegExp(`['"\`]((?:mem|admin|role_[A-Za-z0-9]+)_${t})['"\`]`).exec(e)?.[1] || `mem_${t}`, Vi = (e, t, n) => {
|
|
1924
|
+
let r = Bi(t, n[0]);
|
|
1903
1925
|
return `${e}: this page writes the public copy in ${G(n.map((e) => `pub_${e}`))} itself, and a copy written by a page is how the page that reads it comes to show every thing TWICE: the page's own list holds things the server does not — a record drawn at once under a made-up id while its save is on its way, a list remembered in localStorage, a copy made with addDoc — and a loop that copies "whatever has no copy yet" puts each into pub_${n[0]} as a thing of its own, beside the real one that lands a moment later. The copy is the app's to keep. Delete every write to pub_${n[0]} from this file — on a save, on a delete, in the catch-up effect, where an order is applied — with the helper that strips fields for it, and say once at the top of the component: usePublicCopy('${r}') imported from 'your-ai-workflow-firebase-os', adding { leaveOut: ['…'] } for the fields that helper stripped other than who made the record. The app then keeps pub_${n[0]} matching ${r} while this page is open — each record whole, once, under its own id, only once the server has it — and clears out the repeats already there. Write ${r} exactly as now, never draw a new record under an id made up in the page, and change nothing else (rule 11).`;
|
|
1904
|
-
},
|
|
1926
|
+
}, Hi = /\bactions\s*\??\.\s*(?:records|loading|error|saveRecord|updateRecord|deleteRecord|collection|user)\b/, Ui = (e, t, n) => {
|
|
1905
1927
|
let r = n.match(/['"](pub_[a-z0-9_]+)['"]/)?.[1], i = r ? `'${r}'` : "'pub_<the other page's collection>' — the sentence that built this page names it";
|
|
1906
1928
|
return `${e}: ${t.replace(/\s+/g, "")} is read on a page anyone can open, and such a page is handed no actions at all — actions?.records is empty for ever here, so the page shows nothing to every visitor. What it shows is the pub_ copy the other page's app keeps, read by NAME: import { db } from 'your-ai-workflow-firebase-os' and onSnapshot(collection(db, ${i}), …) from 'firebase/firestore' at the top of the component, a loading state until the first answer lands, and those records handed to everything below that asked for actions.records (useStock(records) included). Delete every actions. read from this file (rule 11).`;
|
|
1907
|
-
},
|
|
1908
|
-
function
|
|
1929
|
+
}, Wi = (e) => /\buseStock\s*\(/.test(e) && /\b(?:saveRecord|updateRecord)\b/.test(e) && !/\busePublicCopy\s*\(/.test(e), Gi = (e) => `${e}: this page keeps the products a shop sells — useStock() says so — and keeps NO public copy of them: a shop anyone can open reads only a pub_ collection, and nothing writes one, so it shows no products at all. Say once at the top of the component usePublicCopy(actions.collection) imported from 'your-ai-workflow-firebase-os' (actions.collection is this page's own collection; add { leaveOut: ['…'] } for what is the owner's own business) — the app then keeps the pub_ copy while this page is open, whole, once, under each record's own id. Write nothing to pub_ yourself, and change nothing else (rule 11).`, Ki = /<(?:motion\.)?h1[\s>]/, qi = /\bfrom\s+['"`]\.\.?\/[^'"`]*['"`]/, Ji = /<(?:div|section|main|article|header)[\s>]/, Yi = (e) => Tn(e) && Ji.test(e) && !Ki.test(e) && !qi.test(e), Xi = (e) => `${e}: this is a page of the website and it has no <h1> — nothing on it says what page this is. A page anyone can open gets no heading from the app, so its content starts with ONE <h1>: the page's own name, or my line for it, sized to suit the page, never as big as it will go — a calm one (text-2xl md:text-4xl font-semibold tracking-tight text-foreground) on a page that is about its things, a display line only on a page that opens on a big picture or a band of colour — with at most one quiet line under it and clear room over it (pt-10 md:pt-16), never pressed up under the menu. SectionHeading is an <h2> — right for the sections below, never the page's title. Add the <h1> at the top and change nothing else (rule 30).`, Zi = RegExp("className\\s*=\\s*(?:\\{\\s*)?(?:`|\"|')([^`\"']*\\bgrid\\b[^`\"']*)(?:`|\"|')\\s*\\}?\\s*>\\s*\\{\\s*(" + ci + ")\\s*\\.(?:slice\\([^)]*\\)\\s*\\.)?map\\(", "g");
|
|
1930
|
+
function Qi(e) {
|
|
1909
1931
|
let t = [];
|
|
1910
|
-
for (let n of e.matchAll(
|
|
1932
|
+
for (let n of e.matchAll(Zi)) {
|
|
1911
1933
|
let r = n[1];
|
|
1912
1934
|
if (!(/(?:^|\s)grid-cols-1(?:\s|$)/.test(r) || !/(?:^|\s)grid-cols-\d/.test(r))) continue;
|
|
1913
|
-
let i = (n.index ?? 0) + n[0].length - 1, a =
|
|
1935
|
+
let i = (n.index ?? 0) + n[0].length - 1, a = Uc(e, i);
|
|
1914
1936
|
a < 0 || !/<Picture\b/.test(e.slice(i, a)) || t.push(`{${n[2]}.map(…)}`);
|
|
1915
1937
|
}
|
|
1916
1938
|
return [...new Set(t)];
|
|
1917
1939
|
}
|
|
1918
|
-
var
|
|
1919
|
-
function
|
|
1940
|
+
var $i = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} laid out ONE to a row on a phone, so every card is the whole width of the screen and a handful of things is a long scroll — and on a wide screen the cards are as oversized. A set of cards is two across on a phone and more as the screen widens: className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6" (pictures of mixed shapes: "columns-2 md:columns-3 xl:columns-4 gap-4", each tile break-inside-avoid mb-4). Change that one className and nothing else: every card keeps everything it holds, exactly as it looks now — its picture, its words, its swatches or choices, its buttons — and nothing new goes round the grid (rule 30).`, ea = /\bdraggable(?=[\s/>=])(?!\s*=\s*(?:\{\s*false\s*\}|["']false["']))|\bonDragStart\s*=(?!\s*\{\s*\(?\s*([A-Za-z_$][\w$]*)\s*\)?\s*=>\s*\{?\s*\1\s*\.\s*(?:stopPropagation|preventDefault)\s*\(\s*\)\s*;?\s*\}?\s*\})/, ta = /\bfrom\s+['"`](react-beautiful-dnd|@hello-pangea\/dnd|@dnd-kit\/[^'"`]+|react-dnd(?:-[^'"`]+)?|sortablejs|react-sortablejs|react-draggable)['"`]/g, na = (e, t) => `${e}: ${t.length ? `${G(t)} ${t.length > 1 ? "are" : "is"} not part of this app — the import resolves in a preview and fails when the app is published — and dragging here is already a piece of the app.` : "this page drags things with the browser's own drag and drop (draggable, onDragStart, onDrop) — nothing moves under a FINGER, a keyboard cannot reach it, and a picture inside the card starts a drag of its own and cancels the card's."} Both kinds of dragging are imported from 'your-ai-workflow-firebase-os'. Things moved BETWEEN stages — orders, leads, applications — are <Pipeline stages={[{ id: 'new', label: 'New' }, …]} items={orders} stageOf={(o) => o.stage} onMove={(o, stage) => updateDoc(doc(db, '…', o.id), { stage })} onOpen={(o) => setChosen(o)} card={(o) => <>…what one card says…</>} />. A set put IN ORDER — pictures, lessons, rows — is <Sortable items={pictures} onReorder={(next) => save(next)} className="grid grid-cols-3 gap-3" item={(p) => …} />. Delete every drag handler, the dragged / drag-over state and any "move left / move right" buttons added to make up for them: the pieces work under a mouse, a finger and the arrow keys, show where the thing will land, and put it back with a sentence when the save fails (rule 31).`;
|
|
1941
|
+
function ra(e) {
|
|
1920
1942
|
let t = 0;
|
|
1921
1943
|
for (let n of e.matchAll(/<table[\s>]/g)) {
|
|
1922
1944
|
let r = e.indexOf("</table>", n.index ?? 0);
|
|
@@ -1924,15 +1946,15 @@ function $i(e) {
|
|
|
1924
1946
|
}
|
|
1925
1947
|
return t;
|
|
1926
1948
|
}
|
|
1927
|
-
var
|
|
1928
|
-
function
|
|
1929
|
-
return /\buseSteps\s*\(|\.get\(\s*['"`][\w-]*step[\w-]*['"`]\s*\)/i.test(e) ? [] : /<(?:Input|input|TextArea|textarea|select|CustomSelect|UploadBox|AddressField|PhoneInputField|BuyerDetails|SwatchField|NumberStepper)\b/.test(e) ? W(e,
|
|
1949
|
+
var ia = (e, t) => `${e}: ${t > 1 ? `${t} lists of records here are each` : "a list of records here is"} drawn as a <table> — on a phone that is a strip of small print to drag sideways, with the button that matters off the edge, and its rows cannot be reached by a keyboard or sorted. Replace ${t > 1 ? "each" : "it"} with <DataTable rows={orders} loading={loading} columns={[{ key: 'number', label: 'Order', value: (o) => o.number }, { key: 'total', label: 'Total', value: (o) => o.total, cell: (o) => money(o.total), align: 'right' }]} onOpen={(o) => setChosen(o)} actions={(o) => …} search="Search orders" empty="No orders yet" /> imported from 'your-ai-workflow-firebase-os': a table whose headings sort on a wide screen, one readable block a record on a phone, soft rows while it loads. \`value\` is the plain value a column sorts and searches by, \`cell\` what it draws when that is more than the value, and \`phone: false\` leaves a column off a phone. Delete the <table>, its overflow-x-auto wrapper and any sorting or searching written for it (rule 31).`, aa = /\bconst\s*\[\s*((?:[A-Za-z_$][\w$]*)?[sS]tep(?:Index|Idx|Number|No|Id)?)\s*,\s*[A-Za-z_$][\w$]*\s*\]\s*=\s*useState\b/g;
|
|
1950
|
+
function oa(e) {
|
|
1951
|
+
return /\buseSteps\s*\(|\.get\(\s*['"`][\w-]*step[\w-]*['"`]\s*\)/i.test(e) ? [] : /<(?:Input|input|TextArea|textarea|select|CustomSelect|UploadBox|AddressField|PhoneInputField|BuyerDetails|SwatchField|NumberStepper)\b/.test(e) ? W(e, aa, 1) : /<(?:Checkout|BuyButton|PayButton|Basket)\b/.test(e) ? W(e, aa, 1).filter((t) => {
|
|
1930
1952
|
let n = new RegExp(String.raw`\bconst\s*\[\s*${t}\s*,\s*([A-Za-z_$][\w$]*)`).exec(e)?.[1];
|
|
1931
1953
|
return n ? new RegExp(String.raw`\b${n}\s*\(\s*(?:\(?\s*[A-Za-z_$][\w$]*\s*\)?\s*=>\s*)?[A-Za-z_$][\w$]*\s*[+-]\s*1\b`).test(e) ? !0 : new Set([...e.matchAll(new RegExp(String.raw`\b(?:${n}|useState(?:<[^>]*>)?)\s*\(\s*(['"\x60][^'"\x60]*['"\x60]|\d+)\s*\)`, "g"))].map((e) => e[1])).size >= 3 : !1;
|
|
1932
1954
|
}) : [];
|
|
1933
1955
|
}
|
|
1934
|
-
var
|
|
1935
|
-
function
|
|
1956
|
+
var sa = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "keep" : "keeps"} the step somebody is on in useState, so RELOADING the page starts them again at step one, the browser's Back button leaves the page instead of going back a step, and nobody can be sent a link to the step they are stuck on. Replace it with const flow = useSteps([{ id: 'details', label: 'Details' }, { id: 'pictures', label: 'Pictures' }, { id: 'review', label: 'Review' }]) imported from 'your-ai-workflow-firebase-os' — flow.step is the step on screen and it lives in the web address (?step=pictures, beside the ?product=… already there), flow.next() and flow.back() move and the browser's Back button follows, flow.reset() once the thing is sent — and draw the line across the top with <StepBar {...flow.bar} /> in place of numbered circles of your own. What was typed on each step is still useVisitorState (rules 16, 19 and 31).`, ca = /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*([A-Za-z_$][\w$]*(?:\??\.[A-Za-z_$][\w$]*)+)/g, la = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} put on the page with dangerouslySetInnerHTML — whatever anybody managed to type into that record then RUNS as code on every visitor's screen, and takes their session with it. Long writing somebody typed — a post, a description, a method — is <Article text={${t[0]}} /> imported from 'your-ai-workflow-firebase-os': a blank line is a paragraph, "# " a heading, "- " a list, **bold**, [words](link), and a picture on a line of its own as  — and it never reads HTML, so nothing typed can run. A short line is plain {text} (rule 31).`;
|
|
1957
|
+
function ua(e) {
|
|
1936
1958
|
let t = /* @__PURE__ */ new Set();
|
|
1937
1959
|
if (/\.toFixed\s*\(/.test(e)) for (let n of e.matchAll(/\b(?:function|const)\s+((?:format|to|as)(?:Currency|Price|Money|Amount|Usd|Eur|Gbp)|currencyFormat(?:ter)?|priceFormat(?:ter)?)\b/g)) t.add(`${n[1]}()`);
|
|
1938
1960
|
for (let n of e.split("\n")) {
|
|
@@ -1947,7 +1969,7 @@ function oa(e) {
|
|
|
1947
1969
|
}
|
|
1948
1970
|
return [...t];
|
|
1949
1971
|
}
|
|
1950
|
-
var
|
|
1972
|
+
var da = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "write" : "writes"} a price out by hand. .toFixed throws on a price that was saved as text or is not there yet — and the whole page goes blank; a "$" typed in front is wrong the day the <PayButton> beside it charges in euros; and 1250 reads "$1250.00" while the Transactions screen says "$1,250.00" for the same order. Every price, total and fee is money(amount) imported from 'your-ai-workflow-firebase-os' — money(25) is 25 in the owner's own currency ("£25.00" for an account in pounds), money(1250, 'eur') is "€1,250.00", and something that is not a number is a quiet dash — with the SAME currency word the page passes to its PayButton, and none where it passes none. Delete the formatter of your own and every typed currency sign.${K(t.length)} (rule 31)`;
|
|
1951
1973
|
function $(e, t) {
|
|
1952
1974
|
let n = [], r = RegExp(`<((?:motion\\.)?(?:${t}))(?=[\\s/>])`, "g");
|
|
1953
1975
|
for (let t of e.matchAll(r)) {
|
|
@@ -1975,33 +1997,33 @@ function $(e, t) {
|
|
|
1975
1997
|
}
|
|
1976
1998
|
return n;
|
|
1977
1999
|
}
|
|
1978
|
-
var
|
|
1979
|
-
function
|
|
2000
|
+
var fa = /onClick\s*=\s*\{\s*\(?\s*([A-Za-z_$][\w$]*)\s*\)?\s*=>\s*\{?\s*\1\s*\.\s*(?:stopPropagation|preventDefault)\s*\(\s*\)\s*;?\s*\}?\s*\}/;
|
|
2001
|
+
function pa(e) {
|
|
1980
2002
|
let t = /* @__PURE__ */ new Set();
|
|
1981
|
-
for (let { tag: n, attrs: r } of $(e, "div|span|li|tr|td|p|h[1-6]|section|article|figure|header|footer|img|label")) /\bonClick\s*=/.test(r) && (/\brole\s*=/.test(r) ||
|
|
2003
|
+
for (let { tag: n, attrs: r } of $(e, "div|span|li|tr|td|p|h[1-6]|section|article|figure|header|footer|img|label")) /\bonClick\s*=/.test(r) && (/\brole\s*=/.test(r) || fa.test(r) || n === "label" && /\bhtmlFor\s*=/.test(r) || t.add(`<${n} onClick>`));
|
|
1982
2004
|
return [...t];
|
|
1983
2005
|
}
|
|
1984
|
-
var
|
|
1985
|
-
function
|
|
2006
|
+
var ma = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} pressed with a mouse and by nothing else — a keyboard never reaches ${t.length > 1 ? "them" : "it"} and a screen reader does not know ${t.length > 1 ? "they" : "it"} can be pressed. Whatever is pressed is a <button type="button" className="text-left …"> (a <Link to="…"> where it goes to another page), with the same classes and the same onClick. Where a whole card is pressable AND has buttons of its own inside it — a button cannot sit in a button — keep the element and give it role="button" tabIndex={0} onKeyDown={(e) => { if (e.target === e.currentTarget && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); open(); } }}. A row of records that opens is <DataTable onOpen={…}>, and a card in a pipeline is <Pipeline onOpen={…}>, which do this themselves.${K(t.length)} (rule 30)`, ha = "(?![©®™])\\p{Extended_Pictographic}", ga = "✕✖✗✘✓✔←→↑↓↗⟳↻⟲▶◀▼▲▸◂▾▴‹›»«★☆⌄", _a = RegExp(`^(?:${ha}|[${ga}ו]|[\\uFE0F\\u200D\\s])+$`, "u"), va = RegExp(`${ha}|[${ga}×]`, "u"), ya = RegExp(`^([${ga}])\\uFE0F?\\s+\\S`, "u"), ba = RegExp(`\\S\\s+([${ga}])\\uFE0F?$`, "u");
|
|
2007
|
+
function xa(e) {
|
|
1986
2008
|
let t = /* @__PURE__ */ new Set(), n = (e) => t.add(e.replace(/\s+/g, " ").trim().slice(0, 28));
|
|
1987
2009
|
for (let t of e.matchAll(/>([^<>{}]+)</g)) {
|
|
1988
2010
|
let e = t[1].trim();
|
|
1989
|
-
if (!(!e || !
|
|
1990
|
-
if (
|
|
2011
|
+
if (!(!e || !va.test(e))) {
|
|
2012
|
+
if (_a.test(e)) {
|
|
1991
2013
|
e !== "•" && n(e);
|
|
1992
2014
|
continue;
|
|
1993
2015
|
}
|
|
1994
|
-
(
|
|
2016
|
+
(ya.test(e) || ba.test(e)) && n(e);
|
|
1995
2017
|
}
|
|
1996
2018
|
}
|
|
1997
2019
|
for (let t of e.matchAll(/(['"`])([^'"`\n]{1,8})\1/g)) {
|
|
1998
2020
|
let e = t[2].trim();
|
|
1999
|
-
e && e !== "×" && e !== "•" &&
|
|
2021
|
+
e && e !== "×" && e !== "•" && va.test(e) && _a.test(e) && n(e);
|
|
2000
2022
|
}
|
|
2001
2023
|
return [...t];
|
|
2002
2024
|
}
|
|
2003
|
-
var
|
|
2004
|
-
function
|
|
2025
|
+
var Sa = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "are characters" : "is a character"} standing in for an icon. A character is drawn by the visitor's own phone — in ITS colours and ITS style, never the theme's, a different drawing on every device — and it is the first thing that makes a page look home-made. An icon is a lucide-react icon: import { X, Check, ArrowRight, ArrowLeft, ShoppingBag, Camera, Package, Star } from 'lucide-react' and draw <X className="w-4 h-4" /> for ✕, <Check /> for ✓, <ArrowRight /> and <ArrowLeft /> for → and ←, and the icon OF the thing for an emoji (🛍️ is <ShoppingBag />, 📷 is <Camera />, 📦 is <Package />) — beside the same words, inside the same button. A turning ⟳ is <Loader /> from 'your-ai-workflow-firebase-os'. And where rule 27 would not have put an icon at all, take it out and leave the words.${K(t.length)} (rule 30)`, Ca = /(?:window\s*\.\s*)?(?:local|session)Storage\s*(?:\.\s*(?:get|set|remove)Item|\[)/g, wa = (e) => `${(e.split("/").pop() || "").replace(/\.[jt]sx?$/, "").replace(/Content$/, "").toLowerCase() || "page"}-draft`, Ta = (e, t) => `${e}: ${G(t)} — this page keeps what somebody is half-way through in the browser's storage itself. Written by hand it comes apart the same way every time: the whole form is saved on every change, so the EMPTY form is stored the moment the page opens and a draft is offered back to somebody who has typed nothing; Discard empties the form, the next save puts the empty one straight back, and the message returns for ever. It also reads storage while the page is being drawn, so the first paint disagrees with the second, and it throws outright in a private window. The app keeps it instead: const { value, setValue, clear, ready } = useVisitorState('${wa(e)}', {…the empty form…}), imported from 'your-ai-workflow-firebase-os'. Write it with setValue as they type — never from an effect that runs on load — draw the empty form until ready turns true, and clear() the moment the save lands AND the moment they discard it. Offer a draft back only while ready is true and what is in it is not the empty form: nothing is kept until something is really typed, and what was discarded stays discarded. Take every line that touches storage out of this file (rule 19).`, Ea = /\banimate-spin\b(?![-[])/, Da = (e) => `${e}: this page sets something spinning itself (animate-spin) — a ring of its own, a character, an icon — so its loading looks like no other screen of the app, and keeps turning for somebody who asked their device for less motion. Loading is the app's: <Loader /> for a ring in a line (size="sm" | "md" | "lg"), <Loader block label="Loading orders" /> in the space a list will take, <Skeleton className="h-24" /> — one per card, row or number still to come — wherever the shape is known, and <Button isLoading={saving}> for a button that is sending; all imported from 'your-ai-workflow-firebase-os'. Replace every animate-spin with the one that fits and delete the element it was on (rule 30).`, Oa = /(?<![\w-])text-\[(?:(?:[0-9]|1[01])(?:\.\d+)?px|0?\.(?:[0-6]\d*|7[0-4]\d*)r?em)\]/g, ka = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} smaller than small print — on a phone nobody reads it, and it fails every accessibility check the owner's clients may run. The smallest words on any page are text-xs (12px), and anything somebody has to READ — a name, a price, a note, a status — is text-sm or larger. Change every one, and where that makes a card too full, say less on the card and the rest in its <Popup> — never shrink the words to fit.${K(t.length)} (rule 30)`;
|
|
2026
|
+
function Aa(e, t) {
|
|
2005
2027
|
let n = /* @__PURE__ */ new Set();
|
|
2006
2028
|
for (let { end: r } of $(e, "Button|button")) {
|
|
2007
2029
|
if (e[r - 1] === "/") continue;
|
|
@@ -2010,8 +2032,8 @@ function Ea(e, t) {
|
|
|
2010
2032
|
}
|
|
2011
2033
|
return [...n];
|
|
2012
2034
|
}
|
|
2013
|
-
var
|
|
2014
|
-
function
|
|
2035
|
+
var ja = /^(?:submit|ok|okay|confirm|proceed|click here)$/i, Ma = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "are buttons that do" : "is a button that does"} not say what ${t.length > 1 ? "they do" : "it does"}. Somebody deciding whether to press it has to work out from the rest of the page what will happen — and "Submit" is the word that makes a page feel like a form to be got through. A button says the thing itself, in the person's own words: "Add product", "Send request", "Save changes", "Book this time", "Pay" with the amount. Change only the words — the button, its onClick and its place stay exactly as they are. A yes-or-no before a delete is <ConfirmModal confirmText="Delete" …>, which already names it (rule 20).${K(t.length)} (rule 32)`, Na = /^(?:apply|apply filters?|apply changes|update results|refresh|reload|refresh list)$/i, Pa = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "are presses" : "is a press"} nobody should have to make — added to EVERY use of the page. Filters and a search narrow the list AS somebody picks and types: keep what they chose in state and filter the list from it on every render, with no button to apply it. And a list is never refreshed by hand: read it live — onSnapshot on its collection, or the page's own hook — so it is up to date the moment anything changes, for everybody looking at it. Delete the button, and whatever state only existed to hold the choices until it was pressed.${K(t.length)} (rule 32)`;
|
|
2036
|
+
function Fa(e) {
|
|
2015
2037
|
let t = [];
|
|
2016
2038
|
for (let n of e.matchAll(/import\s*\{([^}]*)\}\s*from\s*['"]lucide-react['"]/g)) for (let e of n[1].split(",")) {
|
|
2017
2039
|
let n = e.trim().split(/\s+as\s+/).pop()?.trim();
|
|
@@ -2019,8 +2041,8 @@ function ja(e) {
|
|
|
2019
2041
|
}
|
|
2020
2042
|
return t;
|
|
2021
2043
|
}
|
|
2022
|
-
function
|
|
2023
|
-
let t =
|
|
2044
|
+
function Ia(e) {
|
|
2045
|
+
let t = Fa(e);
|
|
2024
2046
|
if (!t.length) return [];
|
|
2025
2047
|
let n = `<(${t.join("|")})\\b[^>]*\\/?>`, r = /* @__PURE__ */ new Set(), i = (e) => e.replace(/<[^>]*>/g, " ").replace(/\{[^}]*\}/g, " ").replace(/\s+/g, " ").trim().slice(0, 32);
|
|
2026
2048
|
for (let t of e.matchAll(/<(h[1-3]|SectionHeading)\b[^>]*>([\s\S]*?)<\/\1>/g)) {
|
|
@@ -2031,25 +2053,25 @@ function Ma(e) {
|
|
|
2031
2053
|
for (let t of e.matchAll(RegExp(`<(h[1-3]|SectionHeading)\\b[^>]*>([\\s\\S]*?)<\\/\\1>\\s*${n}`, "g"))) r.add(`<${t[3]}> beside "${i(t[2])}"`);
|
|
2032
2054
|
return [...r];
|
|
2033
2055
|
}
|
|
2034
|
-
var
|
|
2035
|
-
function
|
|
2036
|
-
let t =
|
|
2056
|
+
var La = (e, t) => `${e}: ${G(t)} — an icon nobody asked for, on a heading. A little picture beside a title is the first thing that makes a page look generated: it says nothing the words do not, and every page gets a different one. A heading is its words, set in the theme's type — take the icon out (and the flex wrapper that only existed to hold it) and leave the words exactly as they are. An icon stays only where the owner's own words asked for one there.${K(t.length)} (rule 27)`, Ra = /(?:^|>)\s*(?:No|Nothing|None|Not any)\b[^<{]{0,80}?\b(?:yet|so far|to show|found|here)\b|\b(?:is|are) empty\b|\bEmpty\b[^<{]{0,20}?(?:list|basket|bag|cart|inbox)/i, za = /\bw-(?:8|9|1\d|2\d|3\d)\b|\bsize=\{(?:3[2-9]|[4-9]\d|\d{3})\}/;
|
|
2057
|
+
function Ba(e) {
|
|
2058
|
+
let t = Fa(e);
|
|
2037
2059
|
if (!t.length) return [];
|
|
2038
2060
|
let n = /* @__PURE__ */ new Set();
|
|
2039
2061
|
for (let r of e.matchAll(RegExp(`<(${t.join("|")})\\b([^>]*)\\/?>`, "g"))) {
|
|
2040
|
-
if (!
|
|
2041
|
-
let t = e.slice(r.index + r[0].length, r.index + r[0].length + 500).match(
|
|
2062
|
+
if (!za.test(r[2])) continue;
|
|
2063
|
+
let t = e.slice(r.index + r[0].length, r.index + r[0].length + 500).match(Ra);
|
|
2042
2064
|
t && n.add(`<${r[1]}> over "${t[0].replace(/^>\s*/, "").trim().slice(0, 40)}"`);
|
|
2043
2065
|
}
|
|
2044
2066
|
return [...n];
|
|
2045
2067
|
}
|
|
2046
|
-
var
|
|
2068
|
+
var Va = (e, t) => `${e}: ${G(t)} — an empty list drawn as a poster: a big icon, a bold line, an explanation. Nobody asked for the picture, and a page with nothing in it yet should look calm, not announced. An empty state is ONE short, quiet line of words in the quieter text colour (text-foreground/60, text-sm), with room around it — no icon, no circle or dashed box round it, no second line — plus the button that adds the first thing where there is one. Take the icon and the wrapper out and keep the words.${K(t.length)} (rule 27)`, Ha = (e, t) => {
|
|
2047
2069
|
let n = 0;
|
|
2048
2070
|
for (let r = t; r < e.length; r++) if (e[r] === "(") n++;
|
|
2049
2071
|
else if (e[r] === ")" && --n === 0) return r;
|
|
2050
2072
|
return e.length;
|
|
2051
|
-
},
|
|
2052
|
-
function
|
|
2073
|
+
}, Ua = /\buseCases\s*[<(]/;
|
|
2074
|
+
function Wa(e) {
|
|
2053
2075
|
let t = /* @__PURE__ */ new Set();
|
|
2054
2076
|
for (let n of e.matchAll(/\{([^{}]*)\}\s*=\s*useCases\s*[<(]/g)) {
|
|
2055
2077
|
let e = n[1].match(/\brecords\b(?:\s*:\s*([A-Za-z_$][\w$]*))?/);
|
|
@@ -2061,99 +2083,99 @@ function Ba(e) {
|
|
|
2061
2083
|
}
|
|
2062
2084
|
return [...t];
|
|
2063
2085
|
}
|
|
2064
|
-
var
|
|
2065
|
-
function
|
|
2066
|
-
if (!
|
|
2086
|
+
var Ga = /<(?:CustomSelect|Input|TextArea|textarea|input|select)\b/g;
|
|
2087
|
+
function Ka(e) {
|
|
2088
|
+
if (!Ua.test(e)) return [];
|
|
2067
2089
|
let t = /* @__PURE__ */ new Set();
|
|
2068
|
-
for (let n of
|
|
2090
|
+
for (let n of Wa(e)) for (let r of e.matchAll(RegExp(`(?:^|[^\\w$.])(${n})\\s*(?:\\?\\.|\\.)\\s*map\\s*\\(`, "g"))) {
|
|
2069
2091
|
let i = (r.index ?? 0) + r[0].length - 1;
|
|
2070
|
-
(e.slice(i,
|
|
2092
|
+
(e.slice(i, Ha(e, i)).match(Ga) || []).length >= 2 && t.add(`${n}.map(…)`);
|
|
2071
2093
|
}
|
|
2072
2094
|
return [...t];
|
|
2073
2095
|
}
|
|
2074
|
-
var
|
|
2075
|
-
function
|
|
2076
|
-
if (!
|
|
2096
|
+
var qa = (e, t) => `${e}: every entry here is drawn OPEN, one under another, each with its own boxes and dropdowns (inside ${G(t)}) — a dozen orders is a long scroll, one at a time, and nobody can see at a glance what is waiting. Draw each one as a compact card or row saying only what tells it apart — its number or name, who it is for, its stage as a small pill, when, how much, who holds it — and move everything done to one into the one that is OPENED: keep its id, const [openId, setOpenId] = useState<string | null>(null), a press on a card sets it, and const open = records.find((r) => r.id === openId) is drawn whole in <Popup open={!!open} onClose={() => setOpenId(null)} title={…} side="right">, laid out as an even grid — each label above its value, one spacing throughout, fields and buttons side by side the same height. Where entries move through stages, the people who work on them see <Pipeline stages={…} items={records} stageOf={(o) => o.status} onMove={(o, stage) => …the save the page already has…} onOpen={(o) => setOpenId(o.id)} card={(o) => <>…</>} />, imported from 'your-ai-workflow-firebase-os'; a person who only follows their own sees theirs as the short list. Keep every save, sendTo, takeBack and notify exactly as it is — only where it is drawn moves (rules 6 and 31).`, Ja = /[\w$]*items\s*(?:(?:\|\||\?\?)\s*\[\s*\])?\s*\)?\s*\??$/i;
|
|
2097
|
+
function Ya(e) {
|
|
2098
|
+
if (!Ua.test(e)) return [];
|
|
2077
2099
|
let t = /* @__PURE__ */ new Set();
|
|
2078
2100
|
for (let n of e.matchAll(/\.map\s*\(\s*\(?\s*([A-Za-z_$][\w$]*)/g)) {
|
|
2079
2101
|
let r = n.index ?? 0, i = e.slice(Math.max(0, r - 60), r);
|
|
2080
|
-
if (!
|
|
2102
|
+
if (!Ja.test(i)) continue;
|
|
2081
2103
|
let [, a, o] = /(\.\s*)?([A-Za-z_$][\w$]*)\s*(?:(?:\|\||\?\?)\s*\[\s*\])?\s*\)?\s*\??$/.exec(i) || [];
|
|
2082
2104
|
if (o && !a && new RegExp(String.raw`\b(?:const|let|var)\s+${o.replace(/\$/g, "\\$")}\s*(?::[^=]*)?=\s*\[`).test(e)) continue;
|
|
2083
|
-
let s = e.indexOf("(", r), c = e.slice(s,
|
|
2105
|
+
let s = e.indexOf("(", r), c = e.slice(s, Ha(e, s)).match(RegExp(`\\bkey\\s*=\\s*\\{\\s*(${n[1]}\\s*(?:\\?\\.|\\.)\\s*(?:id|productId)\\b[^}]*)\\}`));
|
|
2084
2106
|
c && t.add(`key={${c[1].replace(/\s+/g, " ").trim()}}`);
|
|
2085
2107
|
}
|
|
2086
2108
|
return [...t];
|
|
2087
2109
|
}
|
|
2088
|
-
var
|
|
2089
|
-
function
|
|
2090
|
-
let n = [], r = W(t,
|
|
2091
|
-
(r.length ||
|
|
2092
|
-
let i =
|
|
2093
|
-
i && n.push(["mapped-table",
|
|
2094
|
-
let a =
|
|
2095
|
-
a.length && n.push(["step-state",
|
|
2096
|
-
let o = W(t,
|
|
2097
|
-
o.length && n.push(["own-draft",
|
|
2098
|
-
let s = W(t,
|
|
2099
|
-
s.length && n.push(["raw-html",
|
|
2100
|
-
let c =
|
|
2101
|
-
c.length && n.push(["own-money",
|
|
2102
|
-
let l =
|
|
2103
|
-
l.length && n.push(["dead-click",
|
|
2104
|
-
let u =
|
|
2105
|
-
u.length && n.push(["glyph-icon",
|
|
2106
|
-
let d = W(t,
|
|
2107
|
-
d.length && n.push(["tiny-text",
|
|
2108
|
-
let f =
|
|
2109
|
-
f.length && n.push(["vague-button",
|
|
2110
|
-
let p =
|
|
2111
|
-
p.length && n.push(["icon-in-heading",
|
|
2112
|
-
let m =
|
|
2113
|
-
m.length && n.push(["decorated-empty",
|
|
2114
|
-
let h =
|
|
2115
|
-
h.length && n.push(["extra-press",
|
|
2116
|
-
let g =
|
|
2117
|
-
g.length && n.push(["whole-records",
|
|
2118
|
-
let _ =
|
|
2119
|
-
_.length && n.push(["item-key",
|
|
2120
|
-
let v =
|
|
2121
|
-
return v && n.push(["order-currency",
|
|
2122
|
-
}
|
|
2123
|
-
var
|
|
2124
|
-
let t =
|
|
2125
|
-
...W(t,
|
|
2126
|
-
...W(t,
|
|
2127
|
-
...W(t,
|
|
2128
|
-
...W(t,
|
|
2129
|
-
...W(t,
|
|
2110
|
+
var Xa = (e, t) => `${e}: ${G(t)} — the lines of an order are keyed by their id, and an item's id is its PRODUCT's: the same thing bought in two colours or sizes is two lines with ONE key, so React draws one of them twice and drops the other, and the console fills with "Encountered two children with the same key". Key each line by its place in the list instead — items.map((item, i) => <div key={i}>…) — the lines of an order never move (rule 9).`, Za = /function TutorialPopup\(\{ onClose \}[\s\S]*?\n\}\n/;
|
|
2111
|
+
function Qa(e, t) {
|
|
2112
|
+
let n = [], r = W(t, ta, 1);
|
|
2113
|
+
(r.length || ea.test(t)) && n.push(["own-drag", na(e, r)]);
|
|
2114
|
+
let i = ra(t);
|
|
2115
|
+
i && n.push(["mapped-table", ia(e, i)]);
|
|
2116
|
+
let a = oa(t);
|
|
2117
|
+
a.length && n.push(["step-state", sa(e, a)]);
|
|
2118
|
+
let o = W(t, Ca);
|
|
2119
|
+
o.length && n.push(["own-draft", Ta(e, o)]);
|
|
2120
|
+
let s = W(t, ca, 1);
|
|
2121
|
+
s.length && n.push(["raw-html", la(e, s)]);
|
|
2122
|
+
let c = ua(t);
|
|
2123
|
+
c.length && n.push(["own-money", da(e, c)]);
|
|
2124
|
+
let l = pa(t);
|
|
2125
|
+
l.length && n.push(["dead-click", ma(e, l)]);
|
|
2126
|
+
let u = xa(t);
|
|
2127
|
+
u.length && n.push(["glyph-icon", Sa(e, u)]), Ea.test(t) && n.push(["own-spinner", Da(e)]);
|
|
2128
|
+
let d = W(t, Oa);
|
|
2129
|
+
d.length && n.push(["tiny-text", ka(e, d)]);
|
|
2130
|
+
let f = Aa(t, ja);
|
|
2131
|
+
f.length && n.push(["vague-button", Ma(e, f)]);
|
|
2132
|
+
let p = Ia(t);
|
|
2133
|
+
p.length && n.push(["icon-in-heading", La(e, p)]);
|
|
2134
|
+
let m = Ba(t);
|
|
2135
|
+
m.length && n.push(["decorated-empty", Va(e, m)]);
|
|
2136
|
+
let h = Aa(t, Na);
|
|
2137
|
+
h.length && n.push(["extra-press", Pa(e, h)]);
|
|
2138
|
+
let g = Ka(t);
|
|
2139
|
+
g.length && n.push(["whole-records", qa(e, g)]);
|
|
2140
|
+
let _ = Ya(t);
|
|
2141
|
+
_.length && n.push(["item-key", Xa(e, _)]);
|
|
2142
|
+
let v = Ys(t);
|
|
2143
|
+
return v && n.push(["order-currency", Xs(e, v)]), n;
|
|
2144
|
+
}
|
|
2145
|
+
var $a = (e) => `${e}: a bare <img> holds no space while it loads, pops in when it lands, always fetches the full-size file, and goes BLANK the moment its src changes — which is what makes a product's colour swatches flash. Swap every one for <Picture src={…} alt="…" className="…" /> imported from 'your-ai-workflow-firebase-os': same props, and it holds the box open, stands a small version in until the real one lands, and crosses over to a new picture instead of emptying. Add preload={[…other photos]} where a click changes the picture, and priority on the one at the very top of the page.`, eo = /(?:\b(?:const|let|var)\s+[A-Za-z_$]*(?:sample|mock|dummy|demo|fake)[A-Za-z_$]*\s*(?::[^=\n]*)?=\s*\[\s*[{'"`])|(?:\b(?:const|let|var)\s+[A-Za-z_$]*(?:seed|placeholder)[A-Za-z_$]*\s*(?::[^=\n]*)?=\s*\[\s*\{)|(?:\b(?:const|let|var)\s+[A-Za-z_$]*fallback[A-Za-z_$]*\s*(?::[^=\n]*)?=\s*\[\s*\{)|lorem ipsum|\b(?:John|Jane) Doe\b/i, to = (e) => e.replace(/placeholder\s*[=:]\s*(?:\{\s*)?['"`][^'"`]*['"`]/gi, ""), no = /https?:\/\/(?:images\.unsplash\.com|source\.unsplash\.com|picsum\.photos|placehold\.co|via\.placeholder\.com|placeholder\.com|dummyimage\.com|placekitten\.com|randomuser\.me|i\.pravatar\.cc|loremflickr\.com|images\.pexels\.com)[^'"`\s)]*/g, ro = /\b(?:const|let|var)\s+[A-Za-z_$]*(?:initial|default|example|starter|test)_?(?:products|orders|customers|reviews|testimonials|bookings|posts|listings|clients|members|entries|records|services|events|appointments|invoices|users)\b[A-Za-z_$]*\s*(?::[^=\n]*)?=\s*\[\s*\{/gi, io = String.raw`(?=[^}]*\b(?:name|title|price)\s*:\s*['"\x60\d])`, ao = new RegExp(String.raw`\b[\w$.?]*(?:records|products|orders|entries|listings|posts|reviews)\s*(?:\|\||\?\?)\s*\[\s*\{${io}|\b([\w$.?]+)\.length\s*(?:>\s*0\s*)?\?\s*\1\s*:\s*\[\s*\{${io}`, "g"), oo = /\buseState\s*(?:<[^>]*>\s*)?\(\s*\[\s*\{[^}]*\b(?:name|title)\s*:\s*['"`][^'"`]+['"`][^}]*\b(?:price|amount|total|cost)\s*:\s*\d/g, so = (e) => {
|
|
2146
|
+
let t = to(e), n = [
|
|
2147
|
+
...W(t, eo),
|
|
2148
|
+
...W(t, no),
|
|
2149
|
+
...W(t, ro),
|
|
2150
|
+
...W(t, ao),
|
|
2151
|
+
...W(t, oo)
|
|
2130
2152
|
];
|
|
2131
2153
|
return [...new Set(n.map((e) => e.slice(0, 48).trim()))];
|
|
2132
|
-
},
|
|
2154
|
+
}, co = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "are" : "is"} made-up data standing in for the real thing — this is the owner's live app, not a demo. Delete ${t.length > 1 ? "every one of them" : "it"}: a list reads the records it is for and shows a short empty line you designed until there are some, and every name, price and picture on the page comes from the owner, not from you.`, lo = new RegExp(String.raw`\b(?:bg-(?:white|black)\b|(?:bg|text|border|from|via|to|ring|fill|stroke|decoration|outline|shadow|accent|caret|divide)-(?:${"gray|slate|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose"})-\d+\b|(?:bg|text|border|from|via|to|ring|fill|stroke)-\[#[0-9a-fA-F]{3,8}\])`), uo = /-(?:red|orange|amber|yellow|lime|green|emerald|rose)-\d+\b/, fo = (e, t) => {
|
|
2133
2155
|
let n = t.length > 1, r = n ? " Replace every one of them, everywhere in the file." : "";
|
|
2134
|
-
return t.some((e) =>
|
|
2135
|
-
},
|
|
2136
|
-
for (let t of e.matchAll(
|
|
2156
|
+
return t.some((e) => uo.test(e)) ? `${e}: ${G(t)} ${n ? "are hard-coded colours, traffic-light ones among them, and they are" : "is a traffic-light colour, and it is"} nobody's theme — a green "in stock", a red "sold out", an amber "pending" all look cheap over the design the owner chose. Say the state in WORDS and tell it apart with the theme's own range: text-accent and bg-accent/10 for the one that matters, text-foreground for the ordinary one, text-foreground/50 for the quiet one, border-[var(--panel-border)] around a pill. The red of a real error or a delete is the app's own (ConfirmModal, a form's error line, troubleSentence) and is never written by hand on a page.${r}` : `${e}: ${G(t)} ${n ? "are hard-coded colours that ignore the owner's theme and stay" : "is a hard-coded colour that ignores the owner's theme and stays"} wrong in light or dark mode — use bg-background, text-foreground, text-accent, border-[var(--panel-border)], glass-panel or btn-primary.${r}`;
|
|
2157
|
+
}, po = (e, t) => `${e}: ${G(t.map((e) => `"${e}"`))} ${t.length > 1 ? "are" : "is"} wording off the owner's admin screens — the page is for its visitors, not for the owner or for you. Delete ${t.length > 1 ? "every one" : "it"} and any prompt or copy-this box on the page.`, mo = /(?:(?:copy|paste) (?:this|the sentence|it) (?:in)?to (?:your |the )?(?:ai\b|assistant|chat\b|gemini|ai studio)|copy for (?:your |the )?(?:ai|assistant)|ask your assistant|your assistant.{0,3}s chat|copy this to change)[^\n]{0,40}/i, ho = /(?:return|export\s+default\s*(?:\([^)]*\)|[A-Za-z_$][\w$]*)?\s*=>)\s*\(?\s*(?:<>\s*)?<[A-Za-z][\w.]*\s[^>]*?className=\{?\s*["'`]([^"'`]*)["'`]/g, go = (e) => {
|
|
2158
|
+
for (let t of e.matchAll(ho)) {
|
|
2137
2159
|
let e = t[1];
|
|
2138
2160
|
if (!/(?<![\w-])mx-auto(?![\w-])/.test(e)) continue;
|
|
2139
2161
|
let n = e.match(/(?<![\w-])max-w-(?:[2-6]xl|screen-(?:sm|md|lg|xl)|prose)(?![\w-])/);
|
|
2140
2162
|
if (n) return n[0];
|
|
2141
2163
|
}
|
|
2142
2164
|
return null;
|
|
2143
|
-
},
|
|
2144
|
-
for (let t of e.matchAll(
|
|
2165
|
+
}, _o = (e, t) => `${e}: the whole page is wrapped in ${t} with mx-auto, which squeezes it into a narrow strip down the middle of a screen it was handed the full width of — its left and right edges no longer line up with the menu above it. Take the cap off the page's outermost element and let it fill that width, with the content inside one container like the app's other screens (glass-panel border border-[var(--panel-border)] rounded-3xl); a readable measure inside a section is fine, a cap around the page is not.`, vo = /(?<![\w-])(?:h|max-h)-(?:screen|dvh|svh|lvh|\[(?:100|9\d|8\d)(?:vh|dvh|svh|lvh)\])/g, yo = /(?<![\w-])overflow-(?:y-)?(?:auto|scroll)(?![\w-])/, bo = /\[(?:9\d|8\d)(?:vh|dvh|svh|lvh)\]$/, xo = (e) => {
|
|
2166
|
+
for (let t of e.matchAll(vo)) {
|
|
2145
2167
|
let n = t.index ?? 0, r = Math.max(e.lastIndexOf("\"", n), e.lastIndexOf("'", n), e.lastIndexOf("`", n)), i = e.slice(n).search(/["'`]/), a = e.slice(r === -1 ? n : r + 1, i === -1 ? e.length : n + i);
|
|
2146
|
-
if (!(
|
|
2168
|
+
if (!(bo.test(t[0]) && yo.test(a))) return t[0];
|
|
2147
2169
|
}
|
|
2148
2170
|
return null;
|
|
2149
|
-
},
|
|
2171
|
+
}, So = /(?<![\w-])(?:px|pl|pr|p)-(?:\d|\[|px\b)/, Co = /(?<![\w-])(?:px|pl|pr|p)-0(?![\w.])/g, wo = /className=/g, To = 3, Eo = (e) => (e.match(wo) || []).length < To ? !1 : !So.test(e.replace(Co, "")), Do = (e) => `${e}: nothing on this page has any side padding, so its words sit hard against the edge of the window — on a phone they touch the glass. The menu bar above runs the full width with px-4 md:px-8, so give every section that same side padding and the page lines up with the logo: px-4 md:px-8 as the floor, px-6 md:px-12 where a section wants more air. A background, a picture or a band of colour may still run right to the edges — the words, buttons and boxes inside it keep the padding.`, Oo = /\bimport\s*\(\s*['"](\.{1,2}\/[^'"]+)['"]\s*\)(?:\s*\.then\s*\(([^\n]*))?/g, ko = /\b(?:import\s+(?!type\s)(?:([A-Za-z_$][\w$]*)\s*,?\s*)?(\{[^}]*\}|\*\s*as\s+[A-Za-z_$][\w$]*)?\s*from|export\s*(?:\{[^}]*\}|\*)\s*from)\s*['"](\.{1,2}\/[^'"]+)['"]/g, Ao = (e) => {
|
|
2150
2172
|
let t = X(e, "route");
|
|
2151
2173
|
return {
|
|
2152
2174
|
first: t[0],
|
|
2153
2175
|
count: t.length
|
|
2154
2176
|
};
|
|
2155
2177
|
};
|
|
2156
|
-
function
|
|
2178
|
+
function jo(e, t = [], n = {}) {
|
|
2157
2179
|
let r = [], i = [], a = [], u = typeof n.accountCurrency == "string" && /^[a-z]{3}$/i.test(n.accountCurrency.trim()) ? n.accountCurrency.trim().toLowerCase() : null, d = (e) => e.slice(0, Math.max(0, e.indexOf(": "))), h = (e, t) => {
|
|
2158
2180
|
r.push(t), a.push({
|
|
2159
2181
|
check: e,
|
|
@@ -2170,7 +2192,7 @@ function Do(e, t = [], n = {}) {
|
|
|
2170
2192
|
});
|
|
2171
2193
|
}, b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Set();
|
|
2172
2194
|
for (let { file: t } of e) if (J(t)) for (let e of g(q(t))) x.add(e);
|
|
2173
|
-
let ee =
|
|
2195
|
+
let ee = Xl(e), S = /* @__PURE__ */ new Map(), te = /* @__PURE__ */ new Set(), C = /* @__PURE__ */ new Set();
|
|
2174
2196
|
for (let { file: t, source: n } of e) {
|
|
2175
2197
|
if (!hn(t)) continue;
|
|
2176
2198
|
let e = q(t), r = v(e);
|
|
@@ -2189,20 +2211,20 @@ function Do(e, t = [], n = {}) {
|
|
|
2189
2211
|
source: Y(e.source)
|
|
2190
2212
|
}));
|
|
2191
2213
|
for (let e of [...C]) for (let t of w) t.keys.includes(e) && T.add(t.file);
|
|
2192
|
-
let
|
|
2214
|
+
let ne = /* @__PURE__ */ new Set(), D = /* @__PURE__ */ new Set(), re = /* @__PURE__ */ new Map();
|
|
2193
2215
|
for (let e of t) {
|
|
2194
2216
|
let t = It(e, w, c, T);
|
|
2195
2217
|
if (!t) continue;
|
|
2196
|
-
(Ft(e) ?
|
|
2218
|
+
(Ft(e) ? ne : D).add(t);
|
|
2197
2219
|
let n = e.config?.template;
|
|
2198
|
-
typeof n == "string" && kn.includes(n) &&
|
|
2220
|
+
typeof n == "string" && kn.includes(n) && re.set(t, n);
|
|
2199
2221
|
}
|
|
2200
|
-
let
|
|
2222
|
+
let O = /* @__PURE__ */ new Map();
|
|
2201
2223
|
for (let e of t) {
|
|
2202
2224
|
let t = It(e, w, c, T);
|
|
2203
|
-
t &&
|
|
2225
|
+
t && O.set(t, (O.get(t) ?? !1) || Pt(e.config));
|
|
2204
2226
|
}
|
|
2205
|
-
let
|
|
2227
|
+
let ie = /* @__PURE__ */ new Set(), ae = /* @__PURE__ */ new Set();
|
|
2206
2228
|
for (let e of t) {
|
|
2207
2229
|
let t = It(e, w, c, T);
|
|
2208
2230
|
if (!t) continue;
|
|
@@ -2211,39 +2233,39 @@ function Do(e, t = [], n = {}) {
|
|
|
2211
2233
|
"internal",
|
|
2212
2234
|
"admin",
|
|
2213
2235
|
"shared"
|
|
2214
|
-
].includes(n) || r.some((e) => e === "internal" || e === "admin")) &&
|
|
2236
|
+
].includes(n) || r.some((e) => e === "internal" || e === "admin")) && ie.add(t);
|
|
2215
2237
|
let i = e.config?.formEmails;
|
|
2216
|
-
i && (i.owner === !0 || i.confirm === !0) &&
|
|
2238
|
+
i && (i.owner === !0 || i.confirm === !0) && ae.add(t);
|
|
2217
2239
|
}
|
|
2218
|
-
let
|
|
2219
|
-
let r =
|
|
2240
|
+
let oe = (e, t, n) => {
|
|
2241
|
+
let r = Us(t);
|
|
2220
2242
|
if (!r.length) return;
|
|
2221
2243
|
let i = n ? [] : Yt(e, E).files.filter((t) => t !== e).map((e) => E.find((t) => t.file === e)?.source || "");
|
|
2222
2244
|
if (r = r.filter((e) => !i.some((t) => new RegExp(String.raw`<${e}[\s/>]`).test(t))), !r.length) return;
|
|
2223
|
-
let a = n ? null :
|
|
2224
|
-
a === !0 && !o.length ? y("unused-pay",
|
|
2225
|
-
},
|
|
2226
|
-
let { baskets: t, reads: n } =
|
|
2245
|
+
let a = n ? null : O.get(e) ?? null, o = a === !0 ? Vs.filter((e) => [t, ...i].some((t) => new RegExp(String.raw`<${e}[\s/>]`).test(t))) : [];
|
|
2246
|
+
a === !0 && !o.length ? y("unused-pay", ec(e, r, !0)) : y("unused-pay-import", ec(e, r, a, o));
|
|
2247
|
+
}, se = [...new Set(e.flatMap(({ source: e }) => {
|
|
2248
|
+
let { baskets: t, reads: n } = Fl(Y(e));
|
|
2227
2249
|
return t.length ? [] : n;
|
|
2228
|
-
}))],
|
|
2250
|
+
}))], ce = [...new Set(e.flatMap(({ source: e }) => {
|
|
2229
2251
|
let t = Y(e);
|
|
2230
|
-
return /\buseBasket\s*\(/.test(t) && /\.add\s*\(/.test(t) ?
|
|
2231
|
-
}))],
|
|
2252
|
+
return /\buseBasket\s*\(/.test(t) && /\.add\s*\(/.test(t) ? Fl(t).reads : [];
|
|
2253
|
+
}))], k = /* @__PURE__ */ new Set(), A = /* @__PURE__ */ new Set();
|
|
2232
2254
|
for (let { file: t, source: n } of e) {
|
|
2233
2255
|
let e = t.split("/").slice(0, -1).join("/"), r = (t) => `${Kt(`${e}/${t}`).replace(/\.(?:tsx|ts|jsx|js)$/, "")}.tsx`, i = Y(n);
|
|
2234
|
-
for (let e of i.matchAll(
|
|
2235
|
-
for (let e of i.matchAll(
|
|
2256
|
+
for (let e of i.matchAll(ko)) (e[1] ? A : k).add(r(e[3]));
|
|
2257
|
+
for (let e of i.matchAll(Oo)) {
|
|
2236
2258
|
let t = e.index ?? 0, n = i.lastIndexOf("\n", t - 1) + 1;
|
|
2237
|
-
|
|
2259
|
+
R(i.slice(n, t + 6)).slice(-6) === "import" && (e[2] ? (/\.default\b/.test(e[2]) ? A : k).add(r(e[1])) : /\blazy\s*\(\s*(?:async\s*)?\(\s*\)\s*=>\s*(?:\{\s*return\s+)?$/.test(i.slice(Math.max(0, t - 80), t)) && A.add(r(e[1])));
|
|
2238
2260
|
}
|
|
2239
2261
|
}
|
|
2240
|
-
let
|
|
2262
|
+
let le = new Set([...k].filter((e) => !A.has(e)));
|
|
2241
2263
|
for (let { file: t, source: r, helper: i } of e) {
|
|
2242
2264
|
let a = q(t), o = Y(r);
|
|
2243
2265
|
if (hn(t)) {
|
|
2244
2266
|
let e = v(a);
|
|
2245
2267
|
if (e === "shipped") {
|
|
2246
|
-
if (a === "theme.config.ts") for (let e of
|
|
2268
|
+
if (a === "theme.config.ts") for (let e of Is(r)) y("brand-gone", Ls(t, e));
|
|
2247
2269
|
continue;
|
|
2248
2270
|
}
|
|
2249
2271
|
if (e === "other") {
|
|
@@ -2251,7 +2273,7 @@ function Do(e, t = [], n = {}) {
|
|
|
2251
2273
|
continue;
|
|
2252
2274
|
}
|
|
2253
2275
|
if (!Cn(o) && !Tn(o) && h("config-export", `${t}: export one const named ${_(a)}Config.`), e === "tab" || e === "page") {
|
|
2254
|
-
let { first: n, count: r } =
|
|
2276
|
+
let { first: n, count: r } = Ao(o), i = n === void 0 ? null : Z(n);
|
|
2255
2277
|
if (r === 0 || i === null || !i.startsWith("/")) h("route", `${t}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);
|
|
2256
2278
|
else if (i.includes(":") || i.includes("*")) {
|
|
2257
2279
|
let e = i.slice(0, i.search(/[:*]/)).replace(/\/+$/, "") || "/page";
|
|
@@ -2299,8 +2321,8 @@ function Do(e, t = [], n = {}) {
|
|
|
2299
2321
|
continue;
|
|
2300
2322
|
}
|
|
2301
2323
|
if (J(t) || i) {
|
|
2302
|
-
!i && (/Content\.tsx$/.test(a) || !
|
|
2303
|
-
let r = g(a).map((e) => S.get(e)).find(Boolean) ||
|
|
2324
|
+
!i && (/Content\.tsx$/.test(a) || !le.has(t)) && !Tn(o) && h("default-export", `${t}: add export default.`);
|
|
2325
|
+
let r = g(a).map((e) => S.get(e)).find(Boolean) || re.get(t), s = /<form\b/.test(o) || /\bonSubmit\b/.test(o);
|
|
2304
2326
|
if (s && r) {
|
|
2305
2327
|
let e = tr[r];
|
|
2306
2328
|
o.includes(e.token) || y("preset-form", `${t}: this page is a ${r} and already has its own add and edit dialog, themed for light and dark — open it with ${e.call} instead of building a form here.`);
|
|
@@ -2309,268 +2331,272 @@ function Do(e, t = [], n = {}) {
|
|
|
2309
2331
|
c && h("bell-collection", `${t}: ${c[0]} is the bell's own collection and pages never touch it — delete that code and send the notification with notify({ toUid | toRole, title, body, link }) imported from 'your-ai-workflow-firebase-os'. If this page is about notices of its own, keep them somewhere else (user_page_notices).`);
|
|
2310
2332
|
let l = o.match(/['"`](?:chat_threads|chat_files|mem_chat_notes|mem_chat_state)['"`]/);
|
|
2311
2333
|
l && h("chat-collection", `${t}: ${l[0]} is not a collection any page may touch — delete that code. This app has no chat: where people should be able to write to the owner, use the built-in form, <ContactPopup formId="…" />, and what they send arrives in the Inbox. If this page keeps messages of its own, keep them somewhere else (mem_page_messages).`);
|
|
2312
|
-
let d = o.match(
|
|
2334
|
+
let d = o.match(xc);
|
|
2313
2335
|
d && h("chat-call", `${t}: ${d[0]} is not part of this app — the chat is gone, and nothing is exported under that name. Delete it, and where people should be able to write to the owner use the built-in form, <ContactPopup formId="…" />; what they send arrives in the Inbox.`), /['"`]user_phoneclaims['"`]/.test(o) && h("phone-claims", `${t}: user_phoneclaims is how the app keeps a two-step phone number to one account, and pages never touch it — keep this page's own records somewhere else (user_page_phones).`), /<style[\s>]/.test(o) && h("style-tag", `${t}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);
|
|
2314
|
-
let f =
|
|
2336
|
+
let f = xo(o);
|
|
2315
2337
|
f && y("screen-cap", `${t}: ${f} makes the page exactly one screen tall and strands everything below it on a phone — the page already scrolls, so let it be as long as it needs and drop the height and any overflow-hidden around it.`);
|
|
2316
|
-
let p =
|
|
2317
|
-
p && y("narrow-page",
|
|
2338
|
+
let p = go(o);
|
|
2339
|
+
p && y("narrow-page", _o(t, p));
|
|
2318
2340
|
let m = Vn.test(o), _ = Xn(o), v = _ ? null : Pn(o);
|
|
2319
2341
|
_ ? y("address-box", (m ? In : Fn)(t, _)) : v && y("own-address", (m ? In : Fn)(t, v));
|
|
2320
2342
|
let b = Zn(o);
|
|
2321
2343
|
b && y("own-phone", (m ? $n : Qn)(t, b));
|
|
2322
2344
|
let x = Hn(o);
|
|
2323
2345
|
x && y("buyer-twice", Un(t, x));
|
|
2324
|
-
let C = W(o,
|
|
2325
|
-
C.length && y("owner-words",
|
|
2326
|
-
let w =
|
|
2327
|
-
w.length && y("demo-data",
|
|
2328
|
-
let T = W(o,
|
|
2329
|
-
T.length && y("colour",
|
|
2330
|
-
let E =
|
|
2331
|
-
E.length && h("unknown-import",
|
|
2332
|
-
let
|
|
2333
|
-
|
|
2334
|
-
let
|
|
2335
|
-
|
|
2336
|
-
let
|
|
2337
|
-
|
|
2338
|
-
let
|
|
2339
|
-
|
|
2340
|
-
let
|
|
2341
|
-
|
|
2342
|
-
let
|
|
2343
|
-
|
|
2344
|
-
let
|
|
2345
|
-
|
|
2346
|
-
let
|
|
2347
|
-
if (
|
|
2348
|
-
let e =
|
|
2349
|
-
e.length && y("currency-mismatch",
|
|
2346
|
+
let C = W(o, mo);
|
|
2347
|
+
C.length && y("owner-words", po(t, C));
|
|
2348
|
+
let w = so(o);
|
|
2349
|
+
w.length && y("demo-data", co(t, w));
|
|
2350
|
+
let T = W(o, lo);
|
|
2351
|
+
T.length && y("colour", fo(t, T)), /<select[\s/>]/.test(o) && y("native-select", `${t}: a native <select> is drawn by the browser in its own chrome and no theme class can restyle it — replace it with <CustomSelect value={…} options={[{ value, label }]} onChange={(v) => …} /> imported from 'your-ai-workflow-firebase-os' (onChange receives the chosen value string, not an event).`);
|
|
2352
|
+
let E = oc(o);
|
|
2353
|
+
E.length && h("unknown-import", sc(t, E));
|
|
2354
|
+
let O = Ct(o, n.appIcons);
|
|
2355
|
+
O.length && h("bad-import", wt(t, O));
|
|
2356
|
+
let k = Ie(o);
|
|
2357
|
+
k.length && y("not-imported", Le(t, k));
|
|
2358
|
+
let A = Ve(o);
|
|
2359
|
+
A.length && y("hook-order", Ue(t, A));
|
|
2360
|
+
let j = Ge(o);
|
|
2361
|
+
j.length && y("inner-component", Ke(t, j));
|
|
2362
|
+
let M = qe(o);
|
|
2363
|
+
M.length && y("stamp-as-timestamp", Ye(t, M));
|
|
2364
|
+
let N = Xe(o);
|
|
2365
|
+
N.length && y("amount-by-hand", ot(t, N));
|
|
2366
|
+
let P = st(o);
|
|
2367
|
+
P.length && y("needs-index", ct(t, P));
|
|
2368
|
+
let F = dt(o);
|
|
2369
|
+
if (F.length && y("no-prefix", ft(t, F)), Tt.test(o) && y("own-firebase", Et(t, o)), oe(t, o, !!i), u) {
|
|
2370
|
+
let e = Zs(o, u);
|
|
2371
|
+
e.length && y("currency-mismatch", $s(t, e, u));
|
|
2350
2372
|
}
|
|
2351
|
-
let ue =
|
|
2352
|
-
ue.length && y("unknown-action",
|
|
2353
|
-
let de =
|
|
2354
|
-
de && y("notify-as-toast",
|
|
2355
|
-
let fe =
|
|
2356
|
-
fe && y("notify-body",
|
|
2357
|
-
let pe =
|
|
2358
|
-
pe !== null && y("popup-open",
|
|
2359
|
-
let me =
|
|
2360
|
-
me && y("hand-by-field",
|
|
2361
|
-
let he =
|
|
2362
|
-
he && y("own-hand-over",
|
|
2373
|
+
let ue = mt(o, /Content\.tsx$/.test(t));
|
|
2374
|
+
ue.length && y("unknown-action", vt(t, ue));
|
|
2375
|
+
let de = lc(o);
|
|
2376
|
+
de && y("notify-as-toast", bc(t, de));
|
|
2377
|
+
let fe = uc(o);
|
|
2378
|
+
fe && y("notify-body", dc(t, fe));
|
|
2379
|
+
let pe = fc(o);
|
|
2380
|
+
pe !== null && y("popup-open", pc(t, pe));
|
|
2381
|
+
let me = gc(o);
|
|
2382
|
+
me && y("hand-by-field", _c(t, me));
|
|
2383
|
+
let he = vc(o);
|
|
2384
|
+
he && y("own-hand-over", yc(t, he, /<HandOver\b/.test(o))), pr.test(o) && y("bare-textarea", mr(t));
|
|
2363
2385
|
let I = _r(o);
|
|
2364
|
-
I.length && y("own-swatch",
|
|
2386
|
+
I.length && y("own-swatch", Vr(t, I));
|
|
2365
2387
|
let ge = yr(o);
|
|
2366
|
-
ge && y("own-picture-set",
|
|
2367
|
-
let
|
|
2368
|
-
|
|
2369
|
-
let
|
|
2370
|
-
|
|
2371
|
-
let
|
|
2372
|
-
|
|
2373
|
-
let
|
|
2374
|
-
|
|
2375
|
-
let
|
|
2376
|
-
|
|
2377
|
-
let
|
|
2378
|
-
|
|
2379
|
-
let
|
|
2380
|
-
|
|
2381
|
-
let
|
|
2382
|
-
|
|
2383
|
-
let
|
|
2384
|
-
|
|
2385
|
-
let
|
|
2386
|
-
|
|
2387
|
-
let
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
let
|
|
2393
|
-
|
|
2394
|
-
let
|
|
2395
|
-
|
|
2396
|
-
let
|
|
2397
|
-
|
|
2398
|
-
let
|
|
2399
|
-
|
|
2400
|
-
let
|
|
2401
|
-
|
|
2402
|
-
let
|
|
2403
|
-
|
|
2404
|
-
let
|
|
2405
|
-
|
|
2406
|
-
let
|
|
2407
|
-
|
|
2408
|
-
let
|
|
2409
|
-
|
|
2410
|
-
let
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
let
|
|
2416
|
-
|
|
2417
|
-
let
|
|
2418
|
-
|
|
2419
|
-
let
|
|
2420
|
-
|
|
2421
|
-
let
|
|
2422
|
-
|
|
2423
|
-
let
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
let
|
|
2429
|
-
|
|
2430
|
-
let
|
|
2431
|
-
|
|
2432
|
-
let
|
|
2433
|
-
|
|
2434
|
-
let
|
|
2435
|
-
|
|
2388
|
+
ge && y("own-picture-set", wr(t, ge));
|
|
2389
|
+
let _e = Sr(o);
|
|
2390
|
+
_e.length && y("swapped-upload", Cr(t, _e));
|
|
2391
|
+
let ve = ns(o) ? null : kr(o);
|
|
2392
|
+
ve && y("own-stepper", Ar(t, ve));
|
|
2393
|
+
let ye = Nr(o);
|
|
2394
|
+
ye && y("own-chart", Pr(t, ye));
|
|
2395
|
+
let L = o.match(Fr);
|
|
2396
|
+
L && y("full-reload", Ir(t, L[0]));
|
|
2397
|
+
let R = zr(o);
|
|
2398
|
+
R.length && y("typed-id", Br(t, R));
|
|
2399
|
+
let Se = W(o, nr);
|
|
2400
|
+
Se.length && y("media-tag", rr(t, Se)), ir.test(o) && h("bare-img", $a(t));
|
|
2401
|
+
let z = ri(o), Ce = z.filter((e) => !e.held);
|
|
2402
|
+
Ce.length && y("boxed-picture", ai(t, Ce));
|
|
2403
|
+
let we = z.filter((e) => e.held);
|
|
2404
|
+
we.length && y("boxed-upload", ai(t, we));
|
|
2405
|
+
let Te = _i(o);
|
|
2406
|
+
Te.length && y("repeated-items", Ei(t, Te));
|
|
2407
|
+
let Ee = wi(o);
|
|
2408
|
+
Ee.length && y("picture-twice", Ti(t, Ee));
|
|
2409
|
+
let De = ji(o);
|
|
2410
|
+
De && y("own-popup", Mi(t, De));
|
|
2411
|
+
let Oe = [...new Set(W(Pi(o), Ni))];
|
|
2412
|
+
Oe.length && y("own-toast", Fi(t, Oe));
|
|
2413
|
+
for (let [e, n] of Qa(t, o)) y(e, n);
|
|
2414
|
+
let ke = tu(t, o, ee);
|
|
2415
|
+
ke.length && y("leads-nowhere", nu(t, ke)), ar.test(o) && h("background-picture", or(t));
|
|
2416
|
+
let Ae = dr(o);
|
|
2417
|
+
Ae.length && h("stuck-zero", fr(t, Ae));
|
|
2418
|
+
let je = W(o, jc);
|
|
2419
|
+
je.length && y("billing-code", Mc(t, je));
|
|
2420
|
+
let Me = W(mn(o, jc), cu);
|
|
2421
|
+
Me.length && y("payment-code", lu(t, Me));
|
|
2422
|
+
let Ne = W(o, uu);
|
|
2423
|
+
Ne.length && y("email-code", xu(t, Ne));
|
|
2424
|
+
let Pe = $t(o);
|
|
2425
|
+
(Pe.unknown.length || Pe.wrongWay.length) && y("email-template", en(t, Pe.unknown, Pe.wrongWay));
|
|
2426
|
+
let Fe = un(o);
|
|
2427
|
+
Fe.length && y("email-in-effect", dn(t, Fe));
|
|
2428
|
+
let Re = ae.has(t) ? an(o) : [];
|
|
2429
|
+
Re.length && y("email-twice", on(t, Re));
|
|
2430
|
+
let ze = W(o, du);
|
|
2431
|
+
ze.length && h("storage-code", fu(t, ze)), yu.test(o) && y("file-input", bu(t));
|
|
2432
|
+
let Be = mu(o);
|
|
2433
|
+
Be.length && h("browser-dialog", vu(t, Be));
|
|
2434
|
+
let He = xe(o);
|
|
2435
|
+
He && h("ai-code", be(t, He)), Ho(o) && y("case-query", Rs(t));
|
|
2436
|
+
for (let e of ks(o)) y("hook-answer", As(t, e));
|
|
2437
|
+
let We = ds(o);
|
|
2438
|
+
We.length && y("not-a-case", fs(t, We));
|
|
2439
|
+
let Je = hs(o);
|
|
2440
|
+
Je.length && y("save-args", gs(t, Je));
|
|
2441
|
+
let Ze = Ms(o);
|
|
2442
|
+
Ze && y("own-publish", Ns(t, Ze));
|
|
2443
|
+
let Qe = W(o, Nc);
|
|
2444
|
+
Qe.length && y(Qe.some((e) => !e.startsWith("runTransaction")) ? "booking-code" : "own-transaction", Pc(t, Qe));
|
|
2445
|
+
let $e = Ic(o);
|
|
2446
|
+
$e.length && y("stale-times", Lc(t, $e));
|
|
2447
|
+
let B = g(a).some((e) => te.has(e)) || ne.has(t) || !D.has(t) && Tc(o);
|
|
2448
|
+
if (tn.test(o) && (B || D.has(t) && !ie.has(t)) && y("email-anyone", nn(t, B)), B) {
|
|
2449
|
+
Eo(o) && y("no-gutter", Do(t)), Yi(o) && y("no-page-title", Xi(t));
|
|
2450
|
+
let e = Qi(o);
|
|
2451
|
+
e.length && y("one-per-row", $i(t, e));
|
|
2452
|
+
let n = g(a).some((e) => S.has(e)) || re.has(t) ? null : o.match(Hi);
|
|
2453
|
+
n && y("no-actions", Ui(t, n[0], o));
|
|
2454
|
+
let r = mn(o, Nc), i = W(r, Ro);
|
|
2455
|
+
i.length && y("private-collection", zo(t, i));
|
|
2456
|
+
let s = Oc.test(r);
|
|
2457
|
+
s && y("own-tally", Ac(t));
|
|
2458
|
+
let c = kc(W(r, Ec, 1), s);
|
|
2459
|
+
c.length && y("public-write", Dc(t, c));
|
|
2436
2460
|
}
|
|
2437
|
-
let
|
|
2438
|
-
if (!
|
|
2439
|
-
let e =
|
|
2440
|
-
e.length && y("own-write",
|
|
2461
|
+
let et = Io(o);
|
|
2462
|
+
if (!B && Fo(o) && et.size === 1 && !et.has("case") && y("role-curtain", Lo(t)), !B && !r && /\bactions\b/.test(o)) {
|
|
2463
|
+
let e = Ss(o);
|
|
2464
|
+
e.length && y("own-write", Cs(t, e));
|
|
2441
2465
|
}
|
|
2442
|
-
let
|
|
2443
|
-
|
|
2444
|
-
let
|
|
2445
|
-
|
|
2446
|
-
let
|
|
2447
|
-
|
|
2448
|
-
let
|
|
2449
|
-
if (
|
|
2450
|
-
let
|
|
2451
|
-
|
|
2452
|
-
for (let n of
|
|
2453
|
-
let
|
|
2454
|
-
|
|
2455
|
-
let
|
|
2456
|
-
|
|
2457
|
-
let
|
|
2458
|
-
|
|
2459
|
-
let
|
|
2460
|
-
|
|
2461
|
-
let
|
|
2462
|
-
|
|
2463
|
-
let
|
|
2464
|
-
|
|
2465
|
-
let
|
|
2466
|
-
|
|
2467
|
-
let
|
|
2468
|
-
|
|
2469
|
-
let
|
|
2470
|
-
|
|
2471
|
-
let
|
|
2472
|
-
|
|
2466
|
+
let tt = B ? null : il(o);
|
|
2467
|
+
tt && y("whole-inbox", su(t, tt));
|
|
2468
|
+
let nt = Go(o);
|
|
2469
|
+
nt && y("public-copy", Ko(t, nt.token));
|
|
2470
|
+
let rt = zi(o);
|
|
2471
|
+
rt.length && y("own-copy", Vi(t, o, rt)), !B && Wi(o) && y("no-copy", Gi(t));
|
|
2472
|
+
let it = ns(o), at = Xo(o);
|
|
2473
|
+
if (it ? y("own-basket", ts.test(o) ? rs(t, it, /<Checkout\b/.test(o), Zo(o)) : is(t, it)) : at ? y("own-checkout", $o(t, at, Zo(o))) : as(o) && y("own-purchase", os(t)), !it && !at) for (let n of Wl(o, e)) n.why === "flag" ? y("order-flag", au(n.config, t)) : n.why === "asks" ? y("order-asks", Gl(n.config, t)) : y("buyer-form", ou(n.config, t));
|
|
2474
|
+
let lt = cs(o);
|
|
2475
|
+
lt && y("own-return", ls(t, lt));
|
|
2476
|
+
for (let n of Yl(t, o, e)) n.why === "typed" ? y("no-road-in", ru(n.target, t, n.formId)) : y("own-road", iu(n, t));
|
|
2477
|
+
let ut = Wc(o);
|
|
2478
|
+
ut.length && y("swallowed-write", Zc(t, ut));
|
|
2479
|
+
let V = Yc(o);
|
|
2480
|
+
V.length && y("swallowed-block", Xc(t, V));
|
|
2481
|
+
let pt = tl(o);
|
|
2482
|
+
pt.length && y("raw-fault", nl(t, pt));
|
|
2483
|
+
let ht = cl(o);
|
|
2484
|
+
ht.length && y("made-up-history", _l(t, ht));
|
|
2485
|
+
let gt = !B && bl(o);
|
|
2486
|
+
gt && y("own-stock", Hl(t)), !gt && Cl(o) && y("own-left", Vl(t));
|
|
2487
|
+
let _t = Ll(o, ce);
|
|
2488
|
+
_t && y("basket-collection", zl(t, _t.reads, _t.elsewhere));
|
|
2489
|
+
let H = Rl(o, se);
|
|
2490
|
+
H && y("basket-collection-name", Bl(t, H.said, H.reads));
|
|
2491
|
+
let yt = kl(o);
|
|
2492
|
+
yt.length && y("joined-variants", Al(t, yt)), jl(o) && y("zoom-is-a-gallery", Ml(t));
|
|
2493
|
+
let bt = B || gt ? null : dl(o);
|
|
2494
|
+
bt && y("signed-by-admin", Ul(t, bt.collection, bt.signs));
|
|
2495
|
+
let xt = hl(o).filter((e) => !bt || e.startsWith("a profile read"));
|
|
2496
|
+
xt.length && y("own-byline", gl(t, xt));
|
|
2473
2497
|
continue;
|
|
2474
2498
|
}
|
|
2475
2499
|
if (t === c || t === l) {
|
|
2476
|
-
/<style[\s>]/.test(o) && h("style-tag", t === l ? `${t}: a <style> tag is global CSS — the menu is on every screen, so it restyles every page of the app all the time — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).` : `${t}: a <style> tag is global CSS — while the home page is open it restyles every page of the app, the titles and the menu included — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`), t === l && !En(o, "Menu") && h("menu-export", `${t}: App.tsx imports { Menu } from this file — keep the named export \`export function Menu(props)\`; with a default export or another name the owner's menu is never found and visitors get the app's standard menu instead.`), t === c && !En(o, "Home") && h("home-export",
|
|
2477
|
-
let e =
|
|
2500
|
+
/<style[\s>]/.test(o) && h("style-tag", t === l ? `${t}: a <style> tag is global CSS — the menu is on every screen, so it restyles every page of the app all the time — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).` : `${t}: a <style> tag is global CSS — while the home page is open it restyles every page of the app, the titles and the menu included — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`), t === l && !En(o, "Menu") && h("menu-export", `${t}: App.tsx imports { Menu } from this file — keep the named export \`export function Menu(props)\`; with a default export or another name the owner's menu is never found and visitors get the app's standard menu instead.`), t === c && !En(o, "Home") && h("home-export", Ws(t, o));
|
|
2501
|
+
let e = xo(o);
|
|
2478
2502
|
e && y("screen-cap", `${t}: ${e} makes the page exactly one screen tall and strands everything below it on a phone — let the page be as long as it needs and drop the height and any overflow-hidden around it.`);
|
|
2479
|
-
let r = t === c ?
|
|
2480
|
-
r && y("narrow-page",
|
|
2481
|
-
let i =
|
|
2482
|
-
i.length && y("demo-data",
|
|
2483
|
-
let a = W(o,
|
|
2484
|
-
a.length && y("colour",
|
|
2485
|
-
let s =
|
|
2486
|
-
s.length && h("unknown-import",
|
|
2503
|
+
let r = t === c ? go(o) : null;
|
|
2504
|
+
r && y("narrow-page", _o(t, r));
|
|
2505
|
+
let i = so(o);
|
|
2506
|
+
i.length && y("demo-data", co(t, i));
|
|
2507
|
+
let a = W(o, lo);
|
|
2508
|
+
a.length && y("colour", fo(t, a)), /<select[\s/>]/.test(o) && y("native-select", `${t}: a native <select> is drawn by the browser in its own chrome and no theme class can restyle it — replace it with <CustomSelect value={…} options={[{ value, label }]} onChange={(v) => …} /> imported from 'your-ai-workflow-firebase-os' (onChange receives the chosen value string, not an event).`);
|
|
2509
|
+
let s = oc(o);
|
|
2510
|
+
s.length && h("unknown-import", sc(t, s));
|
|
2487
2511
|
let d = Ct(o, n.appIcons);
|
|
2488
2512
|
d.length && h("bad-import", wt(t, d));
|
|
2489
|
-
let f =
|
|
2490
|
-
f.length && y("not-imported",
|
|
2491
|
-
let p =
|
|
2492
|
-
p.length && y("hook-order",
|
|
2493
|
-
let m =
|
|
2494
|
-
m.length && y("inner-component",
|
|
2495
|
-
let g =
|
|
2496
|
-
g.length && y("stamp-as-timestamp",
|
|
2497
|
-
let _ =
|
|
2498
|
-
_.length && y("amount-by-hand",
|
|
2499
|
-
let v =
|
|
2500
|
-
v.length && y("needs-index",
|
|
2501
|
-
let b =
|
|
2502
|
-
if (b.length && y("no-prefix",
|
|
2503
|
-
let e =
|
|
2504
|
-
e.length && y("currency-mismatch",
|
|
2513
|
+
let f = Ie(o);
|
|
2514
|
+
f.length && y("not-imported", Le(t, f));
|
|
2515
|
+
let p = Ve(o);
|
|
2516
|
+
p.length && y("hook-order", Ue(t, p));
|
|
2517
|
+
let m = Ge(o);
|
|
2518
|
+
m.length && y("inner-component", Ke(t, m));
|
|
2519
|
+
let g = qe(o);
|
|
2520
|
+
g.length && y("stamp-as-timestamp", Ye(t, g));
|
|
2521
|
+
let _ = Xe(o);
|
|
2522
|
+
_.length && y("amount-by-hand", ot(t, _));
|
|
2523
|
+
let v = st(o);
|
|
2524
|
+
v.length && y("needs-index", ct(t, v));
|
|
2525
|
+
let b = dt(o);
|
|
2526
|
+
if (b.length && y("no-prefix", ft(t, b)), Tt.test(o) && y("own-firebase", Et(t, o)), oe(t, o, !1), u) {
|
|
2527
|
+
let e = Zs(o, u);
|
|
2528
|
+
e.length && y("currency-mismatch", $s(t, e, u));
|
|
2505
2529
|
}
|
|
2506
|
-
let x =
|
|
2507
|
-
x && y("notify-as-toast",
|
|
2508
|
-
let S =
|
|
2509
|
-
S && y("notify-body",
|
|
2510
|
-
let te =
|
|
2511
|
-
te !== null && y("popup-open",
|
|
2512
|
-
let C =
|
|
2513
|
-
C && y("hand-by-field",
|
|
2514
|
-
let w =
|
|
2515
|
-
w && y("own-hand-over",
|
|
2530
|
+
let x = lc(o);
|
|
2531
|
+
x && y("notify-as-toast", bc(t, x));
|
|
2532
|
+
let S = uc(o);
|
|
2533
|
+
S && y("notify-body", dc(t, S));
|
|
2534
|
+
let te = fc(o);
|
|
2535
|
+
te !== null && y("popup-open", pc(t, te));
|
|
2536
|
+
let C = gc(o);
|
|
2537
|
+
C && y("hand-by-field", _c(t, C));
|
|
2538
|
+
let w = vc(o);
|
|
2539
|
+
w && y("own-hand-over", yc(t, w, /<HandOver\b/.test(o))), pr.test(o) && y("bare-textarea", mr(t));
|
|
2516
2540
|
let T = _r(o);
|
|
2517
|
-
T.length && y("own-swatch",
|
|
2541
|
+
T.length && y("own-swatch", Vr(t, T));
|
|
2518
2542
|
let E = yr(o);
|
|
2519
|
-
E && y("own-picture-set",
|
|
2520
|
-
let
|
|
2521
|
-
|
|
2522
|
-
let
|
|
2523
|
-
|
|
2524
|
-
let
|
|
2525
|
-
|
|
2526
|
-
let
|
|
2527
|
-
|
|
2528
|
-
let
|
|
2529
|
-
|
|
2530
|
-
let
|
|
2531
|
-
|
|
2532
|
-
let se =
|
|
2533
|
-
|
|
2534
|
-
let
|
|
2535
|
-
|
|
2536
|
-
let
|
|
2537
|
-
|
|
2538
|
-
let
|
|
2539
|
-
|
|
2540
|
-
let
|
|
2541
|
-
|
|
2542
|
-
let
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
let
|
|
2548
|
-
|
|
2549
|
-
let ue =
|
|
2550
|
-
ue.length &&
|
|
2551
|
-
let de = W(
|
|
2552
|
-
de.length && y("
|
|
2553
|
-
let fe = W(o,
|
|
2554
|
-
fe.length && y("
|
|
2555
|
-
let pe = W(o,
|
|
2556
|
-
pe.length &&
|
|
2557
|
-
let me =
|
|
2558
|
-
me.length && h("
|
|
2559
|
-
let he =
|
|
2560
|
-
he && h("
|
|
2561
|
-
let I =
|
|
2562
|
-
I
|
|
2563
|
-
let ge =
|
|
2564
|
-
ge.length && y("
|
|
2565
|
-
let
|
|
2566
|
-
_e.length && y("
|
|
2567
|
-
let ve =
|
|
2568
|
-
|
|
2569
|
-
let
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2543
|
+
E && y("own-picture-set", wr(t, E));
|
|
2544
|
+
let ne = Sr(o);
|
|
2545
|
+
ne.length && y("swapped-upload", Cr(t, ne));
|
|
2546
|
+
let D = ns(o) ? null : kr(o);
|
|
2547
|
+
D && y("own-stepper", Ar(t, D));
|
|
2548
|
+
let re = Nr(o);
|
|
2549
|
+
re && y("own-chart", Pr(t, re));
|
|
2550
|
+
let O = o.match(Fr);
|
|
2551
|
+
O && y("full-reload", Ir(t, O[0]));
|
|
2552
|
+
let ie = zr(o);
|
|
2553
|
+
ie.length && y("typed-id", Br(t, ie));
|
|
2554
|
+
let ae = W(o, nr);
|
|
2555
|
+
ae.length && y("media-tag", rr(t, ae)), ir.test(o) && h("bare-img", $a(t));
|
|
2556
|
+
let se = ri(o), ce = se.filter((e) => !e.held);
|
|
2557
|
+
ce.length && y("boxed-picture", ai(t, ce));
|
|
2558
|
+
let k = se.filter((e) => e.held);
|
|
2559
|
+
k.length && y("boxed-upload", ai(t, k));
|
|
2560
|
+
let A = _i(o);
|
|
2561
|
+
A.length && y("repeated-items", Ei(t, A));
|
|
2562
|
+
let le = wi(o);
|
|
2563
|
+
le.length && y("picture-twice", Ti(t, le));
|
|
2564
|
+
let j = mn(o, Za), M = ji(j);
|
|
2565
|
+
M && y("own-popup", Mi(t, M));
|
|
2566
|
+
let N = [...new Set(W(Pi(o), Ni))];
|
|
2567
|
+
N.length && y("own-toast", Fi(t, N));
|
|
2568
|
+
let P = Qi(o);
|
|
2569
|
+
P.length && y("one-per-row", $i(t, P));
|
|
2570
|
+
for (let [e, n] of Qa(t, j)) y(e, n);
|
|
2571
|
+
let F = tu(t, j, ee);
|
|
2572
|
+
F.length && y("leads-nowhere", nu(t, F)), ar.test(o) && h("background-picture", or(t));
|
|
2573
|
+
let ue = dr(o);
|
|
2574
|
+
ue.length && h("stuck-zero", fr(t, ue));
|
|
2575
|
+
let de = W(o, jc);
|
|
2576
|
+
de.length && y("billing-code", Mc(t, de));
|
|
2577
|
+
let fe = W(mn(o, jc), cu);
|
|
2578
|
+
fe.length && y("payment-code", lu(t, fe));
|
|
2579
|
+
let pe = W(o, uu);
|
|
2580
|
+
pe.length && y("email-code", xu(t, pe));
|
|
2581
|
+
let me = W(o, du);
|
|
2582
|
+
me.length && h("storage-code", fu(t, me)), yu.test(o) && y("file-input", bu(t));
|
|
2583
|
+
let he = mu(o);
|
|
2584
|
+
he.length && h("browser-dialog", vu(t, he));
|
|
2585
|
+
let I = xe(o);
|
|
2586
|
+
I && h("ai-code", be(t, I));
|
|
2587
|
+
let ge = W(o, Nc);
|
|
2588
|
+
ge.length && y(ge.some((e) => !e.startsWith("runTransaction")) ? "booking-code" : "own-transaction", Pc(t, ge));
|
|
2589
|
+
let _e = Ic(o);
|
|
2590
|
+
_e.length && y("stale-times", Lc(t, _e));
|
|
2591
|
+
let ve = mn(o, Nc), ye = W(ve, Ro);
|
|
2592
|
+
ye.length && y("private-collection", zo(t, ye));
|
|
2593
|
+
let L = Oc.test(ve);
|
|
2594
|
+
L && y("own-tally", Ac(t));
|
|
2595
|
+
let R = kc(W(ve, Ec, 1), L);
|
|
2596
|
+
R.length && y("public-write", Dc(t, R));
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
let j = /* @__PURE__ */ new Map();
|
|
2574
2600
|
for (let n of t) {
|
|
2575
2601
|
let t = It(n, w, c, T);
|
|
2576
2602
|
if (!t || !e.some((e) => e.file === t)) continue;
|
|
@@ -2578,19 +2604,19 @@ function Do(e, t = [], n = {}) {
|
|
|
2578
2604
|
if (!r.length) continue;
|
|
2579
2605
|
let i = Yt(t, E);
|
|
2580
2606
|
if (!i.whole) continue;
|
|
2581
|
-
let a = i.files.map((e) => E.find((t) => t.file === e)?.source || ""), o =
|
|
2607
|
+
let a = i.files.map((e) => E.find((t) => t.file === e)?.source || ""), o = j.get(t) || [];
|
|
2582
2608
|
for (let e of Vt(r, a)) o.some((t) => t.url === e.url) || o.push(e);
|
|
2583
|
-
o.length &&
|
|
2609
|
+
o.length && j.set(t, o);
|
|
2584
2610
|
}
|
|
2585
|
-
for (let [e, t] of
|
|
2586
|
-
let
|
|
2587
|
-
if (
|
|
2611
|
+
for (let [e, t] of j) y("picture-dropped", Gt(e, t));
|
|
2612
|
+
let M = e.find((e) => e.file === s);
|
|
2613
|
+
if (M) {
|
|
2588
2614
|
let t = e.filter((e) => J(e.file) && !e.helper && e.file.startsWith(`${o}/`)).map((e) => ({
|
|
2589
2615
|
file: e.file,
|
|
2590
2616
|
name: q(e.file),
|
|
2591
2617
|
source: Y(e.source),
|
|
2592
2618
|
keys: g(q(e.file))
|
|
2593
|
-
})), { broken: n, unregistered: r } = Ot(Y(
|
|
2619
|
+
})), { broken: n, unregistered: r } = Ot(Y(M.source), t, o);
|
|
2594
2620
|
n.length && h("registry-import", kt(s, n)), r.length && y("unregistered", At(s, r));
|
|
2595
2621
|
}
|
|
2596
2622
|
return a.sort((e, t) => Number(t.level === "error") - Number(e.level === "error")), {
|
|
@@ -2599,51 +2625,51 @@ function Do(e, t = [], n = {}) {
|
|
|
2599
2625
|
findings: a
|
|
2600
2626
|
};
|
|
2601
2627
|
}
|
|
2602
|
-
var
|
|
2603
|
-
let t = [], n = RegExp(`(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*${
|
|
2628
|
+
var Mo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHeading|div|section|>)`), No = String.raw`\buserRole\s*(?:===|!==|==|!=)\s*['"\`](?:admin|internal)['"\`]`, Po = (e) => {
|
|
2629
|
+
let t = [], n = RegExp(`(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*${No}`, "g");
|
|
2604
2630
|
for (let r of e.matchAll(n)) t.push(r[1]);
|
|
2605
2631
|
return t;
|
|
2606
|
-
},
|
|
2632
|
+
}, Fo = (e) => Mo(No).test(e) || Po(e).some((t) => Mo(`\\b${t}\\b`).test(e)), Io = (e) => {
|
|
2607
2633
|
let t = /* @__PURE__ */ new Set();
|
|
2608
2634
|
for (let n of e.matchAll(/['"`](mem|admin|user|role|case)_[A-Za-z0-9_]+['"`]/g)) t.add(n[1]);
|
|
2609
2635
|
return t;
|
|
2610
|
-
},
|
|
2611
|
-
let t = e.match(
|
|
2636
|
+
}, Lo = (e) => `${e}: a section here is hidden from people by their role, but everything this page reads sits in one collection they may all read anyway — so it is hidden on screen and kept from nobody, and a browser console reads it straight out of the database. Whatever only some of them may see belongs in a collection whose audience matches: admin_ for the admins alone, user_ with that person's own uid for their own records, role_<role>_ for one of the owner's roles, mem_ for the team, case_ for an entry that names who it was sent to. Keep the check as well, so nobody meets a permission error. If what is hidden is only a BUTTON on records everybody here may read, that is right as it is — nothing to change.`, Ro = /['"`](?:mem|admin|user|role|case)_(?!notifications['"`]|chat_notes['"`]|chat_state['"`])[A-Za-z0-9_]+['"`]/, zo = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are private collections" : "is a private collection"} and this page is open to anyone, so every visitor gets a permission error instead of the data — read a pub_ copy here instead, kept in sync by the page that owns the data (it writes the same change to the pub_ copy on every add, edit and delete, under the same document id). Never name a mem_, admin_, user_, role_ or case_ collection on a public page.`, Bo = /(addDoc\s*\(\s*)?collection\s*\(\s*db\s*,\s*['"`]case_[A-Za-z0-9_]+['"`]/g, Vo = /\b(?:getDocs|onSnapshot|query)\s*\(/, Ho = (e) => [...e.matchAll(Bo)].some((e) => !e[1]) && Vo.test(e), Uo = /\b(?:setDoc|updateDoc|addDoc|deleteDoc)\s*\([^;]{0,200}?['"`]pub_[A-Za-z0-9_]+['"`]/, Wo = /['"`](user|case)_(?!profiles['"`]|submissions['"`]|requests['"`]|notifications['"`])[A-Za-z0-9_]+['"`]/, Go = (e) => {
|
|
2637
|
+
let t = e.match(Uo);
|
|
2612
2638
|
if (!t) return null;
|
|
2613
|
-
let n = e.match(
|
|
2639
|
+
let n = e.match(Wo) || (/\buseCases\s*\(/.test(e) ? ["a case_ collection"] : null);
|
|
2614
2640
|
return n ? {
|
|
2615
2641
|
write: t[0],
|
|
2616
2642
|
token: n[0]
|
|
2617
2643
|
} : null;
|
|
2618
|
-
},
|
|
2619
|
-
if (!
|
|
2620
|
-
let t = e.match(
|
|
2621
|
-
return t ? `${t[1]} carried onto the payment by hand` :
|
|
2622
|
-
},
|
|
2644
|
+
}, Ko = (e, t) => `${e}: this page copies ${t} into a pub_ collection, and a pub_ collection is the open internet — the pasted rules let anybody read one, signed in or not. ${t.startsWith("'case") || t === "a case_ collection" ? "An entry that names who it was sent to" : "One person's own records"} must never go there: the copy hands it to every visitor at once. Take the pub_ write out. There is nothing to copy — the rules read a collection's NAME and never which page asked, so two pages behind sign-in read the SAME collection and each person gets their own on both; and where the other page is one anybody can open, a visitor is not that person, so it shows none of it and links to the signed-in page instead.`, qo = /<PayButton\b|\bstartCheckout\s*\(/, Jo = /\b(orderEntryId|orderId|entryId|submissionId)\s*:/, Yo = /<ContactPopup\b[\s\S]{0,600}?\bextra\s*=/, Xo = (e) => {
|
|
2645
|
+
if (!qo.test(e) || /<(?:Checkout|Basket)\b/.test(e)) return null;
|
|
2646
|
+
let t = e.match(Jo);
|
|
2647
|
+
return t ? `${t[1]} carried onto the payment by hand` : Yo.test(e) ? "an order form beside the Pay button" : null;
|
|
2648
|
+
}, Zo = (e) => /<(?:Basket|BuyButton|PayButton|Checkout)\b[^>]*?\bcurrency\s*=\s*\{?\s*['"`]([A-Za-z]{3})['"`]/.exec(e)?.[1].toLowerCase() ?? /\bmoney\s*\([^()]*(?:\([^()]*\)[^()]*)*,\s*['"`]([A-Za-z]{3})['"`]\s*\)/.exec(e)?.[1].toLowerCase() ?? null, Qo = (e) => e ? ` currency="${e}"` : "", $o = (e, t, n = null) => `${e}: this page pays for an order with pieces it wired together itself (${t}) — that hand-over is where a buyer pays and no order is ever saved, or the sale is refused. Replace the <BuyerDetails>, the order form and the <PayButton> of that last step with ONE <Checkout formId="…" items={…} extras={…}${Qo(n)} phone address /> from 'your-ai-workflow-firebase-os', keeping the same formId: give it the basket (each item's id, name, options, quantity, price for one, picture) and it asks who is paying, saves the order whole and only then starts the payment. Delete the order's *Form.config.ts if it asked nothing. Change nothing else on the page.`, es = /\buseVisitorState\s*(?:<[^()]*?>)?\s*\(\s*['"`]([^'"`]*(?:basket|cart|bag)[^'"`]*)['"`]/i, ts = /<(?:PayButton|BuyButton|Checkout|BuyerDetails|CheckoutReturn)\b|\bstartCheckout\s*\(/, ns = (e) => /<Basket\b/.test(e) || !(ts.test(e) || Yo.test(e)) ? null : e.match(es)?.[1] ?? null, rs = (e, t, n, r = null) => `${e}: this page keeps its basket in useVisitorState('${t}') and draws the bag, its lines and its steps itself — so the count on the bag is a second copy that stays a press behind, and the drawer is the page's own guess at where the one button goes. Hand all of it to the app's pieces from 'your-ai-workflow-firebase-os': const basket = useBasket('${t}') wherever something is added or counted — basket.add({ id, name, options, price, picture, max }) (price is for ONE, options the choices in words, max what is in stock; it opens the drawer itself), basket.count, basket.lines; <BasketButton name="${t}" /> where the bag goes; and ONE <Basket name="${t}" formId="…"${Qo(r)} phone address extras={…} /> near the top of the page for the drawer — the lines with their steppers, then who is paying and the Pay button. ${n ? "Move the props of the <Checkout> this page has onto that <Basket> (it has the checkout in it) and delete the <Checkout>" : "It has the checkout in it, so there is no <Checkout>, <BuyerDetails> or <PayButton> of your own"}, the useVisitorState, the drawer or window, the quantity buttons and every basket step. Keep <CheckoutReturn basket="${t}" … />, the product cards and everything else on the page exactly as they are.`, is = (e, t) => `${e}: this page keeps a list in useVisitorState('${t}'), draws it itself and hands it to a form it wired up — so the count on its button is a second copy that stays a press behind, and the drawer is the page's own guess at where the one button goes. Hand all of it to the app's pieces from 'your-ai-workflow-firebase-os': const basket = useBasket('${t}') wherever something is added or counted — basket.add({ id, name, options, picture }) (a price only if the thing has one; it opens the drawer itself), basket.count, basket.lines; <BasketButton name="${t}" /> where the list is opened; and ONE <Basket send name="${t}" formId="…" /> near the top of the page, with the formId the <ContactPopup> had. Its last step IS that form, drawn in the drawer, and the list rides on the entry as items — so delete the <ContactPopup … extra=…>, the useVisitorState, the drawer or window and the quantity buttons. Keep the form's *Form.config.ts, the cards and everything else on the page exactly as they are.`, as = (e) => /<BuyerDetails\b/.test(e) && /<PayButton\b/.test(e) && !/<(?:BuyButton|Checkout|Basket)\b/.test(e), os = (e) => `${e}: this page asks who is paying with <BuyerDetails> and then shows a <PayButton> it wired to the answer itself — two presses where there should be one, boxes standing open on the page, and a button that pays without who they are the day one spread is forgotten. Replace the pair with ONE <BuyButton> from 'your-ai-workflow-firebase-os' carrying the same amount / productName (or priceId / mode), currency, metadata and successPath the <PayButton> had, plus phone / address if the <BuyerDetails> had them: its press opens one small window that says what is being bought, asks who is paying and goes straight on to Stripe. A subscription's plans are <Plans product="…" /> instead, which reads the plans, prices and ids from the owner's Payments screen. Delete the buyer state, the <BuyerDetails> and the <PayButton>; keep the button's words, look and place, and change nothing else on the page.`, ss = /['"`]session_id['"`]|\bconfirmPayment\s*\(/, cs = (e) => {
|
|
2623
2649
|
if (/<CheckoutReturn\b/.test(e)) return null;
|
|
2624
|
-
let t = e.match(
|
|
2650
|
+
let t = e.match(ss);
|
|
2625
2651
|
return t ? t[0].replace(/\s*\($/, "()") : null;
|
|
2626
|
-
},
|
|
2652
|
+
}, ls = (e, t) => `${e}: this page works out for itself what happens when a buyer comes back from paying (${t}) — which is how a basket is emptied before the money moved, or somebody is thanked for a payment that failed. Delete that code and its thank-you, and write <CheckoutReturn basket="…" orderPath="…" /> from 'your-ai-workflow-firebase-os' ONCE near the top of the page, outside any basket window or step: basket is the useVisitorState key the basket is kept under, orderPath the page their order is on. It asks the server, and only then empties the basket and thanks them. Change nothing else on the page.`, us = /\b(useCases|usePublished|useCase)\s*(?:<[^<>()]*>)?\s*\(\s*['"`]([A-Za-z0-9_]+)['"`]/g, ds = (e) => [...e.matchAll(us)].filter((e) => !e[2].startsWith("case_")).map((e) => ({
|
|
2627
2653
|
hook: e[1],
|
|
2628
2654
|
name: e[2]
|
|
2629
|
-
})),
|
|
2655
|
+
})), fs = (e, t) => {
|
|
2630
2656
|
let n = t[0];
|
|
2631
2657
|
return `${e}: ${n.hook}('${n.name}') reads NOTHING — ${n.hook}() is for a page whose entries are sent to people, a case_ collection, and given any other name it opens no listener at all and answers { loading: false, records: [] } for ever. So this page draws its empty line however many records are saved, and every save on it says "Saved" over a page that never changes. The records of the page you are filling are HANDED to you, live, already from the page's own collection: const { records, loading } = actions — never a name you write out, never a listener of your own. Save with await actions.saveRecord(record), which answers the new record's id; change with actions.updateRecord(id, change); delete with actions.deleteRecord(id); and the new row is in actions.records the moment it is written. Delete the ${n.hook}() call${t.length > 1 ? "s" : ""} and the import${t.length > 1 ? "s" : ""} with ${t.length > 1 ? "them" : "it"}.`;
|
|
2632
|
-
},
|
|
2658
|
+
}, ps = /\bsaveRecord\s*\(\s*['"`]([A-Za-z0-9_]+)['"`]/g, ms = /\b(updateRecord|deleteRecord)\s*\(\s*['"`]((?:mem|admin|user|role|case|pub)_[A-Za-z0-9_]+)['"`]\s*,/g, hs = (e) => [...[...e.matchAll(ps)].map((e) => ({
|
|
2633
2659
|
call: "saveRecord",
|
|
2634
2660
|
name: e[1]
|
|
2635
|
-
})), ...[...e.matchAll(
|
|
2661
|
+
})), ...[...e.matchAll(ms)].map((e) => ({
|
|
2636
2662
|
call: e[1],
|
|
2637
2663
|
name: e[2]
|
|
2638
|
-
}))],
|
|
2664
|
+
}))], gs = (e, t) => {
|
|
2639
2665
|
let n = t[0], r = n.call === "saveRecord" ? "actions.saveRecord(record)" : n.call === "updateRecord" ? "actions.updateRecord(id, change)" : "actions.deleteRecord(id)";
|
|
2640
2666
|
return `${e}: actions.${n.call}('${n.name}', …) passes the collection, and none of these calls takes one — they are already bound to the page's own collection, the one you are handed as actions.collection, and a file that has actions never writes a collection name out. ${r} is the whole call. A collection passed in front of the record used to be saved INSTEAD of it — { ...'${n.name}' } is { 0: '${n.name[0]}', 1: '${n.name[1]}', … } — so the record went in with none of its own fields under a "Saved" on the screen. And the save answers the new record's id: const id = await actions.saveRecord(record), which is the id a history line (logHistory) or a link to the new record needs — never a res.id off it${t.length > 1 ? `. There are ${t.length} of these in this file — put every one right` : ""}.`;
|
|
2641
|
-
},
|
|
2667
|
+
}, _s = /^(?:user_submissions|user_requests|user_profiles|user_notifications|user_phoneclaims)$/, vs = String.raw`(?:addDoc|setDoc|updateDoc|deleteDoc)`, ys = String.raw`['"\x60]((?:mem|admin|user|role)_[A-Za-z0-9_]+)['"\x60]`, bs = new RegExp(String.raw`\b${vs}\s*\(\s*(?:doc|collection)\s*\(\s*db\s*,\s*${ys}`, "g"), xs = new RegExp(String.raw`\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:doc|collection)\s*\(\s*db\s*,\s*${ys}`, "g"), Ss = (e) => {
|
|
2642
2668
|
let t = /* @__PURE__ */ new Set();
|
|
2643
|
-
for (let n of e.matchAll(
|
|
2644
|
-
for (let n of e.matchAll(
|
|
2669
|
+
for (let n of e.matchAll(bs)) _s.test(n[1]) || t.add(n[1]);
|
|
2670
|
+
for (let n of e.matchAll(xs)) _s.test(n[2]) || new RegExp(String.raw`\b${vs}\s*\(\s*${n[1].replace(/\$/g, "\\$")}\b`).test(e) && t.add(n[2]);
|
|
2645
2671
|
return [...t];
|
|
2646
|
-
},
|
|
2672
|
+
}, Cs = (e, t) => `${e}: this page writes ${G(t)} itself, and the page you are filling already has the calls that write its records — each one bound to its OWN collection, the one it is handed as actions.collection: await actions.saveRecord(record), which answers the new record's id; actions.updateRecord(id, change); actions.deleteRecord(id), which takes the record's files down with it. A collection written out by hand is a different collection the moment it is a word off: a Products page that wrote mem_products, where its own page keeps mem_products_records, saved every product where nothing read it and deleted nothing it was asked to delete, neither of them saying a word. Even spelled right it loses what the app puts on a record as it saves: who made it and when (<MadeBy record={r} />), the lock that decides who may delete it, the date under a stock count that makes what is LEFT right, the row on the page before the server answers, and the public copy a page anyone can open reads. Write through those three, delete the hand-written reads and writes, and with them the doc/collection/setDoc/addDoc/deleteDoc imports the page no longer needs.`, ws = {
|
|
2647
2673
|
useCases: [
|
|
2648
2674
|
"loading",
|
|
2649
2675
|
"records",
|
|
@@ -2695,38 +2721,38 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2695
2721
|
"createWorkspace",
|
|
2696
2722
|
"createOrg"
|
|
2697
2723
|
]
|
|
2698
|
-
},
|
|
2724
|
+
}, Ts = {
|
|
2699
2725
|
useVisitorState: "Its answer comes apart both ways — const { value, setValue, clear } = useVisitorState('draft', {}) or const [draft, setDraft] = useVisitorState('draft', {}) — and there is no state, setState or data on it: read under those names it is undefined, so what was typed is never kept and never shown.",
|
|
2700
2726
|
useAuth: "Who is signed in is user — const { user, userRole } = useAuth(), then user?.uid and user?.email — and there is no uid, email or name on the answer itself: read under those names it is undefined, so every record is saved with no owner and every notify({ toUid }) reaches nobody."
|
|
2701
|
-
},
|
|
2727
|
+
}, Es = "(useCases|usePublished|useCase|usePeople|useVisitorState|useAuth)", Ds = new RegExp(String.raw`\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*${Es}\b`, "g"), Os = new RegExp(String.raw`\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*${Es}\b`, "g"), ks = (e) => {
|
|
2702
2728
|
let t = /* @__PURE__ */ new Map(), n = (e, n) => {
|
|
2703
|
-
!n ||
|
|
2729
|
+
!n || ws[e].includes(n) || t.set(e, (t.get(e) || /* @__PURE__ */ new Set()).add(n));
|
|
2704
2730
|
};
|
|
2705
|
-
for (let t of e.matchAll(
|
|
2731
|
+
for (let t of e.matchAll(Ds)) for (let e of t[1].split(",")) {
|
|
2706
2732
|
let r = e.split(/[:=]/)[0].trim();
|
|
2707
2733
|
r && !r.startsWith("...") && n(t[2], r);
|
|
2708
2734
|
}
|
|
2709
|
-
for (let t of e.matchAll(
|
|
2735
|
+
for (let t of e.matchAll(Os)) for (let r of e.matchAll(RegExp(`\\b${t[1].replace(/\$/g, "\\$")}\\s*\\??\\.\\s*([A-Za-z_$][\\w$]*)`, "g"))) n(t[2], r[1]);
|
|
2710
2736
|
return [...t].map(([e, t]) => ({
|
|
2711
2737
|
hook: e,
|
|
2712
2738
|
read: [...t]
|
|
2713
2739
|
}));
|
|
2714
|
-
},
|
|
2715
|
-
let r =
|
|
2740
|
+
}, As = (e, { hook: t, read: n }) => {
|
|
2741
|
+
let r = ws[t], i = Ts[t];
|
|
2716
2742
|
if (i) return `${e}: ${t}() hands back { ${t === "useAuth" ? "user, userRole, isAdmin, loading, …" : r.join(", ")} } and nothing else — this page reads ${n.map((e) => `\`${e}\``).join(" and ")} from it, which ${n.length === 1 ? "is" : "are"} never there. ${i} Change nothing else on the page.`;
|
|
2717
2743
|
let a = r[1], o = a === "people" ? "people" : a === "record" ? "entry" : "entries", s = a === "record" ? "so the page draws nothing whatever has arrived and shows its not-found line to everybody" : "so the list is empty whatever has arrived and the page shows its empty line to everybody";
|
|
2718
2744
|
return `${e}: ${t}() hands back { ${r.join(", ")} } and nothing else — this page reads ${n.map((e) => `\`${e}\``).join(" and ")} from it, which ${n.length === 1 ? "is" : "are"} never there, ${s}. Write const { ${r.join(", ")} } = ${t}(…) and read the ${o} from \`${a}\`. Change nothing else on the page.`;
|
|
2719
|
-
},
|
|
2720
|
-
let t =
|
|
2745
|
+
}, js = /(?:access|editors)\s*:\s*\[[^\]]*['"`]everyone['"`]|(?:access|editors)\s*:\s*arrayUnion\s*\(\s*['"`]everyone['"`]|arrayUnion\s*\(\s*['"`]everyone['"`]|arrayRemove\s*\(\s*['"`]everyone['"`]/, Ms = (e) => {
|
|
2746
|
+
let t = js.exec(e);
|
|
2721
2747
|
return t ? /editors/.test(t[0]) ? "editors" : "access" : "";
|
|
2722
|
-
},
|
|
2748
|
+
}, Ns = (e, t) => t === "editors" ? `${e}: this page writes 'everyone' onto a record's editors list. That would mean anybody signed in may rewrite or delete anybody's — the security rules refuse the write outright, so nothing is saved and the person is told nothing. 'everyone' belongs on \`access\` and nowhere else, and it is publish('case_…', id) that puts it there. Take the word out of editors; leave the author's own key on it.` : `${e}: this page writes 'everyone' into a record's access list by hand. Putting it there as the record is created publishes it the moment it is saved — there is no draft, and the owner asked for one. Use the two calls from 'your-ai-workflow-firebase-os': save it with withAccess(data, { uid }) and nothing else, then publish('case_…', id) from the button the person presses when they are ready, unpublish(…) to take it down, isPublished(record) to know which it is.`, Ps = /^data:|placehold|placeholder|picsum|unsplash|dummyimage|loremflickr|example\.com|your[-_]?logo|logo[-_]?here/i, Fs = (e, t) => {
|
|
2723
2749
|
let n = RegExp(`\\b${t}\\s*:\\s*(['"\`])([\\s\\S]*?)\\1`).exec(e);
|
|
2724
2750
|
return n ? n[2].trim() : null;
|
|
2725
|
-
},
|
|
2726
|
-
let t = ["logoUrl", "faviconUrl"], n = new Map(t.map((t) => [t,
|
|
2751
|
+
}, Is = (e) => {
|
|
2752
|
+
let t = ["logoUrl", "faviconUrl"], n = new Map(t.map((t) => [t, Fs(e, t)])), r = /DO NOT EDIT: logoUrl/.test(e), i = [];
|
|
2727
2753
|
for (let e of t) {
|
|
2728
2754
|
let t = n.get(e) ?? null, a = n.get(e === "logoUrl" ? "faviconUrl" : "logoUrl");
|
|
2729
|
-
t ?
|
|
2755
|
+
t ? Ps.test(t) && i.push({
|
|
2730
2756
|
key: e,
|
|
2731
2757
|
why: "stand-in"
|
|
2732
2758
|
}) : (r || a) && i.push({
|
|
@@ -2735,40 +2761,40 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2735
2761
|
});
|
|
2736
2762
|
}
|
|
2737
2763
|
return i;
|
|
2738
|
-
},
|
|
2764
|
+
}, Ls = (e, { key: t, why: n }) => {
|
|
2739
2765
|
let r = t === "logoUrl" ? "logo, the picture in the top bar" : "favicon, the picture in the browser tab";
|
|
2740
2766
|
return n === "stand-in" ? `${e}: \`${t}\` points at a stand-in rather than at my ${r}. That value is my brand and is never yours to choose: put back the link that was there. If you do not have it, say so and stop — I upload it in the "Design it with your assistant" box on my /theme screen, and the sentence that box gives me carries the link for you to paste in. Never a placeholder, never a picture you generated, never a link I did not give you.` : `${e}: \`${t}\` is empty — my ${r}, is gone from my app. This file ships with it set and there is exactly one thing that may ever change it: a link in a message of mine. Put the old value back, character for character (its own history in this file is where it is). If it cannot be recovered, say so plainly and change nothing: I upload it again in the "Design it with your assistant" box on my /theme screen, and the sentence that box gives me carries the link. Never fill it with a placeholder, a picture you generated, or any link I did not give you.`;
|
|
2741
|
-
},
|
|
2767
|
+
}, Rs = (e) => `${e}: a plain query on a case_ collection is refused for everybody but an admin — the security rules prove a list by the key it names, and only useCases() asks for it that way. Read it with useCases('case_tickets') from 'your-ai-workflow-firebase-os' instead ({ loading, records, error }); addDoc(collection(db, '…'), withAccess(data, { uid })) is still how an entry is created.`, zs = /\bimport\s+(?:type\s+)?(?:[A-Za-z_$][\w$]*\s*,\s*)?\{([^}]*)\}\s*from\s*['"]your-ai-workflow-firebase-os['"]/g, Bs = new Set([
|
|
2742
2768
|
"BuyButton",
|
|
2743
2769
|
"PayButton",
|
|
2744
2770
|
"Basket",
|
|
2745
2771
|
"Checkout",
|
|
2746
2772
|
"BasketButton",
|
|
2747
2773
|
"CheckoutReturn"
|
|
2748
|
-
]),
|
|
2774
|
+
]), Vs = [
|
|
2749
2775
|
"BuyButton",
|
|
2750
2776
|
"PayButton",
|
|
2751
2777
|
"Basket",
|
|
2752
2778
|
"Checkout",
|
|
2753
2779
|
"Plans"
|
|
2754
|
-
],
|
|
2780
|
+
], Hs = /\bimport\s+(?!type\s)(?:[A-Za-z_$][\w$]*\s*,\s*)?\{([^}]*)\}\s*from\s*['"]your-ai-workflow-firebase-os['"]/g, Us = (e) => {
|
|
2755
2781
|
let t = [];
|
|
2756
|
-
for (let n of e.matchAll(
|
|
2782
|
+
for (let n of e.matchAll(Hs)) for (let e of n[1].split(",")) {
|
|
2757
2783
|
if (/^\s*type\s/.test(e)) continue;
|
|
2758
2784
|
let [n, r] = e.trim().split(/\s+as\s+/).map((e) => e.trim());
|
|
2759
|
-
|
|
2785
|
+
Bs.has(n) && t.push([n, r || n]);
|
|
2760
2786
|
}
|
|
2761
2787
|
if (!t.length) return [];
|
|
2762
|
-
let n =
|
|
2788
|
+
let n = Pi(e.replace(/\bimport\s[^;]*?\bfrom\s*['"][^'"]+['"]/g, ""));
|
|
2763
2789
|
return [...new Set(t.filter(([, e]) => !new RegExp(String.raw`(?<![\w$.])${e.replace(/\$/g, "\\$")}(?![\w$])`).test(n)).map(([e]) => e))];
|
|
2764
|
-
},
|
|
2790
|
+
}, Ws = (e, t) => {
|
|
2765
2791
|
let n = Dn(t), r = t.match(/\bexport\s+default\s+(?:React\.)?(?:memo|forwardRef)\s*\(\s*([A-Za-z_$][\w$]*)\s*[,)]/)?.[1];
|
|
2766
2792
|
return `${e}: App.tsx imports { Home } from this file — ${n && n !== "default" ? `keep the default export as it is and add \`export { ${n === "Home" ? "Home" : `${n} as Home`} };\` on a line of its own below it` : r ? `keep the default export as it is and add \`export { ${r === "Home" ? "Home" : `${r} as Home`} };\` on a line of its own below it` : "give the page its name: write it as `export function Home() { … }` in place of the default export, and put `export default Home;` below it"}; with only a default export or another name the owner's home page is never found and visitors get the starter page instead. Add that and change nothing else.`;
|
|
2767
|
-
},
|
|
2793
|
+
}, Gs = "Basket|BuyButton|Checkout|PayButton|Plans", Ks = (e) => {
|
|
2768
2794
|
if (e === void 0 || e === !0) return null;
|
|
2769
2795
|
let t = e.startsWith("{") ? /^\{\s*(['"`])([^'"`$]*)\1\s*\}$/.exec(e)?.[2] : e;
|
|
2770
2796
|
return t && /^[A-Za-z]{3}$/.test(t.trim()) ? t.trim().toLowerCase() : null;
|
|
2771
|
-
},
|
|
2797
|
+
}, qs = (e) => {
|
|
2772
2798
|
let t = [];
|
|
2773
2799
|
for (let n of e.matchAll(/(?<![\w$.])money\s*\(/g)) {
|
|
2774
2800
|
let r = [], i = 0, a = "", o = (n.index ?? 0) + n[0].length;
|
|
@@ -2788,11 +2814,11 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2788
2814
|
t.push(r);
|
|
2789
2815
|
}
|
|
2790
2816
|
return t;
|
|
2791
|
-
},
|
|
2792
|
-
let t = Y(e), n = [...new Set([...t.matchAll(
|
|
2817
|
+
}, Js = /(?<![\w$.])([A-Za-z_$][\w$]*)\s*\??\.\s*currency\b/g, Ys = (e) => {
|
|
2818
|
+
let t = Y(e), n = [...new Set([...t.matchAll(Js)].map((e) => e[1]))];
|
|
2793
2819
|
if (!n.length) return null;
|
|
2794
2820
|
let r = (e) => n.find((t) => RegExp(`(?<![\\w$.])${t.replace(/\$/g, "\\$")}\\s*\\??\\.`).test(e)), i = "", a = [];
|
|
2795
|
-
for (let e of
|
|
2821
|
+
for (let e of qs(t)) {
|
|
2796
2822
|
let t = e.length === 1 ? r(e[0]) : void 0;
|
|
2797
2823
|
t && (i ||= t, a.push(`money(${e[0].replace(/\s+/g, " ")})`));
|
|
2798
2824
|
}
|
|
@@ -2800,16 +2826,16 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2800
2826
|
owner: i,
|
|
2801
2827
|
calls: [...new Set(a)]
|
|
2802
2828
|
} : null;
|
|
2803
|
-
},
|
|
2829
|
+
}, Xs = (e, t) => `${e}: ${G(t.calls.map((e) => `"${e}"`))} ${t.calls.length > 1 ? "draw amounts" : "draws an amount"} of ${t.owner}, which carries its own currency — this page reads ${t.owner}.currency — without it. money(amount) with nothing after it is the owner's account currency, so an order paid in euros shows "$" in an account kept in dollars, beside its own "EUR". Every amount on it — its total, its subtotal, each item's price and each extra — is money(amount, ${t.owner}.currency), in the currency it was paid in: pass that second word to each of them, and change nothing else.`, Zs = (e, t) => {
|
|
2804
2830
|
let n = /* @__PURE__ */ new Set();
|
|
2805
|
-
for (let t of e.matchAll(
|
|
2831
|
+
for (let t of e.matchAll(Hs)) for (let e of t[1].split(",")) {
|
|
2806
2832
|
let [t, r] = e.trim().split(/\s+as\s+/).map((e) => e.trim());
|
|
2807
2833
|
t && !/^type\s/.test(e.trim()) && n.add(r || t);
|
|
2808
2834
|
}
|
|
2809
2835
|
let r = [];
|
|
2810
|
-
for (let { tag: i, attrs: a } of $(e,
|
|
2836
|
+
for (let { tag: i, attrs: a } of $(e, Gs)) {
|
|
2811
2837
|
if (!n.has(i)) continue;
|
|
2812
|
-
let o =
|
|
2838
|
+
let o = Nl(a), s = Ks(o.get("currency"));
|
|
2813
2839
|
if (!s || s === t || (i === "BuyButton" || i === "PayButton") && o.has("priceId")) continue;
|
|
2814
2840
|
let c = i === "Basket" && o.has("collection") || i === "Checkout" && (o.has("collection") || /\bcollection\s*:/.test(e));
|
|
2815
2841
|
r.push({
|
|
@@ -2818,8 +2844,8 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2818
2844
|
shape: i === "Plans" ? "words" : c ? "records" : "typed"
|
|
2819
2845
|
});
|
|
2820
2846
|
}
|
|
2821
|
-
if (n.has("money")) for (let n of
|
|
2822
|
-
let e = n.length === 2 ?
|
|
2847
|
+
if (n.has("money")) for (let n of qs(e)) {
|
|
2848
|
+
let e = n.length === 2 ? Ks(/^(['"`])[^'"`$]*\1$/.test(n[1]) ? n[1].slice(1, -1) : void 0) : null;
|
|
2823
2849
|
e && e !== t && r.push({
|
|
2824
2850
|
piece: "money()",
|
|
2825
2851
|
said: e,
|
|
@@ -2827,7 +2853,7 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2827
2853
|
});
|
|
2828
2854
|
}
|
|
2829
2855
|
return r;
|
|
2830
|
-
},
|
|
2856
|
+
}, Qs = (e) => {
|
|
2831
2857
|
try {
|
|
2832
2858
|
return new Intl.NumberFormat("en", {
|
|
2833
2859
|
style: "currency",
|
|
@@ -2836,10 +2862,10 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2836
2862
|
} catch {
|
|
2837
2863
|
return e;
|
|
2838
2864
|
}
|
|
2839
|
-
},
|
|
2840
|
-
let r = G([...new Set(t.map((e) => e.piece))]), i = [...new Set(t.map((e) => e.said))], a = t.find((e) => e.shape === "records") || t.find((e) => e.shape === "typed") || t[0], o = a.shape === "records" ? `the owner's Stripe account charges in ${n}, and a basket checked against her records is refused in any other currency` : a.shape === "typed" ? `its visitors are charged in ${a.said} while her account and every other price in the app are in ${n}` : `the words say ${
|
|
2865
|
+
}, $s = (e, t, n) => {
|
|
2866
|
+
let r = G([...new Set(t.map((e) => e.piece))]), i = [...new Set(t.map((e) => e.said))], a = t.find((e) => e.shape === "records") || t.find((e) => e.shape === "typed") || t[0], o = a.shape === "records" ? `the owner's Stripe account charges in ${n}, and a basket checked against her records is refused in any other currency` : a.shape === "typed" ? `its visitors are charged in ${a.said} while her account and every other price in the app are in ${n}` : `the words say ${Qs(a.said)} while the charge is in ${n}`, s = [...new Set(t.filter((e) => e.piece !== "money()").map((e) => e.said))], c = [...new Set(t.filter((e) => e.piece === "money()").map((e) => e.said))], l = [s.length ? `each ${s.map((e) => `currency='${e}'`).join(" and ")} on this page to currency='${n}'` : "", c.length ? `each ${c.map((e) => `money(…, '${e}')`).join(" and ")} to money(…, '${n}')` : ""].filter(Boolean).join(", and ");
|
|
2841
2867
|
return `${e}: ${r} ${t.length > 1 ? "name" : "names"} ${i.join(" and ")} — ${o}. Change ${l} — or leave the currency out of the piece and out of money(), and the app uses the account's own.`;
|
|
2842
|
-
},
|
|
2868
|
+
}, ec = (e, t, n, r = []) => {
|
|
2843
2869
|
let i = G(t.map((e) => `<${e}>`)), a = t.length > 1 ? "are" : "is";
|
|
2844
2870
|
if (r.length) return `${e}: ${i} ${a} imported and never drawn, and this page takes its payment with ${G(r.map((e) => `<${e}>`))} — delete ${t.length > 1 ? "those names" : "that name"} from the import line, and change nothing else.`;
|
|
2845
2871
|
if (n === !0) {
|
|
@@ -2847,7 +2873,7 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2847
2873
|
return `${e}: ${i} ${a} imported and never drawn, and this page's sentence takes money here — as it stands nobody can pay: whatever the page books or sends goes through for nothing. Draw ${t.length > 1 ? "them" : "it"} where the page's sentence asked${n}, and change nothing else.`;
|
|
2848
2874
|
}
|
|
2849
2875
|
return n === !1 ? `${e}: ${i} ${a} imported and never drawn, and the owner has no payment on this page — delete ${t.length > 1 ? "those names" : "that name"} from the import line, and change nothing else.` : `${e}: ${i} ${a} imported and never drawn — if the page's sentence asked for a payment here, draw ${t.length > 1 ? "them" : "it"} where it asked; if it did not, delete ${t.length > 1 ? "those names" : "that name"} from the import line.`;
|
|
2850
|
-
},
|
|
2876
|
+
}, tc = {
|
|
2851
2877
|
useFirestore: "db — import { db } from 'your-ai-workflow-firebase-os', with collection(db, '…') from 'firebase/firestore'",
|
|
2852
2878
|
useDb: "db — import { db } from 'your-ai-workflow-firebase-os'",
|
|
2853
2879
|
firestore: "db — import { db } from 'your-ai-workflow-firebase-os'",
|
|
@@ -2869,7 +2895,7 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2869
2895
|
formatCurrency: "money(amount)",
|
|
2870
2896
|
Image: "<Picture>",
|
|
2871
2897
|
Img: "<Picture>"
|
|
2872
|
-
},
|
|
2898
|
+
}, nc = new Set([
|
|
2873
2899
|
"collection",
|
|
2874
2900
|
"onSnapshot",
|
|
2875
2901
|
"doc",
|
|
@@ -2891,47 +2917,47 @@ var Oo = (e) => RegExp(`(?:${e})\\s*(?:&&|\\?)\\s*\\(?\\s*<\\s*(?:Card|SectionHe
|
|
|
2891
2917
|
"Timestamp",
|
|
2892
2918
|
"writeBatch",
|
|
2893
2919
|
"runTransaction"
|
|
2894
|
-
]),
|
|
2920
|
+
]), rc = (e) => tc[e] || (nc.has(e) ? `${e} from 'firebase/firestore' — import { ${e} } from 'firebase/firestore'; only db comes from the package` : null) || ic(e), ic = (e) => {
|
|
2895
2921
|
let t = e.toLowerCase();
|
|
2896
|
-
return
|
|
2897
|
-
},
|
|
2922
|
+
return L.find((e) => e.toLowerCase() === t) || L.filter((e) => e.length > 3 && (t.includes(e.toLowerCase()) || e.toLowerCase().includes(t))).sort((t, n) => Math.abs(t.length - e.length) - Math.abs(n.length - e.length))[0] || null;
|
|
2923
|
+
}, ac = /^(?:ChatThread|openChat|useAiChat|askAi|useAiEnabled|useAiModels)$/, oc = (e) => {
|
|
2898
2924
|
let t = /* @__PURE__ */ new Set();
|
|
2899
|
-
for (let n of Y(e).matchAll(
|
|
2925
|
+
for (let n of Y(e).matchAll(zs)) for (let e of n[1].split(",")) {
|
|
2900
2926
|
let n = e.trim().replace(/^type\s+/, "").split(/\s+as\s+/)[0].trim();
|
|
2901
|
-
n && /^[A-Za-z_$][\w$]*$/.test(n) && !
|
|
2927
|
+
n && /^[A-Za-z_$][\w$]*$/.test(n) && !ac.test(n) && !L.includes(n) && t.add(n);
|
|
2902
2928
|
}
|
|
2903
2929
|
return [...t];
|
|
2904
|
-
},
|
|
2905
|
-
let t =
|
|
2930
|
+
}, sc = (e, t) => `${e}: ${G(t.map((e) => {
|
|
2931
|
+
let t = rc(e);
|
|
2906
2932
|
return t ? `${e} (what is there: ${t})` : e;
|
|
2907
|
-
}))} ${t.length > 1 ? "are" : "is"} imported from 'your-ai-workflow-firebase-os', and the package has nothing by ${t.length > 1 ? "those names" : "that name"} — the import fails, and because every page is loaded through registry.ts the WHOLE app stops loading, not only this page. Never guess a name: import only what the app's instructions name, spelled as they spell it. Take ${t.length > 1 ? "each one" : "it"} out of the import and use what is there in its place; if nothing the instructions name does the job, write it in this file from React and 'firebase/firestore' rather than importing a piece that does not exist. (If the instructions DO name it, the installed package is older than they are: update it.)`,
|
|
2908
|
-
for (let t of Y(e).matchAll(
|
|
2933
|
+
}))} ${t.length > 1 ? "are" : "is"} imported from 'your-ai-workflow-firebase-os', and the package has nothing by ${t.length > 1 ? "those names" : "that name"} — the import fails, and because every page is loaded through registry.ts the WHOLE app stops loading, not only this page. Never guess a name: import only what the app's instructions name, spelled as they spell it. Take ${t.length > 1 ? "each one" : "it"} out of the import and use what is there in its place; if nothing the instructions name does the job, write it in this file from React and 'firebase/firestore' rather than importing a piece that does not exist. (If the instructions DO name it, the installed package is older than they are: update it.)`, cc = /\bnotify\s*\(\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\)/g, lc = (e) => {
|
|
2934
|
+
for (let t of Y(e).matchAll(cc)) if (!/\bto(?:Uid|Role)\b|\.\.\./.test(t[1])) return t[0].replace(/\s+/g, " ").slice(0, 80);
|
|
2909
2935
|
return null;
|
|
2910
|
-
},
|
|
2911
|
-
for (let t of Y(e).matchAll(
|
|
2936
|
+
}, uc = (e) => {
|
|
2937
|
+
for (let t of Y(e).matchAll(cc)) if (/\bto(?:Uid|Role)\b|\.\.\./.test(t[1]) && /\bmessage\s*:/.test(t[1]) && !/\bbody\s*:/.test(t[1])) return t[0].replace(/\s+/g, " ").slice(0, 80);
|
|
2912
2938
|
return null;
|
|
2913
|
-
},
|
|
2939
|
+
}, dc = (e, t) => `${e}: "${t}" — notify() takes { toUid or toRole, title, body, link }, and message is not one of its words: the bell shows title in bold with body under it, so this notification arrives with nothing to read. Put the one-line headline in title and the rest in body, and change nothing else about it.`, fc = (e) => {
|
|
2914
2940
|
for (let t of $(Y(e), "Popup")) if (!(/\.\.\./.test(t.attrs) || /\bopen\b/.test(t.attrs))) return t.attrs.replace(/\s+/g, " ").trim().slice(0, 60);
|
|
2915
2941
|
return null;
|
|
2916
|
-
},
|
|
2942
|
+
}, pc = (e, t) => `${e}: a <Popup${t ? ` ${t}…` : ""}> here has no open prop — a window is drawn only while open is true, so this one never appears, and nothing says why. Give it the same state that decides whether it is on screen — <Popup open={!!chosen} onClose={() => setChosen(null)} title="…"> — and keep it rendered rather than mounted only while that state is set, so it can ease in and out (rule 30).`, mc = /\b(?:assign(?:ed|ee)(?:To|Uid|Id)|assignedUserId|handedTo|heldBy|holderUid)\s*:/, hc = (e) => {
|
|
2917
2943
|
let t = e;
|
|
2918
2944
|
for (;;) {
|
|
2919
2945
|
let e = /\b(?:export\s+)?(?:interface\s+[A-Za-z_$][\w$]*[^{]*|type\s+[A-Za-z_$][\w$]*(?:<[^>]*>)?\s*=\s*)\{/.exec(t);
|
|
2920
2946
|
if (!e) return t;
|
|
2921
|
-
let n = (e.index ?? 0) + e[0].length - 1, r =
|
|
2947
|
+
let n = (e.index ?? 0) + e[0].length - 1, r = Kc(t, n);
|
|
2922
2948
|
if (r < 0) return t;
|
|
2923
2949
|
t = `${t.slice(0, e.index)}${t.slice(r + 1)}`;
|
|
2924
2950
|
}
|
|
2925
|
-
},
|
|
2926
|
-
let t =
|
|
2927
|
-
return !n || /\bsendTo\s*\(/.test(t) || /\bactions\s*\.\s*sendTo\b/.test(t) ? null :
|
|
2928
|
-
},
|
|
2929
|
-
let t =
|
|
2930
|
-
return n && /\bsendTo\s*\([^;]*?\bpeople\s*:/.test(t) &&
|
|
2931
|
-
},
|
|
2932
|
-
function
|
|
2951
|
+
}, gc = (e) => {
|
|
2952
|
+
let t = hc(Y(e)), n = t.match(/\buseCases\s*(?:<[^>]*>)?\s*\(\s*['"]([^'"]+)['"]/);
|
|
2953
|
+
return !n || /\bsendTo\s*\(/.test(t) || /\bactions\s*\.\s*sendTo\b/.test(t) ? null : mc.test(t) ? n[1] : null;
|
|
2954
|
+
}, _c = (e, t) => `${e}: an entry here is handed to somebody by writing who holds it onto the record, and sendTo() is never called — the entry's own lists do not change, so the person it was handed to is never sent it and sees nothing, whatever the record says. Handing one over is the app's own piece: <HandOver entry={entry} collection='${t}' name={…} /> imported from 'your-ai-workflow-firebase-os' — the collection this page reads with useCases — on each entry's card (compact) and inside the opened entry (label='Assigned to'). It shows who holds the entry, is the picker for whoever may hand it over, sends it, takes it off whoever had it and rings the person. Replace the picker, its button and the fields written by hand with it, and change nothing else on the page.`, vc = (e) => {
|
|
2955
|
+
let t = hc(Y(e)), n = t.match(/\buseCases\s*(?:<[^>]*>)?\s*\(\s*['"]([^'"]+)['"]/);
|
|
2956
|
+
return n && /\bsendTo\s*\([^;]*?\bpeople\s*:/.test(t) && mc.test(t) ? n[1] : null;
|
|
2957
|
+
}, yc = (e, t, n = !1) => `${e}: handing an entry to a person is built by hand here${n ? " — beside the <HandOver> the page already draws" : ""} — a picker, a button, sendTo(), takeBack() and who holds it written onto the entry. That is the app's own piece: <HandOver entry={entry} collection='${t}' name={…} /> imported from 'your-ai-workflow-firebase-os' — name is what the entry is called, for the bell. It shows who holds the entry by their picture and their name; for whoever may hand it over it is the picker, and picking a person hands it over at once, takes whoever had it off, keeps who holds it on the entry and rings them — and it draws that picker for nobody the owner's database would refuse. Replace the picker, its Assign and Unassign buttons, the usePeople() behind them, the sendTo/takeBack calls to one person, the notify() that told them and every field that wrote who holds it with <HandOver>: on each entry's card (compact) and inside the opened entry (label='Assigned to'). Where the page drew who holds an entry as words, that is <HandOver> too. Change nothing else on the page.`, bc = (e, t) => `${e}: "${t}" — notify() is the BELL: a notification sent to somebody else, and it needs who it is for (toUid for one person, toRole for an audience) and a title. Called like this it sends nothing and shows nothing, so nobody is ever told their save went through. To answer the person who just pressed something it is say('Saved') imported from 'your-ai-workflow-firebase-os' — one brief line, no options needed. Change every notify({ … }) that has no toUid or toRole into say('the same words'), and keep notify() only where somebody ELSE has to hear about it.`, xc = /<ChatThread\b|\bopenChat\s*\(/, Sc = /\buseVisitorState\b/, Cc = /\btally\s*\(/, wc = /\b(?:saveRecord|updateRecord|deleteRecord|usePublicCopy|useStockFromOrders|useTallies|logHistory|useCases|userRole)\b/, Tc = (e) => (Sc.test(e) || Cc.test(e)) && !wc.test(e), Ec = /\b(updateDoc|setDoc|addDoc|deleteDoc|writeBatch)\s*\(/, Dc = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "write" : "writes"} the database from a page anyone can open, and a visitor is not signed in — the rules refuse every write from here with "Missing or insufficient permissions", a pub_ copy included: the page that owns the data writes the copy, this page only reads it. Delete ${t.length > 1 ? "every one of those writes" : "the write"}. What a visitor does goes through the app's own form — <ContactPopup formId="…" isInline bare extra={{ … }} onSubmitted={…} /> from a *Form.config.ts, with what the page worked out (the items, their options, the total) in extra — a <PayButton>, or a schedule question; the page that owns the data reads the entry while an admin has it open and applies it once, to its own record and the pub_ copy together.`, Oc = /\bincrement\s*\(/, kc = (e, t) => t ? e.filter((e) => e !== "updateDoc" && e !== "setDoc") : e, Ac = (e) => `${e}: increment( counts what visitors do by writing the database from a page anyone can open, and a visitor is not signed in — the rules refuse that write for every real visitor, so the number only ever moves for the owner testing their own page while signed in. Delete the write and the increment import. Count with the app's own piece, imported from 'your-ai-workflow-firebase-os': tally('products', product.id, 'views') where the thing is opened — the word for what is counted, the record's own id, and one of views, added, clicks, saves, shares or downloads. It counts a visitor once a visit, leaves the team's own looks out, never throws and never holds the press up, so it is called and not awaited. A shop's <Basket tally="products"> counts every add itself. The page for the team reads them with useTallies('products') — counts.of(product.id).views. Never keep a count on the record, and never in a pub_ collection.`, jc = /fetch\(\s*['"`]\/api\/payments\/portal|cancel_at_period_end|cancelSubscription|subscriptions?\.(?:cancel|del)\(/, Mc = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} a hand-rolled billing flow — the app already has one. Cancel, resume, plan switches, card changes and invoices are ALL Stripe's own portal: render <ManagePlanButton /> imported from 'your-ai-workflow-firebase-os' (buyers also reach it on the built-in /billing page) and delete this code.`, Nc = /['"`](?:mem_bookings|pub_bookings|sys_schedules|sys_bookingpages)['"`]|runTransaction\s*\(/, Pc = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} hand-rolled booking code, and booking a time is not part of this version of the app. Delete it: people ask for a time on the page's form instead — a date question and a short text question in its *Form.config.ts, shown with <ContactPopup formId="…" isInline /> — and each request arrives in the owner's Inbox like any other entry. Say in your reply that booking a time is not in this version.`, Fc = /\b(availableSlots|availableSlotsFor|togetherSlots|slotsFor)\s*\(/g;
|
|
2958
|
+
function Ic(e) {
|
|
2933
2959
|
let t = [];
|
|
2934
|
-
for (let n of e.matchAll(
|
|
2960
|
+
for (let n of e.matchAll(Fc)) {
|
|
2935
2961
|
let r = (n.index ?? 0) + n[0].length - 1, i = 0, a = -1;
|
|
2936
2962
|
for (let t = r; t < e.length; t++) if (e[t] === "(") i++;
|
|
2937
2963
|
else if (e[t] === ")" && --i === 0) {
|
|
@@ -2944,42 +2970,42 @@ function Mc(e) {
|
|
|
2944
2970
|
}
|
|
2945
2971
|
return t;
|
|
2946
2972
|
}
|
|
2947
|
-
var
|
|
2973
|
+
var Lc = (e, t) => {
|
|
2948
2974
|
let n = t[0].trim().replace(/\s+/g, " ");
|
|
2949
2975
|
return `${e}: ${n.length > 80 ? `${n.slice(0, 80)}…` : n} works out free times without the booked ones, so the page offers hours somebody has already booked. Read the booked times with useTakenSlots(memberIds, from) from 'your-ai-workflow-firebase-os' — memberIds the people the page shows, from ONE Date kept in state — and pass them in: availableSlotsFor(schedules, { now: new Date(), taken }). A time booked on ANY booking link, page or schedule of that person is taken on all of them.${K(t.length)}`;
|
|
2950
|
-
},
|
|
2976
|
+
}, Rc = /\b(setDoc|updateDoc|addDoc|deleteDoc)\s*\(/g, zc = String.raw`\b(?:console|logger|log|Sentry)\s*\.\s*\w+`, Bc = /^(?:noop|nop|ignore|ignoreErrors?|swallow|doNothing|_\.noop)$/, Vc = (e, t) => new RegExp(String.raw`\b(?:const|let|var)\s+${t.replace(/\$/g, "\\$")}\s*=\s*(?:async\s*)?(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>\s*(?:\{\s*\}|null\b|undefined\b|void\s*0)|\bfunction\s+${t.replace(/\$/g, "\\$")}\s*\([^()]*\)\s*\{\s*\}`).test(e), Hc = (e, t = "", n = !1) => {
|
|
2951
2977
|
let r = e.trim().replace(/^async\s+/, "").replace(/^(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>\s*/, "").replace(/^function\s*[\w$]*\s*\([^()]*\)\s*/, "");
|
|
2952
|
-
if (RegExp(`^${
|
|
2978
|
+
if (RegExp(`^${zc}$`).test(r)) return !0;
|
|
2953
2979
|
if (((e) => /^\(?\s*(?:null|undefined|void\s*0|false|true|-?\d+(?:\.\d+)?|''|""|\[\s*\])\s*\)?$/.test(e.trim()))(r)) return !n;
|
|
2954
|
-
if (/^[A-Za-z_$][\w$.]*$/.test(r)) return
|
|
2955
|
-
let i = RegExp(`${
|
|
2980
|
+
if (/^[A-Za-z_$][\w$.]*$/.test(r)) return Bc.test(r) || Vc(t, r);
|
|
2981
|
+
let i = RegExp(`${zc}\\s*\\(`);
|
|
2956
2982
|
for (let e = r.search(i); e !== -1; e = r.search(i)) {
|
|
2957
|
-
let t =
|
|
2983
|
+
let t = Uc(r, r.indexOf("(", e));
|
|
2958
2984
|
if (t === -1) break;
|
|
2959
2985
|
r = `${r.slice(0, e)}${r.slice(t + 1)}`;
|
|
2960
2986
|
}
|
|
2961
2987
|
return r.replace(/\b(?:return|null|undefined|void\s*0)\b|[\s;{}()]/g, "") === "";
|
|
2962
|
-
},
|
|
2988
|
+
}, Uc = (e, t) => {
|
|
2963
2989
|
let n = 0;
|
|
2964
2990
|
for (let r = t; r < e.length; r++) if (e[r] === "(") n++;
|
|
2965
2991
|
else if (e[r] === ")" && --n === 0) return r;
|
|
2966
2992
|
return -1;
|
|
2967
2993
|
};
|
|
2968
|
-
function
|
|
2994
|
+
function Wc(e) {
|
|
2969
2995
|
let t = [];
|
|
2970
|
-
for (let n of e.matchAll(
|
|
2971
|
-
let r =
|
|
2996
|
+
for (let n of e.matchAll(Rc)) {
|
|
2997
|
+
let r = Uc(e, (n.index ?? 0) + n[0].length - 1);
|
|
2972
2998
|
if (r === -1) continue;
|
|
2973
2999
|
let i = e.slice(r + 1).match(/^\s*\.catch\s*\(/);
|
|
2974
3000
|
if (!i) continue;
|
|
2975
|
-
let a = r + 1 + i[0].length - 1, o =
|
|
3001
|
+
let a = r + 1 + i[0].length - 1, o = Uc(e, a);
|
|
2976
3002
|
if (o === -1) continue;
|
|
2977
3003
|
let s = e.slice(a + 1, o), c = e.slice(0, n.index ?? 0).replace(/\s+$/, "").replace(/\b(?:await|void)$/, "").replace(/\s+$/, "");
|
|
2978
|
-
|
|
3004
|
+
Hc(s, e, !(c === "" || /[;{}]$/.test(c) || /\bon[A-Z]\w*\s*=\s*\{\s*(?:async\s*)?(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>$/.test(c))) && t.push(`${n[1]}(…).catch(${s.replace(/\s+/g, " ").trim()})`);
|
|
2979
3005
|
}
|
|
2980
3006
|
return t;
|
|
2981
3007
|
}
|
|
2982
|
-
var
|
|
3008
|
+
var Gc = (e) => e.replace(/console\s*\.\s*\w+\s*\([^()]*\)/g, "").replace(/[\s;]/g, "") === "", Kc = (e, t) => {
|
|
2983
3009
|
let n = 0;
|
|
2984
3010
|
for (let r = t; r < e.length; r++) {
|
|
2985
3011
|
let t = e[r];
|
|
@@ -3001,16 +3027,16 @@ var Vc = (e) => e.replace(/console\s*\.\s*\w+\s*\([^()]*\)/g, "").replace(/[\s;]
|
|
|
3001
3027
|
else if (t === "}" && --n === 0) return r;
|
|
3002
3028
|
}
|
|
3003
3029
|
return -1;
|
|
3004
|
-
},
|
|
3005
|
-
function
|
|
3030
|
+
}, qc = /\btry\s*\{/g, Jc = /^\s*catch\s*(?:\([^()]*\)\s*)?\{/;
|
|
3031
|
+
function Yc(e) {
|
|
3006
3032
|
let t = [];
|
|
3007
|
-
for (let n of e.matchAll(
|
|
3008
|
-
let r = (n.index ?? 0) + n[0].length - 1, i =
|
|
3033
|
+
for (let n of e.matchAll(qc)) {
|
|
3034
|
+
let r = (n.index ?? 0) + n[0].length - 1, i = Kc(e, r);
|
|
3009
3035
|
if (i === -1) continue;
|
|
3010
|
-
let a = e.slice(i + 1).match(
|
|
3036
|
+
let a = e.slice(i + 1).match(Jc);
|
|
3011
3037
|
if (!a) continue;
|
|
3012
|
-
let o = i + 1 + a[0].length - 1, s =
|
|
3013
|
-
if (s === -1 || !
|
|
3038
|
+
let o = i + 1 + a[0].length - 1, s = Kc(e, o);
|
|
3039
|
+
if (s === -1 || !Gc(e.slice(o + 1, s))) continue;
|
|
3014
3040
|
let c = e.slice(r + 1, i);
|
|
3015
3041
|
if (/\btry\s*\{/.test(c)) continue;
|
|
3016
3042
|
let l = c.match(/\b(setDoc|updateDoc|addDoc|deleteDoc)\s*\(/);
|
|
@@ -3018,33 +3044,33 @@ function Gc(e) {
|
|
|
3018
3044
|
}
|
|
3019
3045
|
return t;
|
|
3020
3046
|
}
|
|
3021
|
-
var
|
|
3047
|
+
var Xc = (e, t) => `${e}: ${t[0]} throws the failure away — the write is awaited inside a try whose catch tells nobody, so the page carries on as though it landed and whatever it says next ("Saved", a tick, a row it adds to a list) it says whether Firestore took the write or refused it. A refusal is the ordinary case here, not a freak one: the rules refuse a write whose collection prefix is not that person's, so a trail the TEAM appends to in an admin_ collection records the admins and silently records nobody else (contract rule 11). In the catch, say plainly that it did not save and leave what they typed on the screen so they can send it again (contract rule 19).${K(t.length)}`, Zc = (e, t) => `${e}: ${t[0].length > 72 ? `${t[0].slice(0, 72)}…` : t[0]} throws the failure away, so the page carries on as though the write landed — and whatever it says next ("Saved", a tick, a box it clears) it says whether Firestore took the write or refused it. That is the one thing a page must never do: it teaches the owner to trust a number that is not there. Await the write with nothing chained to it, say "Saved" only after it resolves, and in the catch say plainly that it did not save and leave what they typed on the screen so they can send it again (contract rule 19).${K(t.length)}`, Qc = String.raw`(?:(?:e|err|error|ex|caught)\s*\??\.\s*(?:message|code)\b|String\(\s*(?:e|err|error)\s*\))`, $c = new RegExp(String.raw`\bset[A-Z]\w*\(\s*[^;)]{0,90}?` + Qc, ""), el = new RegExp(String.raw`[^=]>\s*\{[^{}]{0,90}?` + Qc + String.raw`[^{}]{0,90}?\}`, ""), tl = (e) => {
|
|
3022
3048
|
let t = [];
|
|
3023
|
-
for (let n of [
|
|
3049
|
+
for (let n of [$c, el]) for (let r of e.matchAll(new RegExp(n.source, "g"))) {
|
|
3024
3050
|
let n = r.index ?? 0, i = e.slice(Math.max(0, n - 60), n);
|
|
3025
3051
|
/console\.\w+\($/.test(i.trimEnd()) || /console\.\w+\([^)]*$/.test(i) || t.push(r[0].trim());
|
|
3026
3052
|
}
|
|
3027
3053
|
return t;
|
|
3028
|
-
},
|
|
3029
|
-
let t = e.match(
|
|
3054
|
+
}, nl = (e, t) => `${e}: ${t[0].length > 72 ? `${t[0].slice(0, 72)}…` : t[0]} puts what you CAUGHT on the screen. Those words are the library talking to a developer — "Missing or insufficient permissions.", "FirebaseError: unavailable", permission-denied — and to the person using the app they mean nothing, read as though they broke something, and leave them with nowhere to go. Say what it means and what to do next instead: troubleSentence(e, 'That could not be saved.'), imported from 'your-ai-workflow-firebase-os', which words a lost connection, a sign-in that ran out, a refusal and a busy app each in their own sentence and falls back to yours for anything else. Put it where the work was, leave what they typed on screen, and give them the button again (contract rule 25).${K(t.length)}`, rl = /['"`](user_submissions|user_requests)['"`]/, il = (e) => {
|
|
3055
|
+
let t = e.match(rl);
|
|
3030
3056
|
return !t || /\bformId\b/.test(e) ? null : t[1];
|
|
3031
|
-
},
|
|
3057
|
+
}, al = /\b(?:user|currentUser)\s*[?!]?\s*\.\s*(?:email|displayName|photoURL)\b/, ol = /\b([A-Za-z_$][\w$]*)\s*:\s*(?:new\s+Date\s*\(\s*)?Date\.now\(\)\s*-(?!=)/g, sl = (e, t) => {
|
|
3032
3058
|
let n = 0;
|
|
3033
3059
|
for (let r = t; r >= 0; r--) if (e[r] === "}") n++;
|
|
3034
3060
|
else if (e[r] === "{" && n-- === 0) return r;
|
|
3035
3061
|
return -1;
|
|
3036
3062
|
};
|
|
3037
|
-
function
|
|
3063
|
+
function cl(e) {
|
|
3038
3064
|
let t = /* @__PURE__ */ new Set();
|
|
3039
|
-
for (let n of e.matchAll(
|
|
3040
|
-
let r =
|
|
3065
|
+
for (let n of e.matchAll(ol)) {
|
|
3066
|
+
let r = sl(e, n.index ?? 0);
|
|
3041
3067
|
if (r === -1) continue;
|
|
3042
|
-
let i =
|
|
3068
|
+
let i = Kc(e, r), a = e.slice(r, i === -1 ? void 0 : i + 1).match(al);
|
|
3043
3069
|
a && t.add(`${n[1]}: Date.now() - … beside ${a[0].replace(/\s+/g, "")}`);
|
|
3044
3070
|
}
|
|
3045
3071
|
return [...t];
|
|
3046
3072
|
}
|
|
3047
|
-
var
|
|
3073
|
+
var ll = (e, t, n) => {
|
|
3048
3074
|
let r = 0;
|
|
3049
3075
|
for (let i = t + 1; i < n; i++) {
|
|
3050
3076
|
let t = e[i];
|
|
@@ -3053,29 +3079,29 @@ var al = (e, t, n) => {
|
|
|
3053
3079
|
else if (t === "," && r === 0) return i;
|
|
3054
3080
|
}
|
|
3055
3081
|
return n;
|
|
3056
|
-
},
|
|
3082
|
+
}, ul = (e, t) => {
|
|
3057
3083
|
let n = t.replace(/\$/g, "\\$"), r = new RegExp(String.raw`\b(?:(?:const|let|var)\s+${n}\s*=\s*(?:useCallback\s*\(\s*)?(?:async\s+)?(?:function\b[^{]*|(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*(?::\s*[^={]+)?=>\s*\(?\s*)|function\s+${n}\s*\([^()]*\)[^{]*)\{`).exec(e);
|
|
3058
3084
|
if (!r) return "";
|
|
3059
|
-
let i =
|
|
3085
|
+
let i = Kc(e, r.index + r[0].length - 1);
|
|
3060
3086
|
return i === -1 ? "" : e.slice(r.index, i + 1);
|
|
3061
3087
|
};
|
|
3062
|
-
function
|
|
3088
|
+
function dl(e) {
|
|
3063
3089
|
for (let t of e.matchAll(/\bonSnapshot\s*\(/g)) {
|
|
3064
|
-
let n = (t.index ?? 0) + t[0].length - 1, r =
|
|
3090
|
+
let n = (t.index ?? 0) + t[0].length - 1, r = Uc(e, n);
|
|
3065
3091
|
if (r === -1) continue;
|
|
3066
|
-
let i =
|
|
3092
|
+
let i = ll(e, n, r), a = e.slice(n + 1, i).trim();
|
|
3067
3093
|
if (/^[A-Za-z_$][\w$]*$/.test(a)) {
|
|
3068
3094
|
let t = new RegExp(String.raw`\b(?:const|let|var)\s+${a.replace(/\$/g, "\\$")}\s*=\s*([^;]{0,300})`).exec(e);
|
|
3069
3095
|
a = t ? t[1] : "";
|
|
3070
3096
|
}
|
|
3071
|
-
let o = a.match(
|
|
3097
|
+
let o = a.match(rl);
|
|
3072
3098
|
if (!o) continue;
|
|
3073
3099
|
let s = e.slice(i, r);
|
|
3074
3100
|
if (/\bsubmitter(?:Name|Email)\b/.test(s)) continue;
|
|
3075
|
-
let c = /* @__PURE__ */ new Set(), l = s.match(
|
|
3101
|
+
let c = /* @__PURE__ */ new Set(), l = s.match(al);
|
|
3076
3102
|
l && c.add(l[0].replace(/\s+/g, ""));
|
|
3077
3103
|
for (let [, t] of s.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
3078
|
-
let n =
|
|
3104
|
+
let n = ul(e, t).match(al);
|
|
3079
3105
|
n && c.add(`${n[0].replace(/\s+/g, "")} (inside ${t})`);
|
|
3080
3106
|
}
|
|
3081
3107
|
if (c.size) return {
|
|
@@ -3085,28 +3111,28 @@ function sl(e) {
|
|
|
3085
3111
|
}
|
|
3086
3112
|
return null;
|
|
3087
3113
|
}
|
|
3088
|
-
var
|
|
3089
|
-
function
|
|
3114
|
+
var fl = /\bdoc\s*\(\s*[\w.]+\s*,\s*['"`]user_?(?:profiles|s)['"`]\s*,\s*([^,)]+)\)/g, pl = /^(?:auth\s*\.\s*)?(?:user|currentUser|me|viewer)\s*[?!]?\s*\.\s*uid$|^uid$/, ml = /\b(createdBy|updatedBy|addedBy|editedBy|modifiedBy|lastEditedBy|lastModifiedBy|createdByName|updatedByName)\s*:\s*[^,\n}]*\b(?:user|currentUser)\s*[?!]?\s*\.\s*(?:email|displayName|uid)\b/g;
|
|
3115
|
+
function hl(e) {
|
|
3090
3116
|
let t = /* @__PURE__ */ new Set();
|
|
3091
|
-
for (let n of e.matchAll(
|
|
3117
|
+
for (let n of e.matchAll(fl)) {
|
|
3092
3118
|
let e = n[1].replace(/\s+/g, "");
|
|
3093
|
-
|
|
3119
|
+
pl.test(e) || t.add(`a profile read for ${e}`);
|
|
3094
3120
|
}
|
|
3095
|
-
for (let n of e.matchAll(
|
|
3121
|
+
for (let n of e.matchAll(ml)) t.add(`${n[1]} signed by hand`);
|
|
3096
3122
|
return [...t];
|
|
3097
3123
|
}
|
|
3098
|
-
var
|
|
3124
|
+
var gl = (e, t) => `${e}: ${G(t)} — the page works out WHO MADE a record for itself. It is already on the record: actions.saveRecord stamps creatorName, creatorAvatar, creatorEmail and createdAt on everything it saves, and actions.updateRecord stamps editedName, editedAvatar and editedAt on every change. Draw them with <MadeBy record={item} /> and <MadeBy record={item} edited /> from 'your-ai-workflow-firebase-os', and delete the rest: reading another person's user_profiles document is "Missing or insufficient permissions" for a role the owner invented and for every member — the card draws for the admin who built the page and for nobody it was built for — and on a card made from an Inbox entry it names the admin instead of the sender; a createdBy: user.email written by hand is an address where a name was wanted. There is no users collection. A person somebody PICKED (an assignee) is saved as a uid AND a name on the record, and the name is what the page draws. The one name a page passes itself is a change made for somebody else: actions.updateRecord(id, { stock, editedName: order.submitterName }) (contract rules 29 and 31).`, _l = (e, t) => `${e}: ${G(t)} — a history line signed with whoever is LOOKING at the page, at a time made up by counting back from now. Lines like that are invented after the fact: every one of them says the owner, or whoever opens the page next, did something they never did, at a time nobody did it. Delete the code that makes them, and any copy of them the page kept in the browser. A history holds only the lines written at the moment something is done, by the save that does it, each signed by the person who did it — and while there are none, it says so (contract rule 29). On a page for the team that is two pieces of the app: logHistory('<the records' collection>', record.id, 'what was done', { from, to }) straight after the save — it signs the line itself — and <History collection="…" of={record.id} /> to draw them.${K(t.length)}`, vl = /['"`]user_(?:submissions|requests)['"`]/, yl = /\bstock\b[^\n;]{0,120}\b(?:quantity|qty)\b/i, bl = (e) => vl.test(e) && yl.test(e) && !/\buseStock(?:FromOrders)?\b/.test(e), xl = /<Basket\b|<Checkout\b|<BuyButton\b|usePublicCopy\(|useTallies\(/, Sl = /\b(?:[A-Za-z_$][\w$]*(?:\??\.?\[[^\]\n]*\])*\??\.)+stock\b(?!SetAt)/, Cl = (e) => xl.test(e) && Sl.test(e) && !/\buseStock(?:FromOrders)?\s*\(/.test(e), wl = String.raw`(?:\b\w*[vV]ariants\s*\??|\(\s*[\w.?]*[vV]ariants\s*(?:\|\||\?\?)\s*\[\]\s*\))\s*\.\s*`, Tl = String.raw`\s*\(\s*\(?\s*\w+\s*(?::\s*[\w<>[\]|. ]+)?,\s*(\w+)\s*(?::\s*[\w<>[\]|. ]+)?\)?\s*=>`, El = RegExp(`${wl}reduce${Tl}[^;]{0,200}?(?:\\b\\1\\s*\\??\\.\\s*stock\\b|\\.left\\s*\\([^)]*\\b\\1\\s*\\??\\.\\s*id\\b)`, "g"), Dl = RegExp(`${wl}flatMap\\s*\\(\\s*\\(?\\s*(\\w+)\\s*(?::\\s*[\\w<>[\\]|. ]+)?\\)?\\s*=>\\s*\\(?\\s*\\1\\s*\\??\\.\\s*(?:shades|colou?rs|swatch(?:es)?)\\b`, "g"), Ol = (e, t) => {
|
|
3099
3125
|
for (let n of e.matchAll(t)) {
|
|
3100
3126
|
let t = e.slice(Math.max(0, (n.index ?? 0) - 160), n.index);
|
|
3101
3127
|
if (!/\.(?:reduce|flatMap)\s*\(|\bfor\s*\(/.test(t)) return !0;
|
|
3102
3128
|
}
|
|
3103
3129
|
return !1;
|
|
3104
|
-
},
|
|
3130
|
+
}, kl = (e) => [...Ol(e, El) ? ["stock"] : [], ...Ol(e, Dl) ? ["shades"] : []], Al = (e, t) => `${e}: this page draws a thing's variants as one — it ${[...t.includes("stock") ? ["adds its variants' stock up into one number (variants.reduce(… .stock …)), to show on its card or to save as the whole product's stock"] : [], ...t.includes("shades") ? ["runs every variant's shades together into one row of swatches (variants.flatMap((v) => v.shades)) that nobody can press"] : []].join(", and ")}. A thing with variants is shown as ONE variant at a time, the chosen one, its card included: every card keeps its own chosen variant (its first until another is pressed), each variant is one <Swatch color={variant.shades[0]} name={variant.name} selected={variant.id === chosen.id} onClick={() => choose(variant)} /> ON the card (its press never opens the card too), and pressing one swaps that card's picture and stock for that variant's right there, without opening anything. The stock shown is the chosen variant's own — stock.left(product, variant.id) where the page uses useStock — never the variants added up, and nothing keeps a product-wide stock beside theirs. A total over the whole shop is fine. Change nothing else on the page.`, jl = (e) => {
|
|
3105
3131
|
if (!/\bcursor-zoom-in\b|<ZoomIn\b/.test(e)) return !1;
|
|
3106
3132
|
let t = $(e, "Lightbox");
|
|
3107
3133
|
return t.length > 0 && t.every((e) => !/(?:^|\s)zoom(?:\s|=|\/|$)/.test(e.attrs));
|
|
3108
|
-
},
|
|
3109
|
-
function
|
|
3134
|
+
}, Ml = (e) => `${e}: this page shows a picture as something to ZOOM (a zoom-in cursor or a magnifier) and then opens it in <Lightbox> without zoom — a gallery: the same picture a little bigger with arrows through the others. Viewing and zooming are two things. Viewing is on the page: the small pictures swap the big one IN PLACE (never open a window). Zooming is pressing the big one: <Lightbox pictures={pictures} index={at} onIndex={setAt} onClose={() => setAt(null)} zoom /> opens it whole and magnifies the spot pressed in it — the close-up then follows the mouse, a finger drags and pinches it, one more press shows it whole again. Add zoom to the <Lightbox>, make the small pictures swap the big one, and change nothing else on the page.`;
|
|
3135
|
+
function Nl(e) {
|
|
3110
3136
|
let t = /* @__PURE__ */ new Map(), n = (t) => {
|
|
3111
3137
|
let n = 0, r = "";
|
|
3112
3138
|
for (let i = t; i < e.length; i += 1) {
|
|
@@ -3153,10 +3179,10 @@ function kl(e) {
|
|
|
3153
3179
|
}
|
|
3154
3180
|
return t;
|
|
3155
3181
|
}
|
|
3156
|
-
var
|
|
3157
|
-
function
|
|
3182
|
+
var Pl = /^pub_[A-Za-z0-9_]{1,120}$/;
|
|
3183
|
+
function Fl(e) {
|
|
3158
3184
|
let t = $(e, "Basket"), n = t.map(({ attrs: e }) => {
|
|
3159
|
-
let t =
|
|
3185
|
+
let t = Nl(e);
|
|
3160
3186
|
return /\{\s*\.\.\./.test(e) && t.set("...", !0), t;
|
|
3161
3187
|
}).filter((e) => {
|
|
3162
3188
|
let t = e.get("send");
|
|
@@ -3168,9 +3194,9 @@ function jl(e) {
|
|
|
3168
3194
|
reads: [...new Set([...r.matchAll(/['"`](pub_[A-Za-z0-9_]+)['"`]/g)].map((e) => e[1]))]
|
|
3169
3195
|
};
|
|
3170
3196
|
}
|
|
3171
|
-
var
|
|
3172
|
-
function
|
|
3173
|
-
let { baskets: n, reads: r } =
|
|
3197
|
+
var Il = (e) => e === !0 ? null : e.startsWith("{") ? /^\{\s*(['"`])([^'"`$]*)\1\s*\}$/.exec(e)?.[2] ?? null : e;
|
|
3198
|
+
function Ll(e, t = []) {
|
|
3199
|
+
let { baskets: n, reads: r } = Fl(e);
|
|
3174
3200
|
return !n.length || n.every((e) => e.has("collection") || e.has("...")) ? null : r.length ? {
|
|
3175
3201
|
reads: r,
|
|
3176
3202
|
elsewhere: !1
|
|
@@ -3179,23 +3205,23 @@ function Nl(e, t = []) {
|
|
|
3179
3205
|
elsewhere: !0
|
|
3180
3206
|
} : null;
|
|
3181
3207
|
}
|
|
3182
|
-
function
|
|
3183
|
-
let { baskets: n, reads: r } =
|
|
3208
|
+
function Rl(e, t = []) {
|
|
3209
|
+
let { baskets: n, reads: r } = Fl(e);
|
|
3184
3210
|
for (let e of n) {
|
|
3185
3211
|
let n = e.get("collection");
|
|
3186
3212
|
if (n === void 0) continue;
|
|
3187
|
-
let i =
|
|
3188
|
-
if (i !== null && (!
|
|
3213
|
+
let i = Il(n);
|
|
3214
|
+
if (i !== null && (!Pl.test(i) || r.length && !r.includes(i) && !t.includes(i))) return {
|
|
3189
3215
|
said: i,
|
|
3190
3216
|
reads: r
|
|
3191
3217
|
};
|
|
3192
3218
|
}
|
|
3193
3219
|
return null;
|
|
3194
3220
|
}
|
|
3195
|
-
var
|
|
3221
|
+
var zl = (e, t, n = !1) => {
|
|
3196
3222
|
let r = n ? "the page that puts things in the basket reads" : "this page reads";
|
|
3197
3223
|
return `${e}: its <Basket> does not say which collection its lines come from, so the server charges what the page says without checking — anybody can send a basket of their own at any price, and nothing bought through it comes off what is left in stock. ${t.length === 1 ? `Add collection="${t[0]}" to it (the public collection ${r})` : `Add collection="…" to it, naming the public collection the things put in the basket are read from — ${r} ${G(t.map((e) => `"${e}"`))}, and every line of the basket comes from one of them`}: the server then reads each line's price off the record before anything is charged. A line's price is the record's price, or its variant's, never a number the page works out; a discount is a Stripe promotion code, typed at checkout.`;
|
|
3198
|
-
},
|
|
3224
|
+
}, Bl = (e, t, n) => `${e}: its <Basket> says collection="${t}", but ${n.length ? `this page reads ${G(n.map((e) => `"${e}"`))}` : "that is not a public collection, and the server reads prices only from the pub_ copy the page reads"} — the server would read each line's price from a collection that is not there and refuse every checkout, for every buyer. ${n.length === 1 ? `Use collection="${n[0]}"` : n.length ? "Use the one its lines come from" : "Use the pub_ name this page reads, as the page's sentence names it"}, letter for letter, and change nothing else.`, Vl = (e) => `${e}: this page reads a stock number straight off a record — product.stock, or a variant's — to show it or to decide whether one can still be bought. That number is the count my team last SET, not what is left: it is right until the first sale and wrong for every visitor from then on, because a sale writes nothing to the record. What is left is the app's to work out: const stock = useStock(records) from 'your-ai-workflow-firebase-os' over the records this page shows, and stock.left(product) or stock.left(product, variant.id) is the number to draw and the number that decides — null where a thing keeps no count. Never product.stock or variants[i].stock in what is shown or compared.`, Hl = (e) => `${e}: this page takes the stock of what was sold by itself — it reads the shop's orders out of user_submissions and counts each line's quantity off a stock number. Written by hand that goes wrong silently: stock taken twice after a reload, taken for a basket nobody paid for, or "Missing or insufficient permissions" for everybody who is not an admin. Nothing is taken off the record at all any more: the record keeps the count my team last set (stock, or variants[i].stock, each variant with an id of its own), the app dates it when it is saved, and what is LEFT is that count minus what Stripe says was paid for since — const stock = useStock(records) from 'your-ai-workflow-firebase-os', and stock.left(product) or stock.left(product, variant.id) is the number, on this page and on the shop alike, for everybody, with nobody signed in. Delete the listener, the useSales() join, the decrement and the list of applied entries.`, Ul = (e, t, n) => `${e}: it applies entries from ${t} — things somebody else SENT — and writes them up signed with ${G(n)}. That code runs in the browser of the admin who has the page open, so every line names that admin, whoever actually placed the order or sent the entry. Take the person off the entry instead: submitterName and submitterEmail when they were signed in, else the name and email they typed into the form, else "A visitor" — and the time off the entry's submittedAt, not the moment the page got round to it. A helper that writes a line takes the person as an argument rather than reading user itself (contract rule 29) — the app's own logHistory(collection, id, what, { by: { name }, at }) does exactly that.`, Wl = (e, t) => {
|
|
3199
3225
|
let n = /<(?:PayButton|BuyButton)\b/.test(e), r = /<PayButton\b/.test(e) && !/<(?:BuyButton|Basket|Checkout|Plans)\b/.test(e), i = [], a = /* @__PURE__ */ new Set();
|
|
3200
3226
|
for (let o of e.matchAll(/<ContactPopup\b/g)) {
|
|
3201
3227
|
let s = o.index ?? 0, c = e.indexOf("/>", s), l = e.slice(s, c === -1 ? s + 800 : c + 2), u = l.match(/formId=\{?\s*['"`]([^'"`]+)['"`]/)?.[1];
|
|
@@ -3222,18 +3248,18 @@ var Fl = (e, t, n = !1) => {
|
|
|
3222
3248
|
}));
|
|
3223
3249
|
}
|
|
3224
3250
|
return i;
|
|
3225
|
-
},
|
|
3251
|
+
}, Gl = (e, t) => `${e}: this is the order ${t} sends back beside its payment, and an order asks the buyer NOTHING (fields: []) — who is paying is their ACCOUNT, and the app's own checkout asks it. Replace that last step — the form, the Pay or Buy button and any <BuyerDetails> — with ONE <Checkout formId="…" items={…} phone address /> from 'your-ai-workflow-firebase-os', keeping this formId (a basket of things is <Basket formId="…" /> instead): it asks who is paying, saves the order whole and only then starts the payment. Take every name, email, phone and address question out of the form's fields. Asked through the form, the buyer has no account: their order can never be shown back to them, and a page built on these entries — an Orders page each buyer sees their own on — has nobody to send it to (contract rule 9).`, Kl = /(?:\bto|\bhref|\borderPath)=\{?\s*['"`]\/([A-Za-z0-9_-]+)[?'"`]|\bnavigate\(\s*['"`]\/([A-Za-z0-9_-]+)[?'"`]/g, ql = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, ""), Jl = /\baddDoc\s*\(\s*collection\s*\(\s*db\s*,\s*['"`]case_|\bsetDoc\s*\(\s*doc\s*\(\s*collection\s*\(\s*db\s*,\s*['"`]case_/, Yl = (e, t, n) => {
|
|
3226
3252
|
let r = (e) => [...t.matchAll(RegExp(`<${e}\\b`, "g"))].map((e) => {
|
|
3227
3253
|
let n = e.index ?? 0, r = t.indexOf("/>", n);
|
|
3228
3254
|
return t.slice(n, r === -1 ? n + 800 : r + 2);
|
|
3229
3255
|
}), i = [...r("Basket"), ...r("Checkout")].find((e) => /\bformId=/.test(e)), a = (i ?? r("ContactPopup").find((e) => /\bextra=/.test(e)))?.match(/formId=\{?\s*['"`]([^'"`]+)['"`]/)?.[1];
|
|
3230
3256
|
if (!a) return [];
|
|
3231
3257
|
let o = [], s = /* @__PURE__ */ new Set(), c = [];
|
|
3232
|
-
for (let e of t.matchAll(
|
|
3233
|
-
let t =
|
|
3258
|
+
for (let e of t.matchAll(Kl)) {
|
|
3259
|
+
let t = ql(e[1] || e[2] || "");
|
|
3234
3260
|
if (!t || s.has(t)) continue;
|
|
3235
3261
|
s.add(t);
|
|
3236
|
-
let r = n.find((e) => J(e.file) &&
|
|
3262
|
+
let r = n.find((e) => J(e.file) && ql(q(e.file).replace(/Content\.tsx$/, "")) === t);
|
|
3237
3263
|
r && c.push(r);
|
|
3238
3264
|
}
|
|
3239
3265
|
let l = (e) => {
|
|
@@ -3248,12 +3274,12 @@ var Fl = (e, t, n = !1) => {
|
|
|
3248
3274
|
}
|
|
3249
3275
|
};
|
|
3250
3276
|
}, u = (e) => {
|
|
3251
|
-
let t = e.match(
|
|
3277
|
+
let t = e.match(rl)?.[1], n = RegExp(`['"\`]${a.replace(/[^A-Za-z0-9_-]/g, "")}['"\`]`).test(e);
|
|
3252
3278
|
return t && n && !/\bappliedEntries\b/.test(e) ? t : void 0;
|
|
3253
3279
|
};
|
|
3254
3280
|
for (let e of c) {
|
|
3255
3281
|
let { theirs: t, gap: n } = l(e);
|
|
3256
|
-
!/\b(?:usePublished|publish)\s*\(/.test(t) && /\buseCases\s*\(/.test(t) &&
|
|
3282
|
+
!/\b(?:usePublished|publish)\s*\(/.test(t) && /\buseCases\s*\(/.test(t) && Jl.test(t) && o.push({
|
|
3257
3283
|
...n,
|
|
3258
3284
|
why: "typed",
|
|
3259
3285
|
inbox: ""
|
|
@@ -3275,9 +3301,9 @@ var Fl = (e, t, n = !1) => {
|
|
|
3275
3301
|
});
|
|
3276
3302
|
}
|
|
3277
3303
|
return o;
|
|
3278
|
-
},
|
|
3304
|
+
}, Xl = (e) => {
|
|
3279
3305
|
let t = [], n = /* @__PURE__ */ new Set(), r = (e, r, i) => {
|
|
3280
|
-
let a = `${
|
|
3306
|
+
let a = `${ql(e)}|${r}`;
|
|
3281
3307
|
!e || n.has(a) || (n.add(a), t.push({
|
|
3282
3308
|
words: e.toLowerCase(),
|
|
3283
3309
|
route: r,
|
|
@@ -3290,7 +3316,7 @@ var Fl = (e, t, n = !1) => {
|
|
|
3290
3316
|
if (i !== "tab" && i !== "page") continue;
|
|
3291
3317
|
let a = Y(n), o = Z(X(a, "route")[0] ?? "");
|
|
3292
3318
|
if (!o || !o.startsWith("/")) continue;
|
|
3293
|
-
let s = Z(X(a, i === "tab" ? "tabName" : "pageName")[0] ?? ""), c = e.find((e) => J(e.file) &&
|
|
3319
|
+
let s = Z(X(a, i === "tab" ? "tabName" : "pageName")[0] ?? ""), c = e.find((e) => J(e.file) && ql(q(e.file).replace(/Content\.tsx$/, "")) === ql(o.slice(1)))?.file ?? "";
|
|
3294
3320
|
s && r(s, o, c), r(o.slice(1).replace(/-/g, " "), o, c);
|
|
3295
3321
|
}
|
|
3296
3322
|
for (let { file: t } of e) {
|
|
@@ -3299,17 +3325,17 @@ var Fl = (e, t, n = !1) => {
|
|
|
3299
3325
|
!e || e === "Dashboard" || r(e.replace(/([a-z0-9])([A-Z])/g, "$1 $2"), `/${e.toLowerCase()}`, t);
|
|
3300
3326
|
}
|
|
3301
3327
|
return t;
|
|
3302
|
-
},
|
|
3328
|
+
}, Zl = (e) => e.replace(/<[^>]*>/g, " ").replace(/\{[^{}]*\}/g, " ").replace(/&\w+;/g, " ").replace(/\s+/g, " ").trim(), Ql = (e, t) => {
|
|
3303
3329
|
let n = e.slice(0, t);
|
|
3304
3330
|
return (n.match(/<Link\b/g) || []).length > (n.match(/<\/Link>/g) || []).length;
|
|
3305
|
-
},
|
|
3306
|
-
function
|
|
3331
|
+
}, $l = /\bhref\s*=\s*(?:\{\s*)?['"`]\s*#?\s*['"`]/, eu = /\bonClick\s*=\s*\{\s*\(\s*\)\s*=>\s*\{\s*\}\s*\}/;
|
|
3332
|
+
function tu(e, t, n) {
|
|
3307
3333
|
let r = [], i = /* @__PURE__ */ new Set();
|
|
3308
3334
|
for (let { tag: a, attrs: o, at: s, end: c } of $(t, "button|Button|a")) {
|
|
3309
|
-
if (/\bonClick\s*=/.test(o) && !
|
|
3335
|
+
if (/\bonClick\s*=/.test(o) && !eu.test(o) || /\bdisabled\b|\{\.\.\./.test(o) || a !== "a" && /type\s*=\s*['"`]submit['"`]/.test(o) || a === "a" && /\bhref\s*=/.test(o) && !$l.test(o) || Ql(t, s)) continue;
|
|
3310
3336
|
let l = t.indexOf(`</${a}>`, c);
|
|
3311
3337
|
if (l === -1 || /<Link\b|<a\b[^>]*\bhref\s*=|\bnavigate\s*\(/.test(t.slice(c + 1, l)) || a !== "a" && !/\btype\s*=/.test(o) && t.lastIndexOf("<form", s) > t.lastIndexOf("</form>", s)) continue;
|
|
3312
|
-
let u =
|
|
3338
|
+
let u = Zl(t.slice(c + 1, l));
|
|
3313
3339
|
if (!u || u.length > 40) continue;
|
|
3314
3340
|
let d = ` ${u.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `, f = n.find((t) => t.file !== e && t.words.length > 2 && d.includes(` ${t.words} `));
|
|
3315
3341
|
!f || i.has(u) || (i.add(u), r.push({
|
|
@@ -3319,15 +3345,15 @@ function Zl(e, t, n) {
|
|
|
3319
3345
|
}
|
|
3320
3346
|
return r;
|
|
3321
3347
|
}
|
|
3322
|
-
var
|
|
3323
|
-
let t =
|
|
3324
|
-
for (let r of e.matchAll(new RegExp(
|
|
3348
|
+
var nu = (e, t) => `${e}: ${G(t.map((e) => `"${e.label}"`))} ${t.length > 1 ? "name pages" : "names a page"} this app HAS — ${G([...new Set(t.map((e) => e.route))])} — and ${t.length > 1 ? "none of them leads anywhere" : "it leads nowhere"}: no link, no handler, nothing happens when it is pressed. This is what a page built BEFORE that one looks like — the button was written when there was nowhere to send it. Wrap exactly the words and classes that are there in <Link to="${t[0].route}" className="…"> (or navigate('${t[0].route}') where a press has to do something first), and change nothing else about it: not its words, not its look, not where it sits.${K(t.length)} (rule 7)`, ru = (e, t, n) => `${e}: ${t} sends what is made there as a form entry ('${n}') and links here — and the app itself lets each of those entries into this page's case_ collection, sent to the person who made it and to the team. So this page only READS them with useCases(): delete the form, button or function that writes a case_ entry by hand. An entry typed in here was paid for by nobody and belongs to nobody, and one sent to a whole role ('role:client') is shown to every person marked with it. While there are no entries yet, show a short empty line — never made-up ones (contract rule 11).`, iu = (e, t) => `${e.target}: it fetches the '${e.formId}' entries out of ${e.inbox} itself — what ${t} sends — and what is in there is not this page's to read. ${e.paid ? "<Checkout> saves each one BEFORE the money is taken, so that list holds every basket somebody walked away from, and this page draws each of them as an order. " : ""}An entry there carries uid and submittedAt — never userId, customerName or createdAt — so a list filtered on a guessed key is empty for the very person who ordered, and nobody but an admin may read the rest of it at all. The app already lets each ${e.paid ? "PAID " : ""}one into this page's own collection, sent to the person who made it and to the team: read them with const { loading, records, error } = useCases('${e.reads}') — the name this page's own sentence gave, if it gave another — and with nothing else. Delete the ${e.inbox} query, and any COPY of the entries kept in a collection of its own for somebody to work from: a stage, a note, who it was handed to are saved ON the entry (updateDoc on it), and handing one to a single person is sendTo('${e.reads}', id, { people: [uid] }) with the person picked from usePeople('internal') — a copy is a second order the person who made it never sees change. Never keep the list in localStorage either. Change nothing about how the page looks (contract rule 11).`, au = (e, t) => `${e}: this form is given extra={…} — an order going back to the page that keeps the record (on ${t}) — it is a payment's, and its entries are half of a transaction, not mail. Add transaction: true to its config, and they land on the owner's Transactions screen beside the payment instead of in the Inbox.`, ou = (e, t) => `${e}: this form stands on a page with a <PayButton> (on ${t}) — the questions asked before paying. Who is paying is not a form, it is their ACCOUNT, and the app's own button asks it: replace the <PayButton> with ONE <BuyButton> from 'your-ai-workflow-firebase-os' carrying the same amount / productName (or priceId / mode), currency, metadata and successPath, plus phone / address when the form asked for them — its press says what is being bought, asks who is paying and goes on to Stripe — then delete the form, its <ContactPopup> and its config. A form here lands every buyer in the Inbox as mail and rings the owner's bell with "New form submission" instead of "New payment" (contract rule 9).`, su = (e, t) => `${e}: it reads ${t} without ever looking at formId, so it takes in every form in the app — the contact form, a newsletter box, a form on somebody else's page — not just this page's own. Read the entry's formId and skip the ones that are not this page's form (const FORM_ID = 'quote'; if (s.formId !== FORM_ID) return;), unless the owner asked for the whole Inbox in so many words. A booking is in neither collection: a time somebody takes is on the calendar, and useBookings() is the only way to read it.`, cu = /loadStripe|@stripe\/stripe-js|new Stripe\(|fetch\(\s*['"`]\/api\/payments/, lu = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} hand-rolled payment code that is not connected to the owner's Stripe account — delete it and take money with <PayButton amount={25} productName="…" /> for a one-off or <PayButton priceId="price_…" mode="subscription" /> for a subscription, imported from 'your-ai-workflow-firebase-os' (read purchases with useMyPayments(); an admins' page reads every sale with useSales()).`, uu = /api\.resend\.com|from\s+['"]resend['"]|fetch\(\s*['"`]\/api\/email/, du = /from\s+['"]firebase\/storage['"]|\buploadBytes(?:Resumable)?\s*\(|\bgetDownloadURL\s*\(|\bgetStorage\s*\(|\bref\s*\(\s*storage\b/, fu = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} upload code written by hand, and the file it writes is guarded by a different rule than the record that links to it — delete it and take the file with <UploadBox value={photo} onChange={setPhoto} label="Product photo" /> imported from 'your-ai-workflow-firebase-os' (add \`shared\` when the picture is also shown on a page this one's people are not the audience for; a file attached while filling in a form is the form's own \`file\` question instead).`, pu = /\b(?:window|globalThis|self)\s*\.\s*(?:confirm|alert|prompt)\b|(?<![\w$.]|function\s)(?:confirm|alert|prompt)\((?![^()]*\)\s*\{)/, mu = (e) => {
|
|
3349
|
+
let t = R(e), n = /* @__PURE__ */ new Set();
|
|
3350
|
+
for (let r of e.matchAll(new RegExp(pu.source, "g"))) {
|
|
3325
3351
|
let i = r[0].replace(/\s+/g, " ").trim(), a = /^(confirm|alert|prompt)\($/.exec(i);
|
|
3326
|
-
a && (new RegExp(String.raw`\b(?:const|let|var|function)\s+${a[1]}\b|\{[^{}]*\b${a[1]}\b[^{}]*\}\s*=`).test(e) ||
|
|
3352
|
+
a && (new RegExp(String.raw`\b(?:const|let|var|function)\s+${a[1]}\b|\{[^{}]*\b${a[1]}\b[^{}]*\}\s*=`).test(e) || _u(e, t, a[1]).some(([e, t]) => (r.index ?? 0) >= e && (r.index ?? 0) < t)) || n.add(i);
|
|
3327
3353
|
}
|
|
3328
3354
|
return [...n];
|
|
3329
|
-
},
|
|
3330
|
-
function
|
|
3355
|
+
}, hu = /\bfunction\s*\*?\s*(?:[A-Za-z_$][\w$]*)?\s*(?:<[^()]*?>)?\s*\(((?:[^()]|\([^()]*\))*)\)|\(((?:[^()]|\([^()]*\))*)\)\s*(?::[^=;{}()]*)?=>|(?<![\w$.])([A-Za-z_$][\w$]*)\s*=>/g;
|
|
3356
|
+
function gu(e, t) {
|
|
3331
3357
|
if (e[3] !== void 0) return e[3] === t;
|
|
3332
3358
|
let n = e[1] ?? e[2] ?? "", r = 0;
|
|
3333
3359
|
for (let e of n.matchAll(new RegExp(String.raw`[{}]|(?<![\w$.])${t}(?![\w$])`, "g"))) {
|
|
@@ -3344,10 +3370,10 @@ function fu(e, t) {
|
|
|
3344
3370
|
}
|
|
3345
3371
|
return !1;
|
|
3346
3372
|
}
|
|
3347
|
-
function
|
|
3373
|
+
function _u(e, t, n) {
|
|
3348
3374
|
let r = [[0, e.length]], i = [];
|
|
3349
|
-
for (let a of e.matchAll(
|
|
3350
|
-
if (!
|
|
3375
|
+
for (let a of e.matchAll(hu)) {
|
|
3376
|
+
if (!gu(a, n)) continue;
|
|
3351
3377
|
let e = a.index ?? 0, o = e + a[0].length;
|
|
3352
3378
|
for (; /\s/.test(t[o] || "");) o++;
|
|
3353
3379
|
if (!a[0].endsWith("=>") && t[o] !== "{") return r;
|
|
@@ -3380,7 +3406,7 @@ function pu(e, t, n) {
|
|
|
3380
3406
|
}
|
|
3381
3407
|
return i;
|
|
3382
3408
|
}
|
|
3383
|
-
var
|
|
3409
|
+
var vu = (e, t) => `${e}: ${G(t.map((e) => e.replace(/\($/, "()")))} ${t.length > 1 ? "open" : "opens"} the browser's own pop-up, drawn by the browser and not the app, over the owner's themed page — delete ${t.length > 1 ? "every one" : "it"}. A delete asks first in <ConfirmModal isOpen={!!toDelete} title="Delete this?" message="It cannot be undone." confirmText="Delete" onConfirm={removeIt} onCancel={() => setToDelete(null)} /> imported from 'your-ai-workflow-firebase-os' (on a from-scratch page actions.confirmDelete(id) opens it for you, and a drive's deleteFile(file) already asks); anything to tell somebody is words you styled on the page.`, yu = /<input[^>]*type\s*=\s*\{?['"]file['"]\}?/i, bu = (e) => `${e}: an <input type="file"> uploads nothing on its own — draw the app's own box instead, <UploadBox value={photo} onChange={setPhoto} label="Product photo" /> imported from 'your-ai-workflow-firebase-os', which uploads as the file is picked or dropped and hands back the link to save on the record (on a form it is the form's own \`file\` question, and on a drive page it is openUploadModal()).`, xu = (e, t) => `${e}: ${G(t)} ${t.length > 1 ? "are" : "is"} hand-rolled email code — delete it. A form sent on a page is emailed by the app itself, as the owner switched it on the page's row; to tell somebody something by email it is notify({ …, email: true }), and the app finds the address; a payment's receipt and sale alert are the app's own; and only on a page the owner's team opens, emailAnyone(to, subject, message) writes to an address — all imported from 'your-ai-workflow-firebase-os'. A page never writes an email body or names a recipient for anybody else (contract rule 10).`, Su = [
|
|
3384
3410
|
"--bg-color",
|
|
3385
3411
|
"--bg-secondary-color",
|
|
3386
3412
|
"--fg-color",
|
|
@@ -3398,10 +3424,10 @@ var mu = (e, t) => `${e}: ${G(t.map((e) => e.replace(/\($/, "()")))} ${t.length
|
|
|
3398
3424
|
"--notification-icon-color",
|
|
3399
3425
|
"--selection-bg",
|
|
3400
3426
|
"--selection-text"
|
|
3401
|
-
],
|
|
3427
|
+
], Cu = (e, t) => {
|
|
3402
3428
|
let n = [], r = t.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
3403
3429
|
for (let [, t, i] of r.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
|
3404
|
-
let r = t.trim(), a =
|
|
3430
|
+
let r = t.trim(), a = Su.filter((e) => RegExp(`${e}\\s*:[^;}]*!important`).test(i));
|
|
3405
3431
|
if (a.length) {
|
|
3406
3432
|
n.push(`${e}: \`${a[0]}\` is set with \`!important\` here, and the app writes that colour itself from the owner's theme — forcing it freezes it, so their Theme screen stops working. Delete the \`!important\` and set the colour in configs/theme.config.ts instead, in background, backgroundSecondary, foreground, accent or buttonText; every other colour is derived from those five.`);
|
|
3407
3433
|
continue;
|
|
@@ -3421,8 +3447,8 @@ var mu = (e, t) => `${e}: ${G(t.map((e) => e.replace(/\($/, "()")))} ${t.length
|
|
|
3421
3447
|
}
|
|
3422
3448
|
return n;
|
|
3423
3449
|
};
|
|
3424
|
-
function
|
|
3425
|
-
return
|
|
3450
|
+
function wu(e, t, n = [], r = {}) {
|
|
3451
|
+
return jo(e.map((e) => ({
|
|
3426
3452
|
file: e.file,
|
|
3427
3453
|
source: t(e.abs),
|
|
3428
3454
|
helper: e.kind === "helper"
|
|
@@ -3430,8 +3456,8 @@ function bu(e, t, n = [], r = {}) {
|
|
|
3430
3456
|
}
|
|
3431
3457
|
//#endregion
|
|
3432
3458
|
//#region src/vite/appIcons.ts
|
|
3433
|
-
var
|
|
3434
|
-
function
|
|
3459
|
+
var Tu = (e) => e.split("-").map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(""), Eu = /* @__PURE__ */ new Map();
|
|
3460
|
+
function Du(e) {
|
|
3435
3461
|
let t = r.resolve(e);
|
|
3436
3462
|
for (;;) {
|
|
3437
3463
|
let e = r.join(t, "node_modules", "lucide-react");
|
|
@@ -3441,22 +3467,22 @@ function Cu(e) {
|
|
|
3441
3467
|
t = i;
|
|
3442
3468
|
}
|
|
3443
3469
|
}
|
|
3444
|
-
function
|
|
3470
|
+
function Ou(e) {
|
|
3445
3471
|
try {
|
|
3446
|
-
let t =
|
|
3472
|
+
let t = Du(e);
|
|
3447
3473
|
if (!t) return null;
|
|
3448
|
-
let i = String(JSON.parse(n.readFileSync(r.join(t, "package.json"), "utf8")).version || ""), a = `${r.resolve(e)}\n${t}`, o =
|
|
3474
|
+
let i = String(JSON.parse(n.readFileSync(r.join(t, "package.json"), "utf8")).version || ""), a = `${r.resolve(e)}\n${t}`, o = Eu.get(a);
|
|
3449
3475
|
if (o && o.version === i) return o.names;
|
|
3450
3476
|
let s = r.join(t, "dist", "esm", "icons"), c = null;
|
|
3451
3477
|
if (n.existsSync(s)) {
|
|
3452
3478
|
c = /* @__PURE__ */ new Set();
|
|
3453
3479
|
for (let e of n.readdirSync(s)) {
|
|
3454
3480
|
let t = /^([a-z0-9]+(?:-[a-z0-9]+)*)\.m?js$/.exec(e);
|
|
3455
|
-
t && c.add(
|
|
3481
|
+
t && c.add(Tu(t[1]));
|
|
3456
3482
|
}
|
|
3457
3483
|
c.size || (c = null);
|
|
3458
3484
|
}
|
|
3459
|
-
return
|
|
3485
|
+
return Eu.set(a, {
|
|
3460
3486
|
version: i,
|
|
3461
3487
|
names: c
|
|
3462
3488
|
}), c;
|
|
@@ -3465,4 +3491,4 @@ function wu(e) {
|
|
|
3465
3491
|
}
|
|
3466
3492
|
}
|
|
3467
3493
|
//#endregion
|
|
3468
|
-
export { p as C,
|
|
3494
|
+
export { p as C, _e as D, ve as E, g as S, f as T, m as _, wu as a, x as b, i as c, o as d, u as f, me as g, _ as h, jo as i, c as l, ee as m, Y as n, pn as o, v as p, Cu as r, a as s, Ou as t, l as u, k as v, S as w, he as x, b as y };
|