tempest-express-sdk 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-GLZYNX63.js → chunk-ADF7OHRH.js} +3 -3
- package/dist/{chunk-GLZYNX63.js.map → chunk-ADF7OHRH.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +514 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -4
- package/dist/index.d.ts +177 -4
- package/dist/index.js +511 -16
- package/dist/index.js.map +1 -1
- package/package.json +7 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z, looseBoolean, toDict, PasswordUtils } from './chunk-
|
|
2
|
-
export { PasswordUtils, VERSION, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, centsField, corsSettingsShape, databaseSettingsShape, looseBoolean as envBoolean, hexColorField, latitudeField, loadSettings, longitudeField, looseBoolean, nonEmptyStrField, nonNegativeFloatField, nonNegativeIntField, percentField, portField, positiveFloatField, positiveIntField, priceField, ratingField, ratioField, serverSettingsShape, slugField, toDict, z } from './chunk-
|
|
1
|
+
import { z, looseBoolean, toDict, PasswordUtils } from './chunk-ADF7OHRH.js';
|
|
2
|
+
export { PasswordUtils, VERSION, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, centsField, corsSettingsShape, databaseSettingsShape, looseBoolean as envBoolean, hexColorField, latitudeField, loadSettings, longitudeField, looseBoolean, nonEmptyStrField, nonNegativeFloatField, nonNegativeIntField, percentField, portField, positiveFloatField, positiveIntField, priceField, ratingField, ratioField, serverSettingsShape, slugField, toDict, z } from './chunk-ADF7OHRH.js';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
4
|
import { Model, column, sql, BaseRepository, RecordNotFound, detectDialect, columnsOf, NodeSqliteDriver, AsyncEngine, or, and } from 'tempest-db-js';
|
|
5
5
|
export { AsyncEngine, AsyncResult, AsyncSession, BaseRepository, Column, DeleteBuilder, InsertBuilder, Model, NoResultError, NodeSqliteDriver, PostgresDialect, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncSession, UpdateBuilder, and, belongsTo, column, columnsOf, createEngine, createSyncEngine, del, detectDialect, getDialect, hasMany, insert, join, loadRelations, not, or, parseDatabaseUrl, select, sql, update } from 'tempest-db-js';
|
|
@@ -9371,6 +9371,85 @@ function adminAction(options, handler) {
|
|
|
9371
9371
|
};
|
|
9372
9372
|
}
|
|
9373
9373
|
|
|
9374
|
+
// src/admin/multipart.ts
|
|
9375
|
+
var BUSBOY_HINT = "The admin's upload and CSV-import screens need the optional peer `busboy`. Install it with: npm install busboy";
|
|
9376
|
+
async function loadBusboy() {
|
|
9377
|
+
try {
|
|
9378
|
+
const module = await import('busboy');
|
|
9379
|
+
return module.default ?? module;
|
|
9380
|
+
} catch {
|
|
9381
|
+
throw new Error(BUSBOY_HINT);
|
|
9382
|
+
}
|
|
9383
|
+
}
|
|
9384
|
+
var MultipartLimitError = class extends Error {
|
|
9385
|
+
/**
|
|
9386
|
+
* @param message - The operator-facing explanation.
|
|
9387
|
+
*/
|
|
9388
|
+
constructor(message) {
|
|
9389
|
+
super(message);
|
|
9390
|
+
this.name = "MultipartLimitError";
|
|
9391
|
+
}
|
|
9392
|
+
};
|
|
9393
|
+
async function parseMultipart(req, options = {}) {
|
|
9394
|
+
const maxFileBytes = options.maxFileBytes ?? 10 * 1024 * 1024;
|
|
9395
|
+
const maxFiles = options.maxFiles ?? 10;
|
|
9396
|
+
const busboy = await loadBusboy();
|
|
9397
|
+
return await new Promise((resolve, reject) => {
|
|
9398
|
+
const fields = {};
|
|
9399
|
+
const files = [];
|
|
9400
|
+
let settled = false;
|
|
9401
|
+
const fail = (error) => {
|
|
9402
|
+
if (settled) return;
|
|
9403
|
+
settled = true;
|
|
9404
|
+
reject(error);
|
|
9405
|
+
};
|
|
9406
|
+
const parser = busboy({
|
|
9407
|
+
headers: req.headers,
|
|
9408
|
+
limits: { fileSize: maxFileBytes, files: maxFiles }
|
|
9409
|
+
});
|
|
9410
|
+
parser.on("field", ((name, value) => {
|
|
9411
|
+
fields[name] = value;
|
|
9412
|
+
}));
|
|
9413
|
+
parser.on("file", ((name, stream, info) => {
|
|
9414
|
+
const chunks = [];
|
|
9415
|
+
stream.on("data", ((chunk) => chunks.push(chunk)));
|
|
9416
|
+
stream.on("limit", () => {
|
|
9417
|
+
fail(
|
|
9418
|
+
new MultipartLimitError(
|
|
9419
|
+
`The file is larger than the ${Math.floor(maxFileBytes / 1024 / 1024)} MB limit.`
|
|
9420
|
+
)
|
|
9421
|
+
);
|
|
9422
|
+
});
|
|
9423
|
+
stream.on("end", () => {
|
|
9424
|
+
const data = Buffer.concat(chunks);
|
|
9425
|
+
const filename = (info.filename ?? "").split(/[/\\]/).pop() ?? "";
|
|
9426
|
+
if (filename === "" || data.length === 0) return;
|
|
9427
|
+
files.push({
|
|
9428
|
+
field: name,
|
|
9429
|
+
filename,
|
|
9430
|
+
contentType: info.mimeType ?? "application/octet-stream",
|
|
9431
|
+
data
|
|
9432
|
+
});
|
|
9433
|
+
});
|
|
9434
|
+
}));
|
|
9435
|
+
parser.on("filesLimit", () => {
|
|
9436
|
+
fail(new MultipartLimitError(`At most ${maxFiles} files can be uploaded at once.`));
|
|
9437
|
+
});
|
|
9438
|
+
parser.on("error", ((error) => {
|
|
9439
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
9440
|
+
}));
|
|
9441
|
+
parser.on("close", () => {
|
|
9442
|
+
if (settled) return;
|
|
9443
|
+
settled = true;
|
|
9444
|
+
resolve({ fields, files });
|
|
9445
|
+
});
|
|
9446
|
+
req.pipe(parser);
|
|
9447
|
+
});
|
|
9448
|
+
}
|
|
9449
|
+
function isMultipart(req) {
|
|
9450
|
+
return (req.header("content-type") ?? "").toLowerCase().startsWith("multipart/form-data");
|
|
9451
|
+
}
|
|
9452
|
+
|
|
9374
9453
|
// src/admin/columns.ts
|
|
9375
9454
|
function humanizeField(name) {
|
|
9376
9455
|
return name.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().split(/\s+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
@@ -9464,6 +9543,8 @@ var NEVER_EDITABLE = [
|
|
|
9464
9543
|
"id",
|
|
9465
9544
|
"createdAt",
|
|
9466
9545
|
"updatedAt",
|
|
9546
|
+
"createdBy",
|
|
9547
|
+
"updatedBy",
|
|
9467
9548
|
"hashedPassword"
|
|
9468
9549
|
];
|
|
9469
9550
|
var NEVER_LISTED = ["hashedPassword"];
|
|
@@ -9500,6 +9581,14 @@ var AdminModel = class {
|
|
|
9500
9581
|
auditModel;
|
|
9501
9582
|
/** Saved list-view presets, in declaration order. */
|
|
9502
9583
|
lenses;
|
|
9584
|
+
/** Columns rendered as file inputs. */
|
|
9585
|
+
uploadFields;
|
|
9586
|
+
/** Backend persisting uploaded files, or `null`. */
|
|
9587
|
+
uploadStorage;
|
|
9588
|
+
/** Whether the CSV import page is exposed. */
|
|
9589
|
+
canImport;
|
|
9590
|
+
/** Foreign-key columns rendered as a typed search box. */
|
|
9591
|
+
autocompleteFields;
|
|
9503
9592
|
actions = /* @__PURE__ */ new Map();
|
|
9504
9593
|
slugOverride;
|
|
9505
9594
|
listDisplayOverride;
|
|
@@ -9527,6 +9616,15 @@ var AdminModel = class {
|
|
|
9527
9616
|
this.canDelete = options.canDelete ?? true;
|
|
9528
9617
|
this.auditModel = options.auditModel ?? null;
|
|
9529
9618
|
this.lenses = [...options.lenses ?? []];
|
|
9619
|
+
this.uploadFields = [...options.uploadFields ?? []];
|
|
9620
|
+
this.uploadStorage = options.uploadStorage ?? null;
|
|
9621
|
+
this.canImport = options.canImport ?? false;
|
|
9622
|
+
this.autocompleteFields = [...options.autocompleteFields ?? []];
|
|
9623
|
+
if (this.uploadFields.length > 0 && this.uploadStorage === null) {
|
|
9624
|
+
throw new Error(
|
|
9625
|
+
`AdminModel(${this.model.tablename}).uploadFields requires an uploadStorage (e.g. LocalUploadStorage / S3UploadStorage) \u2014 without one there is nowhere to write the file.`
|
|
9626
|
+
);
|
|
9627
|
+
}
|
|
9530
9628
|
for (const action of options.actions ?? []) {
|
|
9531
9629
|
if (this.actions.has(action.name)) {
|
|
9532
9630
|
throw new Error(
|
|
@@ -9541,7 +9639,9 @@ var AdminModel = class {
|
|
|
9541
9639
|
["listFilter", this.listFilter],
|
|
9542
9640
|
["searchFields", this.searchFields],
|
|
9543
9641
|
["readonlyFields", this.readonlyFields],
|
|
9544
|
-
["identityField", [this.identityField]]
|
|
9642
|
+
["identityField", [this.identityField]],
|
|
9643
|
+
["uploadFields", this.uploadFields],
|
|
9644
|
+
["autocompleteFields", this.autocompleteFields]
|
|
9545
9645
|
]) {
|
|
9546
9646
|
for (const name of names) {
|
|
9547
9647
|
if (!known.has(name)) {
|
|
@@ -9723,11 +9823,15 @@ function buildFormFields(admin, options = {}) {
|
|
|
9723
9823
|
const values = options.values ?? {};
|
|
9724
9824
|
const errors = options.errors ?? {};
|
|
9725
9825
|
const foreignKeys = options.foreignKeyOptions ?? {};
|
|
9826
|
+
const autocompleteUrls = options.autocompleteUrls ?? {};
|
|
9827
|
+
const autocompleteLabels = options.autocompleteLabels ?? {};
|
|
9828
|
+
const uploads = new Set(admin.uploadFields);
|
|
9726
9829
|
return admin.editableFieldNames().flatMap((name) => {
|
|
9727
9830
|
const column6 = columns[name];
|
|
9728
9831
|
if (column6 === void 0) return [];
|
|
9729
9832
|
const related = foreignKeys[name];
|
|
9730
|
-
const
|
|
9833
|
+
const autocompleteUrl = autocompleteUrls[name];
|
|
9834
|
+
const spec = uploads.has(name) ? { widget: "file", step: null, options: [] } : autocompleteUrl !== void 0 ? { widget: "autocomplete", step: null, options: [] } : related === void 0 ? widgetForColumn(column6) : { widget: "select", step: null, options: related };
|
|
9731
9835
|
const raw = name in values ? values[name] : literalDefault(column6);
|
|
9732
9836
|
return [
|
|
9733
9837
|
{
|
|
@@ -9739,7 +9843,9 @@ function buildFormFields(admin, options = {}) {
|
|
|
9739
9843
|
checked: spec.widget === "checkbox" && toBoolean(raw),
|
|
9740
9844
|
step: spec.step,
|
|
9741
9845
|
options: spec.options,
|
|
9742
|
-
error: errors[name] ?? null
|
|
9846
|
+
error: errors[name] ?? null,
|
|
9847
|
+
autocompleteUrl: autocompleteUrl ?? null,
|
|
9848
|
+
displayLabel: autocompleteLabels[name] ?? ""
|
|
9743
9849
|
}
|
|
9744
9850
|
];
|
|
9745
9851
|
});
|
|
@@ -9787,13 +9893,15 @@ function coerceValue(column6, widget, raw) {
|
|
|
9787
9893
|
return raw;
|
|
9788
9894
|
}
|
|
9789
9895
|
}
|
|
9790
|
-
function parseFormBody(admin, body) {
|
|
9896
|
+
function parseFormBody(admin, body, options = {}) {
|
|
9791
9897
|
const columns = adminColumns(admin.model);
|
|
9792
9898
|
const data = {};
|
|
9793
9899
|
const errors = {};
|
|
9900
|
+
const uploads = options.uploadsAsText === true ? /* @__PURE__ */ new Set() : new Set(admin.uploadFields);
|
|
9794
9901
|
for (const name of admin.editableFieldNames()) {
|
|
9795
9902
|
const column6 = columns[name];
|
|
9796
9903
|
if (column6 === void 0) continue;
|
|
9904
|
+
if (uploads.has(name)) continue;
|
|
9797
9905
|
const { widget } = widgetForColumn(column6);
|
|
9798
9906
|
if (widget === "checkbox") {
|
|
9799
9907
|
data[name] = toBoolean(body[name]);
|
|
@@ -12079,6 +12187,7 @@ function renderListPage(context, view) {
|
|
|
12079
12187
|
</form>
|
|
12080
12188
|
<div class="tempest-admin-list__actions">
|
|
12081
12189
|
${view.newUrl !== null ? `<a class="tempest-admin-list__new" href="${escapeHtml(view.newUrl)}">+ New</a>` : ""}
|
|
12190
|
+
${view.importUrl !== null ? `<a href="${escapeHtml(view.importUrl)}">Import CSV</a>` : ""}
|
|
12082
12191
|
<a href="${escapeHtml(view.exportCsvUrl)}">Export CSV</a>
|
|
12083
12192
|
<a href="${escapeHtml(view.exportJsonUrl)}">Export JSON</a>
|
|
12084
12193
|
</div>
|
|
@@ -12178,6 +12287,18 @@ function renderFormField(field) {
|
|
|
12178
12287
|
control = `<label><span>${label}${field.required ? " *" : ""}</span><select name="${name}"${required}>${blank}${options}</select></label>`;
|
|
12179
12288
|
break;
|
|
12180
12289
|
}
|
|
12290
|
+
case "file":
|
|
12291
|
+
control = `<label><span>${label}${field.required ? " *" : ""}</span><input type="file" name="${name}"${required}></label>${field.value === "" ? "" : `<small class="tempest-admin-form__hint">Current: ${value} \u2014 choose a file to replace it.</small>`}`;
|
|
12292
|
+
break;
|
|
12293
|
+
case "autocomplete":
|
|
12294
|
+
control = `<label><span>${label}${field.required ? " *" : ""}</span>
|
|
12295
|
+
<div class="tempest-admin-ac" data-ac data-ac-url="${escapeHtml(field.autocompleteUrl)}">
|
|
12296
|
+
<input type="text" class="tempest-admin-ac__search" value="${escapeHtml(field.displayLabel)}" placeholder="Search\u2026" autocomplete="off" data-ac-search>
|
|
12297
|
+
<input type="hidden" name="${name}" value="${value}"${required} data-ac-value>
|
|
12298
|
+
<ul class="tempest-admin-ac__results" data-ac-results hidden></ul>
|
|
12299
|
+
</div>
|
|
12300
|
+
</label>`;
|
|
12301
|
+
break;
|
|
12181
12302
|
case "number":
|
|
12182
12303
|
control = `<label><span>${label}${field.required ? " *" : ""}</span><input type="number" name="${name}" value="${value}"${field.step !== null ? ` step="${escapeHtml(field.step)}"` : ""}${required}></label>`;
|
|
12183
12304
|
break;
|
|
@@ -12205,7 +12326,7 @@ function renderFormPage(context, view) {
|
|
|
12205
12326
|
<a href="${escapeHtml(view.backUrl)}">\u2190 Back</a>
|
|
12206
12327
|
</header>
|
|
12207
12328
|
${view.error !== null ? `<p class="tempest-admin-form__error">${escapeHtml(view.error)}</p>` : ""}
|
|
12208
|
-
<form method="post" action="${escapeHtml(view.actionUrl)}" class="tempest-admin-form__form">
|
|
12329
|
+
<form method="post" action="${escapeHtml(view.actionUrl)}" class="tempest-admin-form__form"${view.fields.some((field) => field.widget === "file") ? ' enctype="multipart/form-data"' : ""}>
|
|
12209
12330
|
<input type="hidden" name="csrf_token" value="${escapeHtml(context.session.csrfToken)}">
|
|
12210
12331
|
${view.fields.map(renderFormField).join("")}
|
|
12211
12332
|
<div class="tempest-admin-form__actions">
|
|
@@ -12213,10 +12334,97 @@ function renderFormPage(context, view) {
|
|
|
12213
12334
|
<a href="${escapeHtml(view.backUrl)}" class="tempest-admin-form__cancel">Cancel</a>
|
|
12214
12335
|
</div>
|
|
12215
12336
|
</form>
|
|
12337
|
+
${view.fields.some((field) => field.widget === "autocomplete") ? AUTOCOMPLETE_SCRIPT : ""}
|
|
12216
12338
|
</section>`;
|
|
12217
12339
|
return renderLayout(context, `${heading} \xB7 ${context.site.title}`, body);
|
|
12218
12340
|
}
|
|
12341
|
+
function renderImportPage(context, view) {
|
|
12342
|
+
if (context.session === null) throw new Error("The import page requires a session");
|
|
12343
|
+
const summary = view.created === null ? "" : `<p class="tempest-admin-import__summary">Created ${escapeHtml(view.created)} record${view.created === 1 ? "" : "s"}.</p>`;
|
|
12344
|
+
const failures = view.rowErrors.length > 0 ? `<table class="tempest-admin-import__errors"><thead><tr><th>Row</th><th>Problem</th></tr></thead><tbody>${view.rowErrors.map(
|
|
12345
|
+
(failure) => `<tr><td>${escapeHtml(failure.row)}</td><td>${escapeHtml(failure.message)}</td></tr>`
|
|
12346
|
+
).join("")}</tbody></table>` : "";
|
|
12347
|
+
const body = `<section class="tempest-admin-import">
|
|
12348
|
+
<header class="tempest-admin-detail__header">
|
|
12349
|
+
<h1>Import ${escapeHtml(view.title)}</h1>
|
|
12350
|
+
<a href="${escapeHtml(view.backUrl)}">\u2190 Back</a>
|
|
12351
|
+
</header>
|
|
12352
|
+
${view.error !== null ? `<p class="tempest-admin-form__error">${escapeHtml(view.error)}</p>` : ""}
|
|
12353
|
+
${summary}
|
|
12354
|
+
${failures}
|
|
12355
|
+
<form method="post" action="${escapeHtml(view.actionUrl)}" class="tempest-admin-form__form" enctype="multipart/form-data">
|
|
12356
|
+
<input type="hidden" name="csrf_token" value="${escapeHtml(context.session.csrfToken)}">
|
|
12357
|
+
<div class="tempest-admin-form__field">
|
|
12358
|
+
<label>
|
|
12359
|
+
<span>CSV file *</span>
|
|
12360
|
+
<input type="file" name="file" accept=".csv,text/csv" required>
|
|
12361
|
+
</label>
|
|
12362
|
+
<small class="tempest-admin-form__hint">
|
|
12363
|
+
UTF-8, comma-separated, with a header row. Recognised columns:
|
|
12364
|
+
<code>${escapeHtml(view.columns.join(", "))}</code>. Unknown columns are ignored.
|
|
12365
|
+
</small>
|
|
12366
|
+
</div>
|
|
12367
|
+
<div class="tempest-admin-form__actions">
|
|
12368
|
+
<button type="submit">Import</button>
|
|
12369
|
+
<a href="${escapeHtml(view.backUrl)}" class="tempest-admin-form__cancel">Cancel</a>
|
|
12370
|
+
</div>
|
|
12371
|
+
</form>
|
|
12372
|
+
</section>`;
|
|
12373
|
+
return renderLayout(context, `Import ${view.title} \xB7 ${context.site.title}`, body);
|
|
12374
|
+
}
|
|
12375
|
+
var AUTOCOMPLETE_SCRIPT = `<script>
|
|
12376
|
+
(function () {
|
|
12377
|
+
document.querySelectorAll("[data-ac]").forEach(function (box) {
|
|
12378
|
+
var search = box.querySelector("[data-ac-search]");
|
|
12379
|
+
var value = box.querySelector("[data-ac-value]");
|
|
12380
|
+
var results = box.querySelector("[data-ac-results]");
|
|
12381
|
+
var url = box.getAttribute("data-ac-url");
|
|
12382
|
+
var timer = null;
|
|
12383
|
+
|
|
12384
|
+
function close() {
|
|
12385
|
+
results.hidden = true;
|
|
12386
|
+
results.innerHTML = "";
|
|
12387
|
+
}
|
|
12388
|
+
|
|
12389
|
+
function pick(option) {
|
|
12390
|
+
value.value = option.value;
|
|
12391
|
+
search.value = option.label;
|
|
12392
|
+
close();
|
|
12393
|
+
}
|
|
12394
|
+
|
|
12395
|
+
function run() {
|
|
12396
|
+
var term = search.value.trim();
|
|
12397
|
+
fetch(url + "?q=" + encodeURIComponent(term), { credentials: "same-origin" })
|
|
12398
|
+
.then(function (response) { return response.ok ? response.json() : { options: [] }; })
|
|
12399
|
+
.then(function (payload) {
|
|
12400
|
+
results.innerHTML = "";
|
|
12401
|
+
(payload.options || []).forEach(function (option) {
|
|
12402
|
+
var item = document.createElement("li");
|
|
12403
|
+
item.textContent = option.label;
|
|
12404
|
+
item.setAttribute("role", "option");
|
|
12405
|
+
item.addEventListener("mousedown", function (event) {
|
|
12406
|
+
event.preventDefault();
|
|
12407
|
+
pick(option);
|
|
12408
|
+
});
|
|
12409
|
+
results.appendChild(item);
|
|
12410
|
+
});
|
|
12411
|
+
results.hidden = results.children.length === 0;
|
|
12412
|
+
})
|
|
12413
|
+
.catch(close);
|
|
12414
|
+
}
|
|
12415
|
+
|
|
12416
|
+
search.addEventListener("input", function () {
|
|
12417
|
+
value.value = "";
|
|
12418
|
+
window.clearTimeout(timer);
|
|
12419
|
+
timer = window.setTimeout(run, 250);
|
|
12420
|
+
});
|
|
12421
|
+
search.addEventListener("focus", run);
|
|
12422
|
+
search.addEventListener("blur", function () { window.setTimeout(close, 150); });
|
|
12423
|
+
});
|
|
12424
|
+
})();
|
|
12425
|
+
</script>`;
|
|
12219
12426
|
var logger2 = new JSONLogger("tempest_express_sdk.admin.router");
|
|
12427
|
+
var AUTOCOMPLETE_LIMIT = 20;
|
|
12220
12428
|
var AUDIT_HISTORY_LIMIT = 50;
|
|
12221
12429
|
var FK_OPTION_CAP = 1e3;
|
|
12222
12430
|
var FLASH_MAX_LENGTH = 300;
|
|
@@ -12246,6 +12454,7 @@ function makeAdminRouter(site, options) {
|
|
|
12246
12454
|
const theme = resolveAdminTheme(site.theme);
|
|
12247
12455
|
const showMetrics = options.showMetrics ?? true;
|
|
12248
12456
|
const exportMaxRows = options.exportMaxRows ?? 5e3;
|
|
12457
|
+
const maxUploadBytes = options.maxUploadBytes ?? 10 * 1024 * 1024;
|
|
12249
12458
|
const sessions = new AdminSessionStore({
|
|
12250
12459
|
secret: options.secretKey,
|
|
12251
12460
|
...options.cookieName === void 0 ? {} : { cookieName: options.cookieName },
|
|
@@ -12503,7 +12712,8 @@ function makeAdminRouter(site, options) {
|
|
|
12503
12712
|
mode: "create",
|
|
12504
12713
|
title: admin.verboseName(),
|
|
12505
12714
|
fields: buildFormFields(admin, {
|
|
12506
|
-
foreignKeyOptions: await foreignKeyOptionsFor(admin, state.dbSession)
|
|
12715
|
+
foreignKeyOptions: await foreignKeyOptionsFor(admin, state.dbSession),
|
|
12716
|
+
autocompleteUrls: autocompleteUrlsFor(admin)
|
|
12507
12717
|
}),
|
|
12508
12718
|
actionUrl: `${prefix}/m/${admin.slug()}/new`,
|
|
12509
12719
|
backUrl: `${prefix}/m/${admin.slug()}`,
|
|
@@ -12523,10 +12733,28 @@ function makeAdminRouter(site, options) {
|
|
|
12523
12733
|
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
12524
12734
|
return;
|
|
12525
12735
|
}
|
|
12526
|
-
|
|
12527
|
-
|
|
12736
|
+
let uploadError = null;
|
|
12737
|
+
let submission;
|
|
12738
|
+
try {
|
|
12739
|
+
submission = await readSubmission(req);
|
|
12740
|
+
} catch (error) {
|
|
12741
|
+
if (!(error instanceof MultipartLimitError)) throw error;
|
|
12742
|
+
submission = { fields: {}, files: [] };
|
|
12743
|
+
uploadError = error.message;
|
|
12744
|
+
}
|
|
12745
|
+
const body = submission.fields;
|
|
12746
|
+
if (uploadError === null && !csrfTokenMatches(state.session, body.csrf_token)) {
|
|
12747
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 403);
|
|
12748
|
+
return;
|
|
12749
|
+
}
|
|
12528
12750
|
const parsed = parseFormBody(admin, body);
|
|
12751
|
+
await applyUploads(admin, parsed.data, parsed.errors, submission.files, true);
|
|
12529
12752
|
const foreignKeyOptions = await foreignKeyOptionsFor(admin, state.dbSession);
|
|
12753
|
+
const autocompleteLabels = await autocompleteLabelsFor(
|
|
12754
|
+
admin,
|
|
12755
|
+
body,
|
|
12756
|
+
state.dbSession
|
|
12757
|
+
);
|
|
12530
12758
|
const rerender = (error, status) => {
|
|
12531
12759
|
html(
|
|
12532
12760
|
res,
|
|
@@ -12536,7 +12764,9 @@ function makeAdminRouter(site, options) {
|
|
|
12536
12764
|
fields: buildFormFields(admin, {
|
|
12537
12765
|
values: body,
|
|
12538
12766
|
errors: parsed.errors,
|
|
12539
|
-
foreignKeyOptions
|
|
12767
|
+
foreignKeyOptions,
|
|
12768
|
+
autocompleteUrls: autocompleteUrlsFor(admin),
|
|
12769
|
+
autocompleteLabels
|
|
12540
12770
|
}),
|
|
12541
12771
|
actionUrl: `${prefix}/m/${admin.slug()}/new`,
|
|
12542
12772
|
backUrl: `${prefix}/m/${admin.slug()}`,
|
|
@@ -12545,6 +12775,10 @@ function makeAdminRouter(site, options) {
|
|
|
12545
12775
|
status
|
|
12546
12776
|
);
|
|
12547
12777
|
};
|
|
12778
|
+
if (uploadError !== null) {
|
|
12779
|
+
rerender(uploadError, 400);
|
|
12780
|
+
return;
|
|
12781
|
+
}
|
|
12548
12782
|
if (Object.keys(parsed.errors).length > 0) {
|
|
12549
12783
|
rerender("Please fix the highlighted fields.", 400);
|
|
12550
12784
|
return;
|
|
@@ -12664,6 +12898,129 @@ function makeAdminRouter(site, options) {
|
|
|
12664
12898
|
);
|
|
12665
12899
|
})
|
|
12666
12900
|
);
|
|
12901
|
+
router.get(
|
|
12902
|
+
`${prefix}/m/:slug/import`,
|
|
12903
|
+
guarded(async (req, res) => {
|
|
12904
|
+
const state = await authenticate(req, res);
|
|
12905
|
+
if (state === null) return;
|
|
12906
|
+
const admin = await resolveAdmin(req, res, state, AdminPermission.CREATE);
|
|
12907
|
+
if (admin === null) return;
|
|
12908
|
+
if (!admin.canImport) {
|
|
12909
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
12910
|
+
return;
|
|
12911
|
+
}
|
|
12912
|
+
html(res, renderImport(req, state, admin, null, null, []));
|
|
12913
|
+
})
|
|
12914
|
+
);
|
|
12915
|
+
router.post(
|
|
12916
|
+
`${prefix}/m/:slug/import`,
|
|
12917
|
+
guarded(async (req, res) => {
|
|
12918
|
+
const state = await authenticate(req, res);
|
|
12919
|
+
if (state === null) return;
|
|
12920
|
+
const admin = await resolveAdmin(req, res, state, AdminPermission.CREATE);
|
|
12921
|
+
if (admin === null) return;
|
|
12922
|
+
if (!admin.canImport) {
|
|
12923
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
12924
|
+
return;
|
|
12925
|
+
}
|
|
12926
|
+
let submission;
|
|
12927
|
+
try {
|
|
12928
|
+
submission = await readSubmission(req);
|
|
12929
|
+
} catch (error) {
|
|
12930
|
+
if (!(error instanceof MultipartLimitError)) throw error;
|
|
12931
|
+
html(res, renderImport(req, state, admin, error.message, null, []), 400);
|
|
12932
|
+
return;
|
|
12933
|
+
}
|
|
12934
|
+
if (!csrfTokenMatches(state.session, submission.fields.csrf_token)) {
|
|
12935
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 403);
|
|
12936
|
+
return;
|
|
12937
|
+
}
|
|
12938
|
+
const file = submission.files[0];
|
|
12939
|
+
if (file === void 0) {
|
|
12940
|
+
html(
|
|
12941
|
+
res,
|
|
12942
|
+
renderImport(req, state, admin, "Choose a CSV file to import.", null, []),
|
|
12943
|
+
400
|
|
12944
|
+
);
|
|
12945
|
+
return;
|
|
12946
|
+
}
|
|
12947
|
+
let rows;
|
|
12948
|
+
try {
|
|
12949
|
+
rows = parseCsv(file.data.toString("utf8"));
|
|
12950
|
+
} catch (error) {
|
|
12951
|
+
const message = error instanceof Error ? error.message : "Could not read the file as UTF-8 CSV.";
|
|
12952
|
+
html(res, renderImport(req, state, admin, message, null, []), 400);
|
|
12953
|
+
return;
|
|
12954
|
+
}
|
|
12955
|
+
const repository = admin.repository(state.dbSession);
|
|
12956
|
+
const actorId = backend.principalId(state.principal);
|
|
12957
|
+
const rowErrors = [];
|
|
12958
|
+
let created = 0;
|
|
12959
|
+
for (const [index, row] of rows.entries()) {
|
|
12960
|
+
const parsed = parseFormBody(admin, row, { uploadsAsText: true });
|
|
12961
|
+
const failures = Object.entries(parsed.errors);
|
|
12962
|
+
if (failures.length > 0) {
|
|
12963
|
+
rowErrors.push({
|
|
12964
|
+
row: index + 2,
|
|
12965
|
+
message: failures.map(([field, error]) => `${field}: ${error}`).join("; ")
|
|
12966
|
+
});
|
|
12967
|
+
continue;
|
|
12968
|
+
}
|
|
12969
|
+
stampActor(admin, parsed.data, actorId, true);
|
|
12970
|
+
try {
|
|
12971
|
+
await repository.create(parsed.data);
|
|
12972
|
+
created += 1;
|
|
12973
|
+
} catch (error) {
|
|
12974
|
+
rowErrors.push({
|
|
12975
|
+
row: index + 2,
|
|
12976
|
+
message: error instanceof Error ? error.message : String(error)
|
|
12977
|
+
});
|
|
12978
|
+
}
|
|
12979
|
+
}
|
|
12980
|
+
html(res, renderImport(req, state, admin, null, created, rowErrors));
|
|
12981
|
+
})
|
|
12982
|
+
);
|
|
12983
|
+
router.get(
|
|
12984
|
+
`${prefix}/m/:slug/autocomplete/:field`,
|
|
12985
|
+
guarded(async (req, res) => {
|
|
12986
|
+
if (sessions.load(req) === null) {
|
|
12987
|
+
res.status(401).json({ options: [] });
|
|
12988
|
+
return;
|
|
12989
|
+
}
|
|
12990
|
+
const state = await authenticate(req, res);
|
|
12991
|
+
if (state === null) return;
|
|
12992
|
+
const admin = site.get(String(req.params.slug));
|
|
12993
|
+
const field = String(req.params.field);
|
|
12994
|
+
if (admin === null || !admin.autocompleteFields.includes(field) || !await allows(state.principal, admin, AdminPermission.VIEW)) {
|
|
12995
|
+
res.status(404).json({ options: [] });
|
|
12996
|
+
return;
|
|
12997
|
+
}
|
|
12998
|
+
const column6 = adminColumns(admin.model)[field];
|
|
12999
|
+
const table = column6 === void 0 ? null : foreignKeyTable(column6);
|
|
13000
|
+
const referenced = table === null ? null : site.get(table);
|
|
13001
|
+
if (referenced === null) {
|
|
13002
|
+
res.json({ options: [] });
|
|
13003
|
+
return;
|
|
13004
|
+
}
|
|
13005
|
+
const term = queryString(req.query.q);
|
|
13006
|
+
const searchable = referenced.searchFields.filter((name) => {
|
|
13007
|
+
const target = adminColumns(referenced.model)[name];
|
|
13008
|
+
return target !== void 0 && isSearchableColumn(target);
|
|
13009
|
+
});
|
|
13010
|
+
const filters = term === "" || searchable.length === 0 ? void 0 : or(...searchable.map((name) => ({ [name]: { ilike: `%${term}%` } })));
|
|
13011
|
+
const page2 = await referenced.repository(state.dbSession).paginate({
|
|
13012
|
+
page: 1,
|
|
13013
|
+
pageSize: AUTOCOMPLETE_LIMIT,
|
|
13014
|
+
...filters === void 0 ? {} : { filters }
|
|
13015
|
+
});
|
|
13016
|
+
res.json({
|
|
13017
|
+
options: page2.items.map((row) => ({
|
|
13018
|
+
value: String(row[referenced.identityField]),
|
|
13019
|
+
label: foreignKeyLabel(referenced, row)
|
|
13020
|
+
}))
|
|
13021
|
+
});
|
|
13022
|
+
})
|
|
13023
|
+
);
|
|
12667
13024
|
router.get(
|
|
12668
13025
|
`${prefix}/m/:slug/:identity`,
|
|
12669
13026
|
guarded(async (req, res) => {
|
|
@@ -12711,7 +13068,9 @@ function makeAdminRouter(site, options) {
|
|
|
12711
13068
|
title: admin.verboseName(),
|
|
12712
13069
|
fields: buildFormFields(admin, {
|
|
12713
13070
|
values: row,
|
|
12714
|
-
foreignKeyOptions: await foreignKeyOptionsFor(admin, state.dbSession)
|
|
13071
|
+
foreignKeyOptions: await foreignKeyOptionsFor(admin, state.dbSession),
|
|
13072
|
+
autocompleteUrls: autocompleteUrlsFor(admin),
|
|
13073
|
+
autocompleteLabels: await autocompleteLabelsFor(admin, row, state.dbSession)
|
|
12715
13074
|
}),
|
|
12716
13075
|
actionUrl: `${prefix}/m/${admin.slug()}/${identity}/edit`,
|
|
12717
13076
|
backUrl: `${prefix}/m/${admin.slug()}/${identity}`,
|
|
@@ -12732,10 +13091,28 @@ function makeAdminRouter(site, options) {
|
|
|
12732
13091
|
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
12733
13092
|
return;
|
|
12734
13093
|
}
|
|
12735
|
-
|
|
12736
|
-
|
|
13094
|
+
let uploadError = null;
|
|
13095
|
+
let submission;
|
|
13096
|
+
try {
|
|
13097
|
+
submission = await readSubmission(req);
|
|
13098
|
+
} catch (error) {
|
|
13099
|
+
if (!(error instanceof MultipartLimitError)) throw error;
|
|
13100
|
+
submission = { fields: {}, files: [] };
|
|
13101
|
+
uploadError = error.message;
|
|
13102
|
+
}
|
|
13103
|
+
const body = submission.fields;
|
|
13104
|
+
if (uploadError === null && !csrfTokenMatches(state.session, body.csrf_token)) {
|
|
13105
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 403);
|
|
13106
|
+
return;
|
|
13107
|
+
}
|
|
12737
13108
|
const parsed = parseFormBody(admin, body);
|
|
13109
|
+
await applyUploads(admin, parsed.data, parsed.errors, submission.files, false);
|
|
12738
13110
|
const foreignKeyOptions = await foreignKeyOptionsFor(admin, state.dbSession);
|
|
13111
|
+
const autocompleteLabels = await autocompleteLabelsFor(
|
|
13112
|
+
admin,
|
|
13113
|
+
body,
|
|
13114
|
+
state.dbSession
|
|
13115
|
+
);
|
|
12739
13116
|
const rerender = (error, status) => {
|
|
12740
13117
|
html(
|
|
12741
13118
|
res,
|
|
@@ -12745,7 +13122,9 @@ function makeAdminRouter(site, options) {
|
|
|
12745
13122
|
fields: buildFormFields(admin, {
|
|
12746
13123
|
values: body,
|
|
12747
13124
|
errors: parsed.errors,
|
|
12748
|
-
foreignKeyOptions
|
|
13125
|
+
foreignKeyOptions,
|
|
13126
|
+
autocompleteUrls: autocompleteUrlsFor(admin),
|
|
13127
|
+
autocompleteLabels
|
|
12749
13128
|
}),
|
|
12750
13129
|
actionUrl: `${prefix}/m/${admin.slug()}/${identity}/edit`,
|
|
12751
13130
|
backUrl: `${prefix}/m/${admin.slug()}/${identity}`,
|
|
@@ -12754,6 +13133,10 @@ function makeAdminRouter(site, options) {
|
|
|
12754
13133
|
status
|
|
12755
13134
|
);
|
|
12756
13135
|
};
|
|
13136
|
+
if (uploadError !== null) {
|
|
13137
|
+
rerender(uploadError, 400);
|
|
13138
|
+
return;
|
|
13139
|
+
}
|
|
12757
13140
|
if (Object.keys(parsed.errors).length > 0) {
|
|
12758
13141
|
rerender("Please fix the highlighted fields.", 400);
|
|
12759
13142
|
return;
|
|
@@ -12837,6 +13220,44 @@ function makeAdminRouter(site, options) {
|
|
|
12837
13220
|
const row = await admin.repository(dbSession).first({ [admin.identityField]: identity });
|
|
12838
13221
|
return row ?? null;
|
|
12839
13222
|
}
|
|
13223
|
+
function renderImport(req, state, admin, error, created, rowErrors) {
|
|
13224
|
+
return renderImportPage(context(req, state.session, state.visible), {
|
|
13225
|
+
title: admin.verboseNamePlural(),
|
|
13226
|
+
actionUrl: `${prefix}/m/${admin.slug()}/import`,
|
|
13227
|
+
backUrl: `${prefix}/m/${admin.slug()}`,
|
|
13228
|
+
columns: admin.editableFieldNames(),
|
|
13229
|
+
error,
|
|
13230
|
+
created,
|
|
13231
|
+
rowErrors
|
|
13232
|
+
});
|
|
13233
|
+
}
|
|
13234
|
+
async function readSubmission(req) {
|
|
13235
|
+
if (!isMultipart(req)) {
|
|
13236
|
+
return { fields: req.body ?? {}, files: [] };
|
|
13237
|
+
}
|
|
13238
|
+
const parsed = await parseMultipart(req, { maxFileBytes: maxUploadBytes });
|
|
13239
|
+
return { fields: parsed.fields, files: parsed.files };
|
|
13240
|
+
}
|
|
13241
|
+
async function applyUploads(admin, data, errors, files, creating) {
|
|
13242
|
+
if (admin.uploadFields.length === 0) return;
|
|
13243
|
+
const storage2 = admin.uploadStorage;
|
|
13244
|
+
if (storage2 === null) return;
|
|
13245
|
+
const columns = adminColumns(admin.model);
|
|
13246
|
+
for (const field of admin.uploadFields) {
|
|
13247
|
+
const file = files.find((candidate) => candidate.field === field);
|
|
13248
|
+
if (file === void 0) {
|
|
13249
|
+
const column6 = columns[field];
|
|
13250
|
+
if (creating && column6 !== void 0 && !isColumnOptional(column6)) {
|
|
13251
|
+
errors[field] = "This field is required.";
|
|
13252
|
+
}
|
|
13253
|
+
continue;
|
|
13254
|
+
}
|
|
13255
|
+
const extension = file.filename.includes(".") ? `.${file.filename.split(".").pop()}` : "";
|
|
13256
|
+
const key = `${admin.slug()}/${field}/${randomUUID()}${extension}`;
|
|
13257
|
+
const saved = await storage2.save(key, file.data, { contentType: file.contentType });
|
|
13258
|
+
data[field] = saved.key;
|
|
13259
|
+
}
|
|
13260
|
+
}
|
|
12840
13261
|
async function permittedBulkActions(admin, principal) {
|
|
12841
13262
|
const permitted = [];
|
|
12842
13263
|
for (const option of bulkActionsFor(admin)) {
|
|
@@ -12900,6 +13321,7 @@ function makeAdminRouter(site, options) {
|
|
|
12900
13321
|
filters: query.filterViews,
|
|
12901
13322
|
sort,
|
|
12902
13323
|
newUrl: await allows(state.principal, admin, AdminPermission.CREATE) ? `${prefix}/m/${admin.slug()}/new` : null,
|
|
13324
|
+
importUrl: admin.canImport && await allows(state.principal, admin, AdminPermission.CREATE) ? `${prefix}/m/${admin.slug()}/import` : null,
|
|
12903
13325
|
bulkActions: await permittedBulkActions(admin, state.principal),
|
|
12904
13326
|
bulkUrl: `${prefix}/m/${admin.slug()}/bulk`,
|
|
12905
13327
|
exportCsvUrl: exportUrl("csv"),
|
|
@@ -13024,6 +13446,7 @@ function makeAdminRouter(site, options) {
|
|
|
13024
13446
|
const columns = adminColumns(admin.model);
|
|
13025
13447
|
const options2 = {};
|
|
13026
13448
|
for (const field of Object.keys(foreignKeyFields(admin))) {
|
|
13449
|
+
if (admin.autocompleteFields.includes(field)) continue;
|
|
13027
13450
|
const column6 = columns[field];
|
|
13028
13451
|
if (column6 === void 0) continue;
|
|
13029
13452
|
const related = await relatedOptions(column6, dbSession);
|
|
@@ -13031,6 +13454,29 @@ function makeAdminRouter(site, options) {
|
|
|
13031
13454
|
}
|
|
13032
13455
|
return options2;
|
|
13033
13456
|
}
|
|
13457
|
+
function autocompleteUrlsFor(admin) {
|
|
13458
|
+
const urls = {};
|
|
13459
|
+
for (const field of admin.autocompleteFields) {
|
|
13460
|
+
urls[field] = `${prefix}/m/${admin.slug()}/autocomplete/${field}`;
|
|
13461
|
+
}
|
|
13462
|
+
return urls;
|
|
13463
|
+
}
|
|
13464
|
+
async function autocompleteLabelsFor(admin, row, dbSession) {
|
|
13465
|
+
const labels = {};
|
|
13466
|
+
if (row === null) return labels;
|
|
13467
|
+
const columns = adminColumns(admin.model);
|
|
13468
|
+
for (const field of admin.autocompleteFields) {
|
|
13469
|
+
const value = row[field];
|
|
13470
|
+
if (value === null || value === void 0 || value === "") continue;
|
|
13471
|
+
const column6 = columns[field];
|
|
13472
|
+
const table = column6 === void 0 ? null : foreignKeyTable(column6);
|
|
13473
|
+
const referenced = table === null ? null : site.get(table);
|
|
13474
|
+
if (referenced === null) continue;
|
|
13475
|
+
const related = await referenced.repository(dbSession).first({ [referenced.identityField]: value });
|
|
13476
|
+
if (related !== null) labels[field] = foreignKeyLabel(referenced, related);
|
|
13477
|
+
}
|
|
13478
|
+
return labels;
|
|
13479
|
+
}
|
|
13034
13480
|
return router;
|
|
13035
13481
|
}
|
|
13036
13482
|
function flagAllows(admin, action) {
|
|
@@ -13122,6 +13568,55 @@ function bulkActionsFor(admin) {
|
|
|
13122
13568
|
}
|
|
13123
13569
|
return actions;
|
|
13124
13570
|
}
|
|
13571
|
+
function parseCsv(text) {
|
|
13572
|
+
const source = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
13573
|
+
const rows = [];
|
|
13574
|
+
let row = [];
|
|
13575
|
+
let field = "";
|
|
13576
|
+
let quoted = false;
|
|
13577
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
13578
|
+
const char = source[index];
|
|
13579
|
+
if (quoted) {
|
|
13580
|
+
if (char === '"') {
|
|
13581
|
+
if (source[index + 1] === '"') {
|
|
13582
|
+
field += '"';
|
|
13583
|
+
index += 1;
|
|
13584
|
+
} else {
|
|
13585
|
+
quoted = false;
|
|
13586
|
+
}
|
|
13587
|
+
} else {
|
|
13588
|
+
field += char;
|
|
13589
|
+
}
|
|
13590
|
+
continue;
|
|
13591
|
+
}
|
|
13592
|
+
if (char === '"') {
|
|
13593
|
+
quoted = true;
|
|
13594
|
+
} else if (char === ",") {
|
|
13595
|
+
row.push(field);
|
|
13596
|
+
field = "";
|
|
13597
|
+
} else if (char === "\n" || char === "\r") {
|
|
13598
|
+
if (char === "\r" && source[index + 1] === "\n") index += 1;
|
|
13599
|
+
row.push(field);
|
|
13600
|
+
rows.push(row);
|
|
13601
|
+
row = [];
|
|
13602
|
+
field = "";
|
|
13603
|
+
} else {
|
|
13604
|
+
field += char;
|
|
13605
|
+
}
|
|
13606
|
+
}
|
|
13607
|
+
if (field !== "" || row.length > 0) {
|
|
13608
|
+
row.push(field);
|
|
13609
|
+
rows.push(row);
|
|
13610
|
+
}
|
|
13611
|
+
const header = rows.shift();
|
|
13612
|
+
if (header === void 0 || header.length === 0) {
|
|
13613
|
+
throw new Error("The file has no header row.");
|
|
13614
|
+
}
|
|
13615
|
+
const keys = header.map((name) => name.trim());
|
|
13616
|
+
return rows.filter((entry) => entry.some((value) => value.trim() !== "")).map(
|
|
13617
|
+
(entry) => Object.fromEntries(keys.map((key, position) => [key, entry[position] ?? ""]))
|
|
13618
|
+
);
|
|
13619
|
+
}
|
|
13125
13620
|
function exportValue(value) {
|
|
13126
13621
|
if (value instanceof Date) return value.toISOString();
|
|
13127
13622
|
if (typeof value === "bigint") return value.toString();
|
|
@@ -15154,6 +15649,6 @@ async function withTestDatabase(models, fn) {
|
|
|
15154
15649
|
}
|
|
15155
15650
|
}
|
|
15156
15651
|
|
|
15157
|
-
export { ADMIN_CSS, ActivationService, AdminJsonSite, AdminModel, AdminPermission, AdminSessionStore, AdminSite, AppException, AttemptThrottle, AuditAction, BaseAuditLogModel, BaseController, BaseModel, BaseOAuthClient, BaseOutboxModel, BaseService, BaseUserModel, BaseUserRefreshTokenModel, BaseUserTokenModel, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_DOCS_FAVICON, DEFAULT_LOCALE, EmailProvider, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, GitHubOAuthClient, GoogleOAuthClient, GracefulShutdown, HTTPClient, HTTP_500_LOG_FILE, HTTP_500_MARKER, HttpMetrics, IDEMPOTENCY_HEADER, InvalidTokenException, JSONLogger, JWTUtils, LEVEL_LOG_FILES, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemoryIdempotencyStore, MemoryRateLimitStore, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MessagingHub, MetricsUtils, MfaService, NotFoundException, OAuthError, OIDCProvider, OutboxRelay, OutboxStatus, PHONE_BR_PATTERN, PasswordResetService, REDOC_CDN_URL, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, RedisIdempotencyStore, RedisRateLimitStore, RedisSSEBroker, RedisSessionStore, Region, RetryPolicy, S3UploadStorage, SSEBroker, ServerSentEvent, SessionService, TOTPHelper, TaskManager, TelegramProvider, TenantScopedRepository, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, UserModelAuthBackend, UserTokenPurpose, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WebhookSignatureVerifier, WhatsAppProvider, activationSchema, addLogSink, adminAction, adminColumns, adminLens, adminThemeCss, attachWebSocketHub, authResponseSchema, authSettingsShape, backupDatabase, bearerToken, bodySizeLimitMiddleware, broadcastText, buildContentDisposition, buildFormFields, buildPaginationLinkHeader, cached2 as cached, cepField, citiesByUf, cnpjField, coerceFlag, configureFileLogging, configureLogging, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createTestDatabase, createdByColumn, csrfMiddleware, csrfTokenMatches, cursorPaginationFilterSchema, cursorPaginationSchema, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, diffSnapshots, emailSettingsShape, encodeCursor, envList, escapeHtml, filterForColumn, foreignKeyFields, foreignKeyLabel, foreignKeyTable, formatCellValue, formatFieldValue, generateCsrfToken, generateOAuthState, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, hashOpaqueToken, humanizeField, idempotencyMiddleware, inboundMessageSchema, isColumnOptional, isSearchableColumn, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, jwtSettingsShape, keyByHeader, keyByIp, keyByJwtClaim, keyByJwtSubject, listStates, logEntrySchema, logSettingsShape, loginSchema, makeAdminJsonRouter, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeLogsRouter, makeMetricsRouter, makeSessionMiddleware, makeToolSpecRouter, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, metricCard, mfaChallengeSchema, mfaCodeSchema, mfaEnrollResponseSchema, minioSettingsShape, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, parseFormBody, partitionTotal, passwordResetConfirmSchema, passwordResetRequestSchema, phoneBrField, prometheusMiddleware, rabbitmqSettingsShape, rateLimitMiddleware, redisSettingsShape, refreshSchema, registerExceptionHandlers, renderAuthResultPage, renderDashboardPage, renderDetailPage, renderFormPage, renderLayout, renderListPage, renderLoginPage, renderMfaPage, renderPasswordResetFormPage, requestIdMiddleware, requestTracingMiddleware, requireRoles, resolveAdminTheme, resolveDownloadPath, resolveRedocBundle, runServer, runWithRequestContext, sendBytesDownload, sendFileDownload, sessionCookie, sessionSettingsShape, setRequestId, signupSchema, snapshot, sseResponse, statesByRegion, syncFilterSchema, syncPaginationSchema, tableNameFor, toUtc, tokenFromUrl, tokenPairSchema, tokenSettingsShape, trendDirection, trendPercent, ufField, updatedByColumn, uploadSettingsShape, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSettingsShape, webPushSubscriptionSchema, webSocketSettingsShape, widgetForColumn, withTestDatabase, wrapWithSlowQueryLog, wsEnvelopeSchema };
|
|
15652
|
+
export { ADMIN_CSS, ActivationService, AdminJsonSite, AdminModel, AdminPermission, AdminSessionStore, AdminSite, AppException, AttemptThrottle, AuditAction, BaseAuditLogModel, BaseController, BaseModel, BaseOAuthClient, BaseOutboxModel, BaseService, BaseUserModel, BaseUserRefreshTokenModel, BaseUserTokenModel, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_DOCS_FAVICON, DEFAULT_LOCALE, EmailProvider, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, GitHubOAuthClient, GoogleOAuthClient, GracefulShutdown, HTTPClient, HTTP_500_LOG_FILE, HTTP_500_MARKER, HttpMetrics, IDEMPOTENCY_HEADER, InvalidTokenException, JSONLogger, JWTUtils, LEVEL_LOG_FILES, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemoryIdempotencyStore, MemoryRateLimitStore, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MessagingHub, MetricsUtils, MfaService, MultipartLimitError, NotFoundException, OAuthError, OIDCProvider, OutboxRelay, OutboxStatus, PHONE_BR_PATTERN, PasswordResetService, REDOC_CDN_URL, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, RedisIdempotencyStore, RedisRateLimitStore, RedisSSEBroker, RedisSessionStore, Region, RetryPolicy, S3UploadStorage, SSEBroker, ServerSentEvent, SessionService, TOTPHelper, TaskManager, TelegramProvider, TenantScopedRepository, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, UserModelAuthBackend, UserTokenPurpose, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WebhookSignatureVerifier, WhatsAppProvider, activationSchema, addLogSink, adminAction, adminColumns, adminLens, adminThemeCss, attachWebSocketHub, authResponseSchema, authSettingsShape, backupDatabase, bearerToken, bodySizeLimitMiddleware, broadcastText, buildContentDisposition, buildFormFields, buildPaginationLinkHeader, cached2 as cached, cepField, citiesByUf, cnpjField, coerceFlag, configureFileLogging, configureLogging, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createTestDatabase, createdByColumn, csrfMiddleware, csrfTokenMatches, cursorPaginationFilterSchema, cursorPaginationSchema, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, diffSnapshots, emailSettingsShape, encodeCursor, envList, escapeHtml, filterForColumn, foreignKeyFields, foreignKeyLabel, foreignKeyTable, formatCellValue, formatFieldValue, generateCsrfToken, generateOAuthState, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, hashOpaqueToken, humanizeField, idempotencyMiddleware, inboundMessageSchema, isColumnOptional, isMultipart, isSearchableColumn, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, jwtSettingsShape, keyByHeader, keyByIp, keyByJwtClaim, keyByJwtSubject, listStates, logEntrySchema, logSettingsShape, loginSchema, makeAdminJsonRouter, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeLogsRouter, makeMetricsRouter, makeSessionMiddleware, makeToolSpecRouter, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, metricCard, mfaChallengeSchema, mfaCodeSchema, mfaEnrollResponseSchema, minioSettingsShape, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, parseCsv, parseFormBody, parseMultipart, partitionTotal, passwordResetConfirmSchema, passwordResetRequestSchema, phoneBrField, prometheusMiddleware, rabbitmqSettingsShape, rateLimitMiddleware, redisSettingsShape, refreshSchema, registerExceptionHandlers, renderAuthResultPage, renderDashboardPage, renderDetailPage, renderFormPage, renderImportPage, renderLayout, renderListPage, renderLoginPage, renderMfaPage, renderPasswordResetFormPage, requestIdMiddleware, requestTracingMiddleware, requireRoles, resolveAdminTheme, resolveDownloadPath, resolveRedocBundle, runServer, runWithRequestContext, sendBytesDownload, sendFileDownload, sessionCookie, sessionSettingsShape, setRequestId, signupSchema, snapshot, sseResponse, statesByRegion, syncFilterSchema, syncPaginationSchema, tableNameFor, toUtc, tokenFromUrl, tokenPairSchema, tokenSettingsShape, trendDirection, trendPercent, ufField, updatedByColumn, uploadSettingsShape, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSettingsShape, webPushSubscriptionSchema, webSocketSettingsShape, widgetForColumn, withTestDatabase, wrapWithSlowQueryLog, wsEnvelopeSchema };
|
|
15158
15653
|
//# sourceMappingURL=index.js.map
|
|
15159
15654
|
//# sourceMappingURL=index.js.map
|