@orieljs/framework 0.1.0-alpha.11 → 0.1.0-alpha.13
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/{chunk-MURTCTRH.js → chunk-6GMXKQ6O.js} +2 -2
- package/dist/{chunk-HTJKT2R2.js → chunk-CKH4KCYF.js} +1 -1
- package/dist/{chunk-HTJKT2R2.js.map → chunk-CKH4KCYF.js.map} +1 -1
- package/dist/{chunk-KDE5BLI3.js → chunk-OTCTQVDQ.js} +7 -4
- package/dist/chunk-OTCTQVDQ.js.map +1 -0
- package/dist/{chunk-24673JQF.js → chunk-ZRTWDJKD.js} +3 -3
- package/dist/contracts.d.ts +7 -0
- package/dist/contracts.js +1 -1
- package/dist/index.d.ts +247 -5
- package/dist/index.js +969 -190
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.js +3 -3
- package/dist/{testing-CKwfWJGn.d.ts → testing-B8NOiQC9.d.ts} +6 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +3 -3
- package/docs/authentication.md +2 -0
- package/docs/console.md +1 -1
- package/docs/deployment.md +25 -5
- package/docs/live.md +20 -0
- package/docs/navigation.json +4 -0
- package/docs/routing.md +1 -1
- package/docs/scheduling.md +3 -0
- package/docs/storage.md +24 -3
- package/docs/two-factor.md +183 -0
- package/docs/vite.md +35 -7
- package/package.json +35 -33
- package/dist/chunk-KDE5BLI3.js.map +0 -1
- /package/dist/{chunk-MURTCTRH.js.map → chunk-6GMXKQ6O.js.map} +0 -0
- /package/dist/{chunk-24673JQF.js.map → chunk-ZRTWDJKD.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
renderToString,
|
|
23
23
|
styleString,
|
|
24
24
|
view
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-ZRTWDJKD.js";
|
|
26
26
|
import {
|
|
27
27
|
API_ROUTES_STUB,
|
|
28
28
|
Application as Application2,
|
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
QueueFake,
|
|
75
75
|
RenameClasses,
|
|
76
76
|
Repository,
|
|
77
|
+
SESSION_URI,
|
|
77
78
|
StorageFake,
|
|
78
79
|
TEMPORARY_UPLOAD_DEFAULTS,
|
|
79
80
|
TemporaryFileNotFoundError,
|
|
@@ -148,7 +149,7 @@ import {
|
|
|
148
149
|
vite,
|
|
149
150
|
viteReactRefresh,
|
|
150
151
|
writeResponse
|
|
151
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-OTCTQVDQ.js";
|
|
152
153
|
import {
|
|
153
154
|
Arr,
|
|
154
155
|
BindingResolutionError,
|
|
@@ -175,7 +176,7 @@ import {
|
|
|
175
176
|
retry,
|
|
176
177
|
tap,
|
|
177
178
|
value
|
|
178
|
-
} from "./chunk-
|
|
179
|
+
} from "./chunk-6GMXKQ6O.js";
|
|
179
180
|
import {
|
|
180
181
|
Application,
|
|
181
182
|
AuthFactory,
|
|
@@ -217,7 +218,7 @@ import {
|
|
|
217
218
|
ValidationFactory,
|
|
218
219
|
ViewFactory,
|
|
219
220
|
token
|
|
220
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-CKH4KCYF.js";
|
|
221
222
|
|
|
222
223
|
// ../Auth/dist/index.js
|
|
223
224
|
import { createHash } from "crypto";
|
|
@@ -229,6 +230,13 @@ import { createHmac } from "crypto";
|
|
|
229
230
|
import { readdir } from "fs/promises";
|
|
230
231
|
import { join } from "path";
|
|
231
232
|
import { pathToFileURL } from "url";
|
|
233
|
+
import { relative } from "path";
|
|
234
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
235
|
+
import { join as join2 } from "path";
|
|
236
|
+
import { createHmac as createHmac3, randomBytes as randomBytes2, randomInt as randomInt2, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
237
|
+
import { renderSVG } from "uqr";
|
|
238
|
+
import { randomInt } from "crypto";
|
|
239
|
+
import { createHmac as createHmac2, randomBytes, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
232
240
|
var AuthenticationError = class extends Error {
|
|
233
241
|
constructor(message = "Unauthenticated.", guards = [], redirectTo2) {
|
|
234
242
|
super(message);
|
|
@@ -1682,6 +1690,455 @@ function auth(guard2) {
|
|
|
1682
1690
|
const manager2 = inject(AuthFactory);
|
|
1683
1691
|
return guard2 === void 0 ? manager2 : manager2.guard(guard2);
|
|
1684
1692
|
}
|
|
1693
|
+
var TWO_FACTOR_DEFAULTS = {
|
|
1694
|
+
enabled: false,
|
|
1695
|
+
methods: ["email", "app"],
|
|
1696
|
+
default: "email",
|
|
1697
|
+
code_ttl: 600,
|
|
1698
|
+
pending_ttl: 600,
|
|
1699
|
+
max_attempts: 5,
|
|
1700
|
+
resend_after: 30,
|
|
1701
|
+
window: 1,
|
|
1702
|
+
recovery_codes: 8
|
|
1703
|
+
};
|
|
1704
|
+
function twoFactorConfig(config2) {
|
|
1705
|
+
return { ...TWO_FACTOR_DEFAULTS, ...config2.get("auth.two_factor", {}) };
|
|
1706
|
+
}
|
|
1707
|
+
var TWO_FACTOR_COLUMNS_MIGRATION = `import { Migration, Schema } from '@orieljs/framework'
|
|
1708
|
+
|
|
1709
|
+
export default class extends Migration {
|
|
1710
|
+
async up() {
|
|
1711
|
+
await Schema.table('users', (t) => {
|
|
1712
|
+
t.text('two_factor_secret').nullable()
|
|
1713
|
+
t.text('two_factor_recovery_codes').nullable()
|
|
1714
|
+
t.timestamp('two_factor_confirmed_at').nullable()
|
|
1715
|
+
t.string('two_factor_method', 10).nullable()
|
|
1716
|
+
})
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
async down() {
|
|
1720
|
+
await Schema.table('users', (t) => {
|
|
1721
|
+
t.dropColumn('two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at', 'two_factor_method')
|
|
1722
|
+
})
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
`;
|
|
1726
|
+
var NAME = "add_two_factor_columns_to_users_table";
|
|
1727
|
+
var pad = (value2) => String(value2).padStart(2, "0");
|
|
1728
|
+
function installTwoFactorMigration(directory, now5 = /* @__PURE__ */ new Date()) {
|
|
1729
|
+
if (existsSync(directory) && readdirSync(directory).some((file) => file.endsWith(`_${NAME}.ts`))) {
|
|
1730
|
+
throw new Error("The two-factor migration is there already: run `oriel migrate`.");
|
|
1731
|
+
}
|
|
1732
|
+
mkdirSync(directory, { recursive: true });
|
|
1733
|
+
const stamp = `${now5.getFullYear()}_${pad(now5.getMonth() + 1)}_${pad(now5.getDate())}_${pad(now5.getHours())}${pad(now5.getMinutes())}${pad(now5.getSeconds())}`;
|
|
1734
|
+
const path = join2(directory, `${stamp}_${NAME}.ts`);
|
|
1735
|
+
writeFileSync(path, TWO_FACTOR_COLUMNS_MIGRATION);
|
|
1736
|
+
return path;
|
|
1737
|
+
}
|
|
1738
|
+
var TwoFactorChallenged = class {
|
|
1739
|
+
constructor(user, method) {
|
|
1740
|
+
this.user = user;
|
|
1741
|
+
this.method = method;
|
|
1742
|
+
}
|
|
1743
|
+
user;
|
|
1744
|
+
method;
|
|
1745
|
+
};
|
|
1746
|
+
var TwoFactorFailed = class {
|
|
1747
|
+
constructor(user, method) {
|
|
1748
|
+
this.user = user;
|
|
1749
|
+
this.method = method;
|
|
1750
|
+
}
|
|
1751
|
+
user;
|
|
1752
|
+
method;
|
|
1753
|
+
};
|
|
1754
|
+
var TwoFactorAuthenticated = class {
|
|
1755
|
+
constructor(user, method) {
|
|
1756
|
+
this.user = user;
|
|
1757
|
+
this.method = method;
|
|
1758
|
+
}
|
|
1759
|
+
user;
|
|
1760
|
+
method;
|
|
1761
|
+
};
|
|
1762
|
+
var RecoveryCodeUsed = class {
|
|
1763
|
+
constructor(user) {
|
|
1764
|
+
this.user = user;
|
|
1765
|
+
}
|
|
1766
|
+
user;
|
|
1767
|
+
};
|
|
1768
|
+
var TwoFactorAppEnabled = class {
|
|
1769
|
+
constructor(user) {
|
|
1770
|
+
this.user = user;
|
|
1771
|
+
}
|
|
1772
|
+
user;
|
|
1773
|
+
};
|
|
1774
|
+
var TwoFactorAppDisabled = class {
|
|
1775
|
+
constructor(user) {
|
|
1776
|
+
this.user = user;
|
|
1777
|
+
}
|
|
1778
|
+
user;
|
|
1779
|
+
};
|
|
1780
|
+
function qrSvg(text4) {
|
|
1781
|
+
return renderSVG(text4);
|
|
1782
|
+
}
|
|
1783
|
+
var CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1784
|
+
var part = () => Array.from({ length: 5 }, () => CHARACTERS[randomInt(CHARACTERS.length)]).join("");
|
|
1785
|
+
function generateRecoveryCodes(count = 8) {
|
|
1786
|
+
const codes = /* @__PURE__ */ new Set();
|
|
1787
|
+
while (codes.size < count) codes.add(`${part()}-${part()}`);
|
|
1788
|
+
return [...codes];
|
|
1789
|
+
}
|
|
1790
|
+
function normalizeCode(input) {
|
|
1791
|
+
return input.replace(/[\s-]/g, "").toLowerCase();
|
|
1792
|
+
}
|
|
1793
|
+
var TwoFactorStore = class {
|
|
1794
|
+
constructor(encrypter, config2) {
|
|
1795
|
+
this.encrypter = encrypter;
|
|
1796
|
+
this.config = config2;
|
|
1797
|
+
}
|
|
1798
|
+
encrypter;
|
|
1799
|
+
config;
|
|
1800
|
+
secret(user) {
|
|
1801
|
+
const stored = user.getAttribute("two_factor_secret");
|
|
1802
|
+
return stored ? this.encrypter.decryptString(stored) : void 0;
|
|
1803
|
+
}
|
|
1804
|
+
/** A new secret (or none) is unconfirmed until confirm(). */
|
|
1805
|
+
async setSecret(user, secret) {
|
|
1806
|
+
user.setAttribute("two_factor_secret", secret === null ? null : this.encrypter.encryptString(secret));
|
|
1807
|
+
user.setAttribute("two_factor_confirmed_at", null);
|
|
1808
|
+
await user.save();
|
|
1809
|
+
}
|
|
1810
|
+
confirmedAt(user) {
|
|
1811
|
+
const stored = user.getAttribute("two_factor_confirmed_at");
|
|
1812
|
+
return stored ? new Date(stored) : void 0;
|
|
1813
|
+
}
|
|
1814
|
+
async confirm(user) {
|
|
1815
|
+
user.setAttribute("two_factor_confirmed_at", /* @__PURE__ */ new Date());
|
|
1816
|
+
await user.save();
|
|
1817
|
+
}
|
|
1818
|
+
recoveryCodes(user) {
|
|
1819
|
+
const stored = user.getAttribute("two_factor_recovery_codes");
|
|
1820
|
+
return stored ? JSON.parse(this.encrypter.decryptString(stored)) : [];
|
|
1821
|
+
}
|
|
1822
|
+
async setRecoveryCodes(user, codes) {
|
|
1823
|
+
user.setAttribute("two_factor_recovery_codes", this.encrypter.encryptString(JSON.stringify(codes)));
|
|
1824
|
+
await user.save();
|
|
1825
|
+
}
|
|
1826
|
+
/** Whether the user's authenticator counts: set up and confirmed. */
|
|
1827
|
+
hasApp(user) {
|
|
1828
|
+
return this.secret(user) !== void 0 && this.confirmedAt(user) !== void 0;
|
|
1829
|
+
}
|
|
1830
|
+
/** The methods open to the user now: the app's, of those the user can use. */
|
|
1831
|
+
methods(user) {
|
|
1832
|
+
return this.config().methods.filter((method) => method === "email" || this.hasApp(user));
|
|
1833
|
+
}
|
|
1834
|
+
/** The method the user is asked for: the one they chose if they can use it, else the app's default, else another. */
|
|
1835
|
+
method(user) {
|
|
1836
|
+
const open = this.methods(user);
|
|
1837
|
+
const chosen = user.getAttribute("two_factor_method");
|
|
1838
|
+
if (chosen && open.includes(chosen)) return chosen;
|
|
1839
|
+
return open.includes(this.config().default) ? this.config().default : open[0];
|
|
1840
|
+
}
|
|
1841
|
+
async setMethod(user, method) {
|
|
1842
|
+
user.setAttribute("two_factor_method", method);
|
|
1843
|
+
await user.save();
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
var toMailCallback3;
|
|
1847
|
+
var TwoFactorCode = class {
|
|
1848
|
+
constructor(code, minutes) {
|
|
1849
|
+
this.code = code;
|
|
1850
|
+
this.minutes = minutes;
|
|
1851
|
+
}
|
|
1852
|
+
code;
|
|
1853
|
+
minutes;
|
|
1854
|
+
/** Build the mail yourself (e.g. a MailMessage from the Notifications component). */
|
|
1855
|
+
static toMailUsing(callback) {
|
|
1856
|
+
toMailCallback3 = callback;
|
|
1857
|
+
}
|
|
1858
|
+
via() {
|
|
1859
|
+
return ["mail"];
|
|
1860
|
+
}
|
|
1861
|
+
toMail(notifiable) {
|
|
1862
|
+
if (toMailCallback3 !== void 0) return toMailCallback3(notifiable, this.code, this.minutes);
|
|
1863
|
+
return {
|
|
1864
|
+
subject: "Your sign-in code",
|
|
1865
|
+
lines: [`Your sign-in code is ${this.code}.`, `It works once, for ${this.minutes} minutes.`],
|
|
1866
|
+
outroLines: ["If you didn't just try to sign in, someone else knows your password: change it."]
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
var ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
1871
|
+
var STEP_SECONDS = 30;
|
|
1872
|
+
function base32Encode(bytes) {
|
|
1873
|
+
let bits = 0;
|
|
1874
|
+
let value2 = 0;
|
|
1875
|
+
let out = "";
|
|
1876
|
+
for (const byte of bytes) {
|
|
1877
|
+
value2 = value2 << 8 | byte;
|
|
1878
|
+
bits += 8;
|
|
1879
|
+
while (bits >= 5) {
|
|
1880
|
+
out += ALPHABET[value2 >>> bits - 5 & 31];
|
|
1881
|
+
bits -= 5;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (bits > 0) out += ALPHABET[value2 << 5 - bits & 31];
|
|
1885
|
+
return out;
|
|
1886
|
+
}
|
|
1887
|
+
function base32Decode(text4) {
|
|
1888
|
+
const clean = text4.replace(/[\s=]/g, "").toUpperCase();
|
|
1889
|
+
const out = [];
|
|
1890
|
+
let bits = 0;
|
|
1891
|
+
let value2 = 0;
|
|
1892
|
+
for (const char of clean) {
|
|
1893
|
+
const index = ALPHABET.indexOf(char);
|
|
1894
|
+
if (index === -1) throw new Error("Not a base32 secret.");
|
|
1895
|
+
value2 = value2 << 5 | index;
|
|
1896
|
+
bits += 5;
|
|
1897
|
+
if (bits >= 8) {
|
|
1898
|
+
out.push(value2 >>> bits - 8 & 255);
|
|
1899
|
+
bits -= 8;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
return new Uint8Array(out);
|
|
1903
|
+
}
|
|
1904
|
+
function generateSecret() {
|
|
1905
|
+
return base32Encode(new Uint8Array(randomBytes(20)));
|
|
1906
|
+
}
|
|
1907
|
+
function totpCode(secret, at, step = Math.floor(at / 1e3 / STEP_SECONDS)) {
|
|
1908
|
+
const counter = Buffer.alloc(8);
|
|
1909
|
+
counter.writeBigUInt64BE(BigInt(step));
|
|
1910
|
+
const hmac = createHmac2("sha1", base32Decode(secret)).update(counter).digest();
|
|
1911
|
+
const offset = hmac[hmac.length - 1] & 15;
|
|
1912
|
+
const binary = (hmac[offset] & 127) << 24 | hmac[offset + 1] << 16 | hmac[offset + 2] << 8 | hmac[offset + 3];
|
|
1913
|
+
return String(binary % 1e6).padStart(6, "0");
|
|
1914
|
+
}
|
|
1915
|
+
function verifyTotp(secret, code, at, window = 1) {
|
|
1916
|
+
if (!/^\d{6}$/.test(code)) return void 0;
|
|
1917
|
+
const now5 = Math.floor(at / 1e3 / STEP_SECONDS);
|
|
1918
|
+
let matched;
|
|
1919
|
+
for (let step = now5 - window; step <= now5 + window; step++) {
|
|
1920
|
+
if (timingSafeEqual2(Buffer.from(totpCode(secret, at, step)), Buffer.from(code)) && matched === void 0) matched = step;
|
|
1921
|
+
}
|
|
1922
|
+
return matched;
|
|
1923
|
+
}
|
|
1924
|
+
function otpauthUrl(options) {
|
|
1925
|
+
const issuer = encodeURIComponent(options.issuer);
|
|
1926
|
+
return `otpauth://totp/${issuer}:${encodeURIComponent(options.account)}?secret=${options.secret}&issuer=${issuer}&algorithm=SHA1&digits=6&period=${STEP_SECONDS}`;
|
|
1927
|
+
}
|
|
1928
|
+
var PENDING_LOGIN = "oriel.two_factor.login";
|
|
1929
|
+
var pendingKey = (nonce) => `oriel:two-factor:pending:${nonce}`;
|
|
1930
|
+
var emailKey = (id) => `oriel:two-factor:email:${String(id)}`;
|
|
1931
|
+
var stepKey = (id) => `oriel:two-factor:totp:${String(id)}`;
|
|
1932
|
+
function maskEmail(email) {
|
|
1933
|
+
const [name = "", domain = ""] = String(email ?? "").split("@");
|
|
1934
|
+
return `${name.slice(0, 1)}***@${domain}`;
|
|
1935
|
+
}
|
|
1936
|
+
var TwoFactorManager = class {
|
|
1937
|
+
#requireWhen;
|
|
1938
|
+
/** Who is asked for a second factor when two-factor is on. Default: everyone. */
|
|
1939
|
+
requireWhen(callback) {
|
|
1940
|
+
this.#requireWhen = callback;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Checks the credentials. `'authenticated'`: logged in (two-factor off, not required for this user, or no method
|
|
1944
|
+
* open to them). `'challenge'`: the password was right and the code is asked for (an email method's code is sent
|
|
1945
|
+
* now). `false`: wrong credentials.
|
|
1946
|
+
*/
|
|
1947
|
+
async attempt(credentials, remember = false) {
|
|
1948
|
+
const config2 = this.#config();
|
|
1949
|
+
const guard2 = this.#guard();
|
|
1950
|
+
if (!config2.enabled) return await guard2.attempt(credentials, remember) ? "authenticated" : false;
|
|
1951
|
+
const guardName = inject(ConfigRepository).get("auth.defaults.guard", "web");
|
|
1952
|
+
await this.#dispatch(new Attempting(guardName, credentials, remember));
|
|
1953
|
+
const valid = await guard2.validate(credentials);
|
|
1954
|
+
const user = guard2.getLastAttempted();
|
|
1955
|
+
if (!valid || user === void 0) {
|
|
1956
|
+
await this.#dispatch(new Failed(guardName, user, credentials));
|
|
1957
|
+
return false;
|
|
1958
|
+
}
|
|
1959
|
+
const method = this.#store().method(user);
|
|
1960
|
+
if (method === void 0 || !await (this.#requireWhen?.(user) ?? true)) {
|
|
1961
|
+
await guard2.login(user, remember);
|
|
1962
|
+
return "authenticated";
|
|
1963
|
+
}
|
|
1964
|
+
const nonce = randomBytes2(16).toString("hex");
|
|
1965
|
+
const record = { id: user.getAuthIdentifier(), remember, method, expires: Date.now() + config2.pending_ttl * 1e3, attempts: 0, nonce };
|
|
1966
|
+
inject(Session).put(PENDING_LOGIN, record);
|
|
1967
|
+
await inject(CacheManager).put(pendingKey(nonce), true, config2.pending_ttl);
|
|
1968
|
+
if (method === "email") await this.#sendCode(user);
|
|
1969
|
+
await this.#dispatch(new TwoFactorChallenged(user, method));
|
|
1970
|
+
return "challenge";
|
|
1971
|
+
}
|
|
1972
|
+
/** The pending login, for the challenge page: the method asked for, the others open, and where an email code went. */
|
|
1973
|
+
async challenge() {
|
|
1974
|
+
const pending = await this.#pending();
|
|
1975
|
+
if (pending === void 0) return void 0;
|
|
1976
|
+
return { method: pending.record.method, methods: this.#store().methods(pending.user), email: maskEmail(pending.user.getAttribute("email")) };
|
|
1977
|
+
}
|
|
1978
|
+
/** A 6-digit code for the pending method, or a recovery code. True: the user is logged in. */
|
|
1979
|
+
async verify(code) {
|
|
1980
|
+
const pending = await this.#pending();
|
|
1981
|
+
if (pending === void 0) return false;
|
|
1982
|
+
const { record, user } = pending;
|
|
1983
|
+
const typed = normalizeCode(code);
|
|
1984
|
+
let used;
|
|
1985
|
+
if (/^\d{6}$/.test(typed)) {
|
|
1986
|
+
if (record.method === "app" ? await this.#checkApp(user, typed) : await this.#checkEmail(user, typed)) used = record.method;
|
|
1987
|
+
} else if (/^[a-z0-9]{10}$/.test(typed) && await this.#checkRecovery(user, typed)) {
|
|
1988
|
+
used = "recovery";
|
|
1989
|
+
}
|
|
1990
|
+
if (used === void 0) {
|
|
1991
|
+
record.attempts++;
|
|
1992
|
+
await this.#dispatch(new TwoFactorFailed(user, record.method));
|
|
1993
|
+
if (record.attempts >= this.#config().max_attempts) await this.#end(record);
|
|
1994
|
+
else inject(Session).put(PENDING_LOGIN, record);
|
|
1995
|
+
return false;
|
|
1996
|
+
}
|
|
1997
|
+
await this.#end(record);
|
|
1998
|
+
await this.#guard().login(user, record.remember);
|
|
1999
|
+
await this.#dispatch(new TwoFactorAuthenticated(user, used));
|
|
2000
|
+
return true;
|
|
2001
|
+
}
|
|
2002
|
+
/** Asks for another of the user's methods; switching to email sends a code. */
|
|
2003
|
+
async useMethod(method) {
|
|
2004
|
+
const pending = await this.#pending();
|
|
2005
|
+
if (pending === void 0 || !this.#store().methods(pending.user).includes(method)) return;
|
|
2006
|
+
pending.record.method = method;
|
|
2007
|
+
inject(Session).put(PENDING_LOGIN, pending.record);
|
|
2008
|
+
if (method === "email") await this.#sendCode(pending.user);
|
|
2009
|
+
}
|
|
2010
|
+
/** Sends a new email code; false within `resend_after` seconds of the last one, or when the method isn't email. */
|
|
2011
|
+
async resend() {
|
|
2012
|
+
const pending = await this.#pending();
|
|
2013
|
+
if (pending === void 0 || pending.record.method !== "email") return false;
|
|
2014
|
+
const last = await inject(CacheManager).get(emailKey(pending.record.id));
|
|
2015
|
+
if (last !== void 0 && Date.now() - last.sent < this.#config().resend_after * 1e3) return false;
|
|
2016
|
+
await this.#sendCode(pending.user);
|
|
2017
|
+
return true;
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* Starts setting up an authenticator app: a new secret (unconfirmed, so it doesn't count yet), the `otpauth://` URL
|
|
2021
|
+
* and a QR code of it for the app to scan.
|
|
2022
|
+
*/
|
|
2023
|
+
async enableApp(user) {
|
|
2024
|
+
const secret = generateSecret();
|
|
2025
|
+
await this.#store().setSecret(user, secret);
|
|
2026
|
+
const issuer = this.#config().issuer ?? inject(ConfigRepository).get("app.name", "Oriel");
|
|
2027
|
+
const url2 = otpauthUrl({ secret, issuer, account: String(user.getAttribute("email") ?? user.getAuthIdentifier()) });
|
|
2028
|
+
return { secret, url: url2, svg: qrSvg(url2) };
|
|
2029
|
+
}
|
|
2030
|
+
/** Confirms the app with a code from it: then it counts, becomes the user's method, and recovery codes are made. */
|
|
2031
|
+
async confirmApp(user, code) {
|
|
2032
|
+
const store = this.#store();
|
|
2033
|
+
const secret = store.secret(user);
|
|
2034
|
+
if (secret === void 0 || verifyTotp(secret, normalizeCode(code), Date.now(), this.#config().window) === void 0) return false;
|
|
2035
|
+
await store.confirm(user);
|
|
2036
|
+
await store.setMethod(user, "app");
|
|
2037
|
+
if (store.recoveryCodes(user).length === 0) await store.setRecoveryCodes(user, generateRecoveryCodes(this.#config().recovery_codes));
|
|
2038
|
+
await this.#dispatch(new TwoFactorAppEnabled(user));
|
|
2039
|
+
return true;
|
|
2040
|
+
}
|
|
2041
|
+
/** Removes the authenticator app: the secret is forgotten and the user is asked by email again. */
|
|
2042
|
+
async disableApp(user) {
|
|
2043
|
+
const store = this.#store();
|
|
2044
|
+
await store.setSecret(user, null);
|
|
2045
|
+
await store.setMethod(user, null);
|
|
2046
|
+
await this.#dispatch(new TwoFactorAppDisabled(user));
|
|
2047
|
+
}
|
|
2048
|
+
/** The method the user prefers; one they can't use (an unconfirmed app, one the app turned off) isn't asked for. */
|
|
2049
|
+
async setMethod(user, method) {
|
|
2050
|
+
await this.#store().setMethod(user, method);
|
|
2051
|
+
}
|
|
2052
|
+
/** The user's recovery codes, made on first ask (show them once, and offer to make new ones). */
|
|
2053
|
+
async recoveryCodes(user) {
|
|
2054
|
+
const store = this.#store();
|
|
2055
|
+
const codes = store.recoveryCodes(user);
|
|
2056
|
+
if (codes.length > 0) return codes;
|
|
2057
|
+
return this.regenerateRecoveryCodes(user);
|
|
2058
|
+
}
|
|
2059
|
+
/** New recovery codes; the old ones stop working. */
|
|
2060
|
+
async regenerateRecoveryCodes(user) {
|
|
2061
|
+
const codes = generateRecoveryCodes(this.#config().recovery_codes);
|
|
2062
|
+
await this.#store().setRecoveryCodes(user, codes);
|
|
2063
|
+
return codes;
|
|
2064
|
+
}
|
|
2065
|
+
#config() {
|
|
2066
|
+
return twoFactorConfig(inject(ConfigRepository));
|
|
2067
|
+
}
|
|
2068
|
+
#store() {
|
|
2069
|
+
return new TwoFactorStore(inject(Encrypter), () => this.#config());
|
|
2070
|
+
}
|
|
2071
|
+
#guard() {
|
|
2072
|
+
return inject(AuthFactory).guard();
|
|
2073
|
+
}
|
|
2074
|
+
async #dispatch(event2) {
|
|
2075
|
+
const container = currentContainer();
|
|
2076
|
+
if (container?.bound(EventDispatcher)) await container.make(EventDispatcher).dispatch(event2);
|
|
2077
|
+
}
|
|
2078
|
+
/** The live pending login and its user; an expired, replayed or orphaned one is forgotten. */
|
|
2079
|
+
async #pending() {
|
|
2080
|
+
const session2 = inject(Session);
|
|
2081
|
+
const record = session2.get(PENDING_LOGIN);
|
|
2082
|
+
if (record === void 0 || record === null) return void 0;
|
|
2083
|
+
const live2 = record.expires > Date.now() && await inject(CacheManager).get(pendingKey(record.nonce)) === true;
|
|
2084
|
+
const user = live2 ? await this.#guard().provider.retrieveById(record.id) : void 0;
|
|
2085
|
+
if (user === void 0) {
|
|
2086
|
+
session2.forget(PENDING_LOGIN);
|
|
2087
|
+
return void 0;
|
|
2088
|
+
}
|
|
2089
|
+
return { record, user };
|
|
2090
|
+
}
|
|
2091
|
+
async #end(record) {
|
|
2092
|
+
inject(Session).forget(PENDING_LOGIN);
|
|
2093
|
+
await inject(CacheManager).forget(pendingKey(record.nonce));
|
|
2094
|
+
}
|
|
2095
|
+
#hash(code) {
|
|
2096
|
+
return createHmac3("sha256", inject(Encrypter).getKey()).update(code).digest("hex");
|
|
2097
|
+
}
|
|
2098
|
+
async #sendCode(user) {
|
|
2099
|
+
const config2 = this.#config();
|
|
2100
|
+
const code = String(randomInt2(1e6)).padStart(6, "0");
|
|
2101
|
+
const now5 = Date.now();
|
|
2102
|
+
const stored = { hash: this.#hash(code), sent: now5, expires: now5 + config2.code_ttl * 1e3 };
|
|
2103
|
+
await inject(CacheManager).put(emailKey(user.getAuthIdentifier()), stored, config2.code_ttl);
|
|
2104
|
+
await sendNotification(user, new TwoFactorCode(code, Math.round(config2.code_ttl / 60)));
|
|
2105
|
+
}
|
|
2106
|
+
async #checkEmail(user, code) {
|
|
2107
|
+
const cache2 = inject(CacheManager);
|
|
2108
|
+
const stored = await cache2.get(emailKey(user.getAuthIdentifier()));
|
|
2109
|
+
if (stored === void 0 || stored.expires <= Date.now()) return false;
|
|
2110
|
+
if (!timingSafeEqual3(Buffer.from(stored.hash), Buffer.from(this.#hash(code)))) return false;
|
|
2111
|
+
await cache2.forget(emailKey(user.getAuthIdentifier()));
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
async #checkApp(user, code) {
|
|
2115
|
+
const store = this.#store();
|
|
2116
|
+
const secret = store.secret(user);
|
|
2117
|
+
if (secret === void 0 || !store.hasApp(user)) return false;
|
|
2118
|
+
const step = verifyTotp(secret, code, Date.now(), this.#config().window);
|
|
2119
|
+
if (step === void 0) return false;
|
|
2120
|
+
const cache2 = inject(CacheManager);
|
|
2121
|
+
const last = await cache2.get(stepKey(user.getAuthIdentifier()));
|
|
2122
|
+
if (last !== void 0 && step <= last) return false;
|
|
2123
|
+
await cache2.put(stepKey(user.getAuthIdentifier()), step, 300);
|
|
2124
|
+
return true;
|
|
2125
|
+
}
|
|
2126
|
+
async #checkRecovery(user, code) {
|
|
2127
|
+
const store = this.#store();
|
|
2128
|
+
const codes = store.recoveryCodes(user);
|
|
2129
|
+
const index = codes.findIndex((candidate) => normalizeCode(candidate) === code);
|
|
2130
|
+
if (index === -1) return false;
|
|
2131
|
+
await store.setRecoveryCodes(user, codes.filter((_, i) => i !== index));
|
|
2132
|
+
await this.#dispatch(new RecoveryCodeUsed(user));
|
|
2133
|
+
return true;
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
var TwoFactorChallenge = class {
|
|
2137
|
+
async handle(request2, next) {
|
|
2138
|
+
if (await inject(TwoFactorManager).challenge() === void 0) throw new AuthenticationError("Log in first.");
|
|
2139
|
+
return next(request2);
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
1685
2142
|
var AuthServiceProvider = class extends ServiceProvider {
|
|
1686
2143
|
register() {
|
|
1687
2144
|
this.mergeConfigFrom(
|
|
@@ -1693,7 +2150,8 @@ var AuthServiceProvider = class extends ServiceProvider {
|
|
|
1693
2150
|
},
|
|
1694
2151
|
providers: { users: { driver: "eloquent", model: null } },
|
|
1695
2152
|
passwords: { users: { provider: "users", table: "password_reset_tokens", expire: 60, throttle: 60 } },
|
|
1696
|
-
password_timeout: 10800
|
|
2153
|
+
password_timeout: 10800,
|
|
2154
|
+
two_factor: { ...TWO_FACTOR_DEFAULTS }
|
|
1697
2155
|
},
|
|
1698
2156
|
"auth"
|
|
1699
2157
|
);
|
|
@@ -1703,15 +2161,29 @@ var AuthServiceProvider = class extends ServiceProvider {
|
|
|
1703
2161
|
this.app.singleton(PasswordBrokerManager, (container) => new PasswordBrokerManager(container));
|
|
1704
2162
|
this.app.singleton(PasswordBroker, (container) => container.make(PasswordBrokerManager).broker());
|
|
1705
2163
|
this.app.singleton(TokenRepository, (container) => new TokenRepository(() => container.make(DatabaseManager)));
|
|
2164
|
+
this.app.singleton(TwoFactorManager, () => new TwoFactorManager());
|
|
1706
2165
|
}
|
|
1707
2166
|
async boot() {
|
|
1708
2167
|
if (this.app.bound(Router)) {
|
|
1709
|
-
this.app.make(Router).aliasMiddleware("auth", Authenticate).aliasMiddleware("guest", RedirectIfAuthenticated).aliasMiddleware("can", Authorize).aliasMiddleware("verified", EnsureEmailIsVerified);
|
|
2168
|
+
this.app.make(Router).aliasMiddleware("auth", Authenticate).aliasMiddleware("guest", RedirectIfAuthenticated).aliasMiddleware("can", Authorize).aliasMiddleware("verified", EnsureEmailIsVerified).aliasMiddleware("two-factor.challenge", TwoFactorChallenge);
|
|
1710
2169
|
}
|
|
1711
2170
|
if (this.app.bound(EventDispatcher)) this.app.make(EventDispatcher).listen(Registered, SendEmailVerificationNotification);
|
|
1712
2171
|
await discoverPolicies(this.app.appPath("Policies"), this.app.make(Gate));
|
|
2172
|
+
if (this.app.bound(ConsoleKernel)) {
|
|
2173
|
+
const app2 = this.app;
|
|
2174
|
+
this.app.make(ConsoleKernel).command("two-factor:install", function() {
|
|
2175
|
+
try {
|
|
2176
|
+
this.info(`Wrote ${relative(app2.basePath(), installTwoFactorMigration(app2.databasePath("migrations")))}: run \`oriel migrate\`.`);
|
|
2177
|
+
return 0;
|
|
2178
|
+
} catch (error) {
|
|
2179
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
2180
|
+
return 1;
|
|
2181
|
+
}
|
|
2182
|
+
}).purpose("Write the migration for two-factor login's columns on users");
|
|
2183
|
+
}
|
|
1713
2184
|
}
|
|
1714
2185
|
};
|
|
2186
|
+
var TwoFactor = facade(TwoFactorManager);
|
|
1715
2187
|
|
|
1716
2188
|
// ../Bus/dist/index.js
|
|
1717
2189
|
var BusError = class extends Error {
|
|
@@ -1973,9 +2445,9 @@ var Dispatcher = class {
|
|
|
1973
2445
|
}
|
|
1974
2446
|
#afterResponseList() {
|
|
1975
2447
|
const owner = currentContainer() ?? this.container;
|
|
1976
|
-
let
|
|
1977
|
-
if (
|
|
1978
|
-
return
|
|
2448
|
+
let list6 = this.#afterResponse.get(owner);
|
|
2449
|
+
if (list6 === void 0) this.#afterResponse.set(owner, list6 = []);
|
|
2450
|
+
return list6;
|
|
1979
2451
|
}
|
|
1980
2452
|
/** Runs the job (like dispatchSync) after the response is sent, or after the current queued job. */
|
|
1981
2453
|
dispatchAfterResponse(job) {
|
|
@@ -2154,7 +2626,7 @@ var BusServiceProvider = class extends ServiceProvider {
|
|
|
2154
2626
|
import { setTimeout as sleep } from "timers/promises";
|
|
2155
2627
|
import { createHash as createHash5 } from "crypto";
|
|
2156
2628
|
import { link, mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
2157
|
-
import { dirname, join as
|
|
2629
|
+
import { dirname, join as join3 } from "path";
|
|
2158
2630
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
2159
2631
|
import { createHash as createHash22, randomUUID } from "crypto";
|
|
2160
2632
|
import Redis from "ioredis";
|
|
@@ -2430,7 +2902,7 @@ var FileStore = class {
|
|
|
2430
2902
|
keyPrefix;
|
|
2431
2903
|
path(key) {
|
|
2432
2904
|
const hash2 = createHash5("sha1").update(this.keyPrefix + key).digest("hex");
|
|
2433
|
-
return
|
|
2905
|
+
return join3(this.directory, hash2.slice(0, 2), hash2.slice(2, 4), hash2);
|
|
2434
2906
|
}
|
|
2435
2907
|
tempPath(path) {
|
|
2436
2908
|
return `${path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -3016,8 +3488,8 @@ var RedisRepository = class extends Repository2 {
|
|
|
3016
3488
|
return quit;
|
|
3017
3489
|
}
|
|
3018
3490
|
tags(names) {
|
|
3019
|
-
const
|
|
3020
|
-
const taggedStore = this.redisStore.tags(
|
|
3491
|
+
const list6 = Array.isArray(names) ? names : [names];
|
|
3492
|
+
const taggedStore = this.redisStore.tags(list6);
|
|
3021
3493
|
const prefix = this.redisStore.getPrefix();
|
|
3022
3494
|
return new Repository2(taggedStore, (_store, name, seconds, owner) => new RedisLock(this.redis, prefix + name, seconds, owner));
|
|
3023
3495
|
}
|
|
@@ -3234,18 +3706,18 @@ var CacheServiceProvider = class extends ServiceProvider {
|
|
|
3234
3706
|
};
|
|
3235
3707
|
|
|
3236
3708
|
// ../Console/dist/index.js
|
|
3237
|
-
import { existsSync } from "fs";
|
|
3238
|
-
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
3239
|
-
import { dirname as dirname2, join as join3, relative, sep } from "path";
|
|
3240
3709
|
import { existsSync as existsSync2 } from "fs";
|
|
3710
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
3711
|
+
import { dirname as dirname2, join as join4, relative as relative2, sep } from "path";
|
|
3712
|
+
import { existsSync as existsSync22 } from "fs";
|
|
3241
3713
|
import { join as join22 } from "path";
|
|
3242
3714
|
import { existsSync as existsSync3 } from "fs";
|
|
3243
3715
|
import { mkdir as mkdir22, writeFile as writeFile22 } from "fs/promises";
|
|
3244
3716
|
import { dirname as dirname22, join as join32 } from "path";
|
|
3245
|
-
import { join as
|
|
3717
|
+
import { join as join42 } from "path";
|
|
3246
3718
|
import { spawn } from "child_process";
|
|
3247
3719
|
import { start as start2 } from "repl";
|
|
3248
|
-
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
3720
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
|
|
3249
3721
|
import { join as join5 } from "path";
|
|
3250
3722
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3251
3723
|
import { createHash as createHash6 } from "crypto";
|
|
@@ -3703,7 +4175,7 @@ var GeneratorCommand = class extends Command {
|
|
|
3703
4175
|
async prepare(_rawName) {
|
|
3704
4176
|
}
|
|
3705
4177
|
qualifyClass(rawName) {
|
|
3706
|
-
const segments = rawName.split("/").filter(Boolean).map((
|
|
4178
|
+
const segments = rawName.split("/").filter(Boolean).map((part2) => Str.studly(part2));
|
|
3707
4179
|
const last = segments.pop();
|
|
3708
4180
|
if (!last) throw new Error("A name is required.");
|
|
3709
4181
|
const suffix2 = this.suffix();
|
|
@@ -3720,7 +4192,7 @@ var GeneratorCommand = class extends Command {
|
|
|
3720
4192
|
}
|
|
3721
4193
|
targetPath(rawName) {
|
|
3722
4194
|
const { directories, className } = this.qualifyClass(rawName);
|
|
3723
|
-
return
|
|
4195
|
+
return join4(this.root(this.type()), ...directories, this.fileName(className));
|
|
3724
4196
|
}
|
|
3725
4197
|
/** The singular kind of file for messages, like Laravel's `$type`: 'Http/Controllers' -> 'Controller'. */
|
|
3726
4198
|
kind() {
|
|
@@ -3728,13 +4200,13 @@ var GeneratorCommand = class extends Command {
|
|
|
3728
4200
|
}
|
|
3729
4201
|
/** The import specifier from the generated file to `target` (an absolute path without extension): './X', '../Y/X'. */
|
|
3730
4202
|
importPath(rawName, target) {
|
|
3731
|
-
const specifier =
|
|
4203
|
+
const specifier = relative2(dirname2(this.targetPath(rawName)), target).split(sep).join("/");
|
|
3732
4204
|
return specifier.startsWith(".") ? specifier : `./${specifier}`;
|
|
3733
4205
|
}
|
|
3734
4206
|
/** A --model value as studly segments: 'Admin/blog_post' -> ['Admin', 'BlogPost']; undefined when there is none. */
|
|
3735
4207
|
modelSegments(value2) {
|
|
3736
4208
|
if (typeof value2 !== "string") return void 0;
|
|
3737
|
-
const segments = value2.split(/[/\\]/).filter(Boolean).map((
|
|
4209
|
+
const segments = value2.split(/[/\\]/).filter(Boolean).map((part2) => Str.studly(part2));
|
|
3738
4210
|
return segments.length > 0 ? segments : void 0;
|
|
3739
4211
|
}
|
|
3740
4212
|
/** The model's file under app/Models, without the extension (Laravel's qualifyModel() into App\Models). */
|
|
@@ -3748,7 +4220,7 @@ var GeneratorCommand = class extends Command {
|
|
|
3748
4220
|
return 1;
|
|
3749
4221
|
}
|
|
3750
4222
|
const target = this.targetPath(rawName);
|
|
3751
|
-
if (
|
|
4223
|
+
if (existsSync2(target) && !this.option("force")) {
|
|
3752
4224
|
this.error(`${this.kind()} already exists.`);
|
|
3753
4225
|
return 1;
|
|
3754
4226
|
}
|
|
@@ -4279,7 +4751,7 @@ var MakeControllerCommand = class extends GeneratorCommand {
|
|
|
4279
4751
|
async prepare() {
|
|
4280
4752
|
const segments = this.modelSegments(this.option("model"));
|
|
4281
4753
|
if (!segments) return;
|
|
4282
|
-
if (!
|
|
4754
|
+
if (!existsSync22(`${this.modelPath(segments)}.ts`)) await this.call("make:model", { name: segments.join("/") });
|
|
4283
4755
|
if (this.option("requests")) {
|
|
4284
4756
|
const model = segments.at(-1);
|
|
4285
4757
|
await this.call("make:request", { name: `Store${model}Request` });
|
|
@@ -4343,7 +4815,7 @@ var MakeFactoryCommand = class extends GeneratorCommand {
|
|
|
4343
4815
|
extraPlaceholders(rawName) {
|
|
4344
4816
|
const option = this.option("model");
|
|
4345
4817
|
const modelName = typeof option === "string" ? option : this.qualifyClass(rawName).className.replace(/Factory$/, "");
|
|
4346
|
-
const segments = modelName.split("/").filter(Boolean).map((
|
|
4818
|
+
const segments = modelName.split("/").filter(Boolean).map((part2) => Str.studly(part2));
|
|
4347
4819
|
return {
|
|
4348
4820
|
model: segments.at(-1) ?? modelName,
|
|
4349
4821
|
modelImport: this.importPath(rawName, inject(Application).appPath(["Models", ...segments].join("/")))
|
|
@@ -4374,7 +4846,7 @@ var MakeListenerCommand = class extends GeneratorCommand {
|
|
|
4374
4846
|
extraPlaceholders(rawName) {
|
|
4375
4847
|
const event2 = this.option("event");
|
|
4376
4848
|
if (typeof event2 !== "string") return {};
|
|
4377
|
-
const segments = event2.split("/").filter(Boolean).map((
|
|
4849
|
+
const segments = event2.split("/").filter(Boolean).map((part2) => Str.studly(part2));
|
|
4378
4850
|
return {
|
|
4379
4851
|
event: segments.at(-1) ?? event2,
|
|
4380
4852
|
eventImport: this.importPath(rawName, inject(Application).appPath(["Events", ...segments].join("/")))
|
|
@@ -4404,7 +4876,7 @@ var MakeMailCommand = class extends GeneratorCommand {
|
|
|
4404
4876
|
viewName(rawName) {
|
|
4405
4877
|
const option = this.option("view");
|
|
4406
4878
|
if (typeof option === "string" && option !== "") return option;
|
|
4407
|
-
const parts = rawName.split("/").filter(Boolean).map((
|
|
4879
|
+
const parts = rawName.split("/").filter(Boolean).map((part2) => Str.kebab(Str.studly(part2)));
|
|
4408
4880
|
return ["mail", ...parts].join(".");
|
|
4409
4881
|
}
|
|
4410
4882
|
/** Laravel's writeView(): an existing view is reported and left alone, even with --force. */
|
|
@@ -4620,7 +5092,7 @@ var MakeTestCommand = class extends GeneratorCommand {
|
|
|
4620
5092
|
return TEST_STUB;
|
|
4621
5093
|
}
|
|
4622
5094
|
root(subpath) {
|
|
4623
|
-
return inject(Application).basePath(
|
|
5095
|
+
return inject(Application).basePath(join42("tests", subpath));
|
|
4624
5096
|
}
|
|
4625
5097
|
fileName(className) {
|
|
4626
5098
|
return `${className.replace(/(?<=.)Test$/, "")}.test.ts`;
|
|
@@ -4835,7 +5307,7 @@ var TinkerCommand = class extends Command {
|
|
|
4835
5307
|
};
|
|
4836
5308
|
function sourceFiles(directory) {
|
|
4837
5309
|
const files3 = [];
|
|
4838
|
-
for (const entry of
|
|
5310
|
+
for (const entry of readdirSync2(directory, { withFileTypes: true })) {
|
|
4839
5311
|
const path = join5(directory, entry.name);
|
|
4840
5312
|
if (entry.isDirectory()) files3.push(...sourceFiles(path));
|
|
4841
5313
|
else if (/\.(ts|js|mts|mjs)$/.test(entry.name) && !/\.d\.[mc]?ts$/.test(entry.name)) files3.push(path);
|
|
@@ -4933,6 +5405,15 @@ var Kernel2 = class {
|
|
|
4933
5405
|
defaultOutput;
|
|
4934
5406
|
lastOutput = "";
|
|
4935
5407
|
running = 0;
|
|
5408
|
+
loader;
|
|
5409
|
+
bootstrapping;
|
|
5410
|
+
/** What bootstrap() runs once: the provider hands it the loading of the app's commands and routes/console.ts. */
|
|
5411
|
+
bootstrapWith(loader) {
|
|
5412
|
+
this.loader = loader;
|
|
5413
|
+
}
|
|
5414
|
+
bootstrap() {
|
|
5415
|
+
return this.bootstrapping ??= this.loader?.() ?? Promise.resolve();
|
|
5416
|
+
}
|
|
4936
5417
|
register(...commands) {
|
|
4937
5418
|
for (const entry of commands) {
|
|
4938
5419
|
const instance = typeof entry === "function" ? new entry() : entry;
|
|
@@ -5362,6 +5843,27 @@ var ScheduledEventImpl = class {
|
|
|
5362
5843
|
if (this.betweenWindow && !this.isWithinWindow(this.betweenWindow, now5)) return false;
|
|
5363
5844
|
return !this.unlessWindow || !this.isWithinWindow(this.unlessWindow, now5);
|
|
5364
5845
|
}
|
|
5846
|
+
/**
|
|
5847
|
+
* The latest minute after `from` and up to `to` at which this event was due, its between()/unlessBetween() windows
|
|
5848
|
+
* judged at that minute; undefined when there is none. Walks back from `to` (at most 10,000 due minutes).
|
|
5849
|
+
*/
|
|
5850
|
+
dueAt(from, to) {
|
|
5851
|
+
const end = Math.floor(to.getTime() / 6e4) * 6e4;
|
|
5852
|
+
const parsed = CronExpressionParser.parse(this.cronExpression, { tz: this.zone(), currentDate: new Date(end + 6e4) });
|
|
5853
|
+
for (let step = 0; step < 1e4; step++) {
|
|
5854
|
+
let minute;
|
|
5855
|
+
try {
|
|
5856
|
+
minute = parsed.prev().toDate();
|
|
5857
|
+
} catch {
|
|
5858
|
+
return void 0;
|
|
5859
|
+
}
|
|
5860
|
+
if (minute.getTime() <= from.getTime()) return void 0;
|
|
5861
|
+
if (this.betweenWindow && !this.isWithinWindow(this.betweenWindow, minute)) continue;
|
|
5862
|
+
if (this.unlessWindow && this.isWithinWindow(this.unlessWindow, minute)) continue;
|
|
5863
|
+
return minute;
|
|
5864
|
+
}
|
|
5865
|
+
return void 0;
|
|
5866
|
+
}
|
|
5365
5867
|
zone() {
|
|
5366
5868
|
if (this.tz !== void 0) return this.tz;
|
|
5367
5869
|
const container = currentContainer();
|
|
@@ -5482,6 +5984,9 @@ var ScheduleImpl = class {
|
|
|
5482
5984
|
dueEvents(now5) {
|
|
5483
5985
|
return this.scheduledEvents.filter((event2) => event2.isDue(now5));
|
|
5484
5986
|
}
|
|
5987
|
+
dueBetween(from, to) {
|
|
5988
|
+
return this.scheduledEvents.filter((event2) => event2.dueAt(from, to) !== void 0);
|
|
5989
|
+
}
|
|
5485
5990
|
push(event2) {
|
|
5486
5991
|
this.scheduledEvents.push(event2);
|
|
5487
5992
|
return event2;
|
|
@@ -5490,7 +5995,14 @@ var ScheduleImpl = class {
|
|
|
5490
5995
|
var ConsoleServiceProvider = class extends ServiceProvider {
|
|
5491
5996
|
register() {
|
|
5492
5997
|
this.mergeConfigFrom({ stubs: {} }, "console");
|
|
5493
|
-
this.app.singleton(ConsoleKernel, () =>
|
|
5998
|
+
this.app.singleton(ConsoleKernel, () => {
|
|
5999
|
+
const kernel = new Kernel2(this.app);
|
|
6000
|
+
kernel.bootstrapWith(async () => {
|
|
6001
|
+
await discoverCommands(kernel, this.app.appPath("Console/Commands"));
|
|
6002
|
+
await loadConsoleRoutes(this.app, kernel);
|
|
6003
|
+
});
|
|
6004
|
+
return kernel;
|
|
6005
|
+
});
|
|
5494
6006
|
this.app.singleton(Schedule, () => new ScheduleImpl());
|
|
5495
6007
|
}
|
|
5496
6008
|
boot() {
|
|
@@ -5527,9 +6039,7 @@ var ConsoleServiceProvider = class extends ServiceProvider {
|
|
|
5527
6039
|
);
|
|
5528
6040
|
this.app.booted(async (app2) => {
|
|
5529
6041
|
if (!app2.runningInConsole()) return;
|
|
5530
|
-
|
|
5531
|
-
await discoverCommands(kernel, app2.appPath("Console/Commands"));
|
|
5532
|
-
await loadConsoleRoutes(app2, kernel);
|
|
6042
|
+
await app2.make(ConsoleKernel).bootstrap();
|
|
5533
6043
|
});
|
|
5534
6044
|
}
|
|
5535
6045
|
};
|
|
@@ -5555,7 +6065,7 @@ async function runConsoleAndExit(app2, argv = process.argv.slice(2), exit = (cod
|
|
|
5555
6065
|
}
|
|
5556
6066
|
|
|
5557
6067
|
// ../Cookie/dist/index.js
|
|
5558
|
-
import { createHmac as
|
|
6068
|
+
import { createHmac as createHmac4, timingSafeEqual as timingSafeEqual4 } from "crypto";
|
|
5559
6069
|
var SAME_SITE = { lax: "Lax", strict: "Strict", none: "None" };
|
|
5560
6070
|
function serializeCookie(cookie2, now5 = Date.now()) {
|
|
5561
6071
|
const parts = [];
|
|
@@ -5593,12 +6103,12 @@ function parseSetCookie(header) {
|
|
|
5593
6103
|
}
|
|
5594
6104
|
function parseCookieHeader(header) {
|
|
5595
6105
|
const cookies = {};
|
|
5596
|
-
for (const
|
|
5597
|
-
const equals4 =
|
|
6106
|
+
for (const part2 of (header ?? "").split(";")) {
|
|
6107
|
+
const equals4 = part2.indexOf("=");
|
|
5598
6108
|
if (equals4 === -1) continue;
|
|
5599
|
-
const name =
|
|
6109
|
+
const name = part2.slice(0, equals4).trim();
|
|
5600
6110
|
if (name === "" || Object.hasOwn(cookies, name) || name === "__proto__") continue;
|
|
5601
|
-
cookies[name] = decode(
|
|
6111
|
+
cookies[name] = decode(part2.slice(equals4 + 1).trim());
|
|
5602
6112
|
}
|
|
5603
6113
|
return cookies;
|
|
5604
6114
|
}
|
|
@@ -5609,7 +6119,7 @@ function withHeaders(response2, change) {
|
|
|
5609
6119
|
}
|
|
5610
6120
|
var CookieValuePrefix = {
|
|
5611
6121
|
create(name, key) {
|
|
5612
|
-
return `${
|
|
6122
|
+
return `${createHmac4("sha1", key).update(`${name}v2`).digest("hex")}|`;
|
|
5613
6123
|
},
|
|
5614
6124
|
remove(value2) {
|
|
5615
6125
|
return value2.slice(41);
|
|
@@ -5619,7 +6129,7 @@ var CookieValuePrefix = {
|
|
|
5619
6129
|
const actual = Buffer.from(value2.slice(0, 41));
|
|
5620
6130
|
for (const key of keys) {
|
|
5621
6131
|
const expected = Buffer.from(CookieValuePrefix.create(name, key));
|
|
5622
|
-
if (actual.length === expected.length &&
|
|
6132
|
+
if (actual.length === expected.length && timingSafeEqual4(actual, expected)) return CookieValuePrefix.remove(value2);
|
|
5623
6133
|
}
|
|
5624
6134
|
return void 0;
|
|
5625
6135
|
}
|
|
@@ -5688,11 +6198,11 @@ var AddQueuedCookiesToResponse = class {
|
|
|
5688
6198
|
};
|
|
5689
6199
|
var neverEncrypt = /* @__PURE__ */ new Set();
|
|
5690
6200
|
function isDeletion(attributes, now5 = Date.now()) {
|
|
5691
|
-
for (const
|
|
5692
|
-
const equals4 =
|
|
6201
|
+
for (const part2 of attributes.split(";")) {
|
|
6202
|
+
const equals4 = part2.indexOf("=");
|
|
5693
6203
|
if (equals4 === -1) continue;
|
|
5694
|
-
const key =
|
|
5695
|
-
const value2 =
|
|
6204
|
+
const key = part2.slice(0, equals4).trim().toLowerCase();
|
|
6205
|
+
const value2 = part2.slice(equals4 + 1).trim();
|
|
5696
6206
|
if (key === "max-age" && Number(value2) <= 0) return true;
|
|
5697
6207
|
if (key === "expires" && Date.parse(value2) <= now5) return true;
|
|
5698
6208
|
}
|
|
@@ -5791,21 +6301,21 @@ import { sql as sql4 } from "kysely";
|
|
|
5791
6301
|
import { sql as sql5 } from "kysely";
|
|
5792
6302
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
5793
6303
|
import { isDeepStrictEqual } from "util";
|
|
5794
|
-
import { existsSync as existsSync5, readdirSync as
|
|
5795
|
-
import { join as
|
|
6304
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3 } from "fs";
|
|
6305
|
+
import { join as join43 } from "path";
|
|
5796
6306
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
5797
6307
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
5798
|
-
import { existsSync as
|
|
6308
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5799
6309
|
import { join as join52 } from "path";
|
|
5800
6310
|
import { existsSync as existsSync32, readdirSync as readdirSync22 } from "fs";
|
|
5801
6311
|
import { basename, extname, join as join6 } from "path";
|
|
5802
6312
|
import { pathToFileURL as pathToFileURL22 } from "url";
|
|
5803
6313
|
import { existsSync as existsSync42 } from "fs";
|
|
5804
|
-
import { isAbsolute, join as join7, relative as
|
|
6314
|
+
import { isAbsolute, join as join7, relative as relative22 } from "path";
|
|
5805
6315
|
import { pathToFileURL as pathToFileURL32 } from "url";
|
|
5806
6316
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
5807
6317
|
import { dirname as dirname3, relative as relative3, sep as sep2 } from "path";
|
|
5808
|
-
import { existsSync as existsSync52, readdirSync as
|
|
6318
|
+
import { existsSync as existsSync52, readdirSync as readdirSync32, statSync } from "fs";
|
|
5809
6319
|
import { join as join8 } from "path";
|
|
5810
6320
|
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
5811
6321
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -6217,9 +6727,9 @@ var Cursor = class _Cursor {
|
|
|
6217
6727
|
static fromEncoded(encoded) {
|
|
6218
6728
|
if (typeof encoded !== "string" || encoded === "") return void 0;
|
|
6219
6729
|
try {
|
|
6220
|
-
const
|
|
6221
|
-
if (typeof
|
|
6222
|
-
const { _pointsToNextItems: pointsToNext, ...parameters } =
|
|
6730
|
+
const json3 = JSON.parse(Buffer.from(encoded.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8"));
|
|
6731
|
+
if (typeof json3 !== "object" || json3 === null || Array.isArray(json3)) return void 0;
|
|
6732
|
+
const { _pointsToNextItems: pointsToNext, ...parameters } = json3;
|
|
6223
6733
|
if (typeof pointsToNext !== "boolean") return void 0;
|
|
6224
6734
|
return new _Cursor(parameters, pointsToNext);
|
|
6225
6735
|
} catch {
|
|
@@ -6436,9 +6946,9 @@ var Paginator = class extends AbstractPaginator {
|
|
|
6436
6946
|
};
|
|
6437
6947
|
}
|
|
6438
6948
|
};
|
|
6439
|
-
var
|
|
6949
|
+
var pad2 = (n, width = 2) => String(n).padStart(width, "0");
|
|
6440
6950
|
function formatDateTime(date) {
|
|
6441
|
-
return `${
|
|
6951
|
+
return `${pad2(date.getUTCFullYear(), 4)}-${pad2(date.getUTCMonth() + 1)}-${pad2(date.getUTCDate())} ${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}`;
|
|
6442
6952
|
}
|
|
6443
6953
|
function formatDate(date) {
|
|
6444
6954
|
return formatDateTime(date).slice(0, 10);
|
|
@@ -6470,7 +6980,7 @@ var OPERATORS = /* @__PURE__ */ new Set(["=", "<", ">", "<=", ">=", "<>", "!=",
|
|
|
6470
6980
|
var EMPTY = sql2.raw("");
|
|
6471
6981
|
var isSqlFragment = (value2) => typeof value2 === "object" && value2 !== null && typeof value2.toOperationNode === "function";
|
|
6472
6982
|
var join9 = (parts, separator = ", ") => sql2.join(parts, sql2.raw(separator));
|
|
6473
|
-
var clauses = (parts) => join9(parts.filter((
|
|
6983
|
+
var clauses = (parts) => join9(parts.filter((part2) => part2 !== void 0), " ");
|
|
6474
6984
|
function likeToGlob(pattern) {
|
|
6475
6985
|
return pattern.replaceAll("*", "[*]").replaceAll("?", "[?]").replaceAll("%", "*").replaceAll("_", "?");
|
|
6476
6986
|
}
|
|
@@ -6741,17 +7251,17 @@ function keyOf(item, alias) {
|
|
|
6741
7251
|
}
|
|
6742
7252
|
return value2;
|
|
6743
7253
|
}
|
|
6744
|
-
var
|
|
6745
|
-
function dateWhereValue(
|
|
7254
|
+
var pad22 = (value2, width = 2) => String(value2).padStart(width, "0");
|
|
7255
|
+
function dateWhereValue(part2, value2) {
|
|
6746
7256
|
if (value2 instanceof Expression) return value2;
|
|
6747
7257
|
if (value2 instanceof Date) {
|
|
6748
|
-
if (
|
|
6749
|
-
if (
|
|
6750
|
-
if (
|
|
6751
|
-
if (
|
|
7258
|
+
if (part2 === "date") return `${pad22(value2.getUTCFullYear(), 4)}-${pad22(value2.getUTCMonth() + 1)}-${pad22(value2.getUTCDate())}`;
|
|
7259
|
+
if (part2 === "time") return `${pad22(value2.getUTCHours())}:${pad22(value2.getUTCMinutes())}:${pad22(value2.getUTCSeconds())}`;
|
|
7260
|
+
if (part2 === "day") return pad22(value2.getUTCDate());
|
|
7261
|
+
if (part2 === "month") return pad22(value2.getUTCMonth() + 1);
|
|
6752
7262
|
return String(value2.getUTCFullYear());
|
|
6753
7263
|
}
|
|
6754
|
-
if (
|
|
7264
|
+
if (part2 === "day" || part2 === "month") return pad22(Math.trunc(Number(value2)) || 0);
|
|
6755
7265
|
return typeof value2 === "number" ? String(value2) : value2;
|
|
6756
7266
|
}
|
|
6757
7267
|
var isRow = (value2) => typeof value2 === "object" && value2 !== null && !(value2 instanceof Expression) && !(value2 instanceof Builder) && !Array.isArray(value2);
|
|
@@ -7310,15 +7820,15 @@ var Builder = class _Builder {
|
|
|
7310
7820
|
return [...this.connection.compile(this.grammar.compileSelect(this)).parameters];
|
|
7311
7821
|
}
|
|
7312
7822
|
clone() {
|
|
7313
|
-
const
|
|
7314
|
-
Object.assign(
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
return
|
|
7823
|
+
const copy2 = Object.create(Object.getPrototypeOf(this));
|
|
7824
|
+
Object.assign(copy2, this);
|
|
7825
|
+
copy2.columns = [...this.columns];
|
|
7826
|
+
copy2.joins = [...this.joins];
|
|
7827
|
+
copy2.wheres = [...this.wheres];
|
|
7828
|
+
copy2.groups = [...this.groups];
|
|
7829
|
+
copy2.havings = [...this.havings];
|
|
7830
|
+
copy2.orders = [...this.orders];
|
|
7831
|
+
return copy2;
|
|
7322
7832
|
}
|
|
7323
7833
|
/** A fresh builder on the same connection and table (for nested where groups). */
|
|
7324
7834
|
newQuery() {
|
|
@@ -7361,10 +7871,10 @@ var Builder = class _Builder {
|
|
|
7361
7871
|
return this;
|
|
7362
7872
|
}
|
|
7363
7873
|
/** Laravel's addDateBasedWhere(): one value means '='; the operator is checked against the whitelist. */
|
|
7364
|
-
addDateBasedWhere(
|
|
7874
|
+
addDateBasedWhere(part2, column, args, boolean) {
|
|
7365
7875
|
const [operator, value2] = args.length === 1 ? ["=", args[0]] : [args[0].toLowerCase(), args[1]];
|
|
7366
7876
|
this.grammar.operator(operator);
|
|
7367
|
-
this.wheres.push({ type: "date", part, column, operator, value: dateWhereValue(
|
|
7877
|
+
this.wheres.push({ type: "date", part: part2, column, operator, value: dateWhereValue(part2, value2), boolean });
|
|
7368
7878
|
return this;
|
|
7369
7879
|
}
|
|
7370
7880
|
pushNull(column, not, boolean) {
|
|
@@ -7931,7 +8441,7 @@ var SchemaGrammar = class {
|
|
|
7931
8441
|
return sql4`${sql4.raw(type)} check (${sql4.id(column.name)} in (${allowed}))`;
|
|
7932
8442
|
}
|
|
7933
8443
|
};
|
|
7934
|
-
var present = (parts) => parts.filter((
|
|
8444
|
+
var present = (parts) => parts.filter((part2) => part2 !== void 0);
|
|
7935
8445
|
function mergeRebuilds(steps) {
|
|
7936
8446
|
const merged = [];
|
|
7937
8447
|
for (const step of steps) {
|
|
@@ -9624,12 +10134,12 @@ var Builder2 = class extends Builder {
|
|
|
9624
10134
|
return Builder.prototype.getBindings.call(this.applyScopes());
|
|
9625
10135
|
}
|
|
9626
10136
|
clone() {
|
|
9627
|
-
const
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
return new Proxy(
|
|
10137
|
+
const copy2 = super.clone();
|
|
10138
|
+
copy2.scopes = new Map(this.scopes);
|
|
10139
|
+
copy2.removedScopes = [...this.removedScopes];
|
|
10140
|
+
copy2.eagerLoad = new Map(this.eagerLoad);
|
|
10141
|
+
copy2.selectCasts = { ...this.selectCasts };
|
|
10142
|
+
return new Proxy(copy2, handler);
|
|
9633
10143
|
}
|
|
9634
10144
|
/** Nested where groups get a builder of the same model (without global scopes), so local scopes work inside them. */
|
|
9635
10145
|
newQuery() {
|
|
@@ -9939,11 +10449,11 @@ var EloquentCollection = class extends Collection {
|
|
|
9939
10449
|
};
|
|
9940
10450
|
async function loadMissingOn(models, paths) {
|
|
9941
10451
|
for (const path of paths) {
|
|
9942
|
-
const [
|
|
9943
|
-
const missing = models.filter((model) => !model.relationLoaded(
|
|
10452
|
+
const [head2 = "", ...rest] = path.split(".");
|
|
10453
|
+
const missing = models.filter((model) => !model.relationLoaded(head2));
|
|
9944
10454
|
if (missing.length) await missing[0].newQueryWithoutRelationships().with(path).eagerLoadRelations(missing);
|
|
9945
10455
|
if (!rest.length) continue;
|
|
9946
|
-
const related = models.flatMap((model) => relatedModels(model.getRelations()[
|
|
10456
|
+
const related = models.flatMap((model) => relatedModels(model.getRelations()[head2]));
|
|
9947
10457
|
if (related.length) await loadMissingOn(related, [rest.join(".")]);
|
|
9948
10458
|
}
|
|
9949
10459
|
}
|
|
@@ -10648,9 +11158,9 @@ var Factory2 = class _Factory {
|
|
|
10648
11158
|
return new Collection(created);
|
|
10649
11159
|
}
|
|
10650
11160
|
newInstance(changes) {
|
|
10651
|
-
const
|
|
10652
|
-
Object.assign(
|
|
10653
|
-
return
|
|
11161
|
+
const copy2 = Object.create(Object.getPrototypeOf(this));
|
|
11162
|
+
Object.assign(copy2, this, changes);
|
|
11163
|
+
return copy2;
|
|
10654
11164
|
}
|
|
10655
11165
|
async makeModels(attributes) {
|
|
10656
11166
|
const models = [];
|
|
@@ -10710,9 +11220,9 @@ var Factory2 = class _Factory {
|
|
|
10710
11220
|
};
|
|
10711
11221
|
async function discoverFactories(path) {
|
|
10712
11222
|
if (!existsSync5(path)) return;
|
|
10713
|
-
for (const file of
|
|
11223
|
+
for (const file of readdirSync3(path).sort()) {
|
|
10714
11224
|
if (!/\.(ts|js|mts|mjs)$/.test(file) || /\.d\.[mc]?ts$/.test(file)) continue;
|
|
10715
|
-
const module = await import(pathToFileURL3(
|
|
11225
|
+
const module = await import(pathToFileURL3(join43(path, file)).href);
|
|
10716
11226
|
for (const exported of Object.values(module)) {
|
|
10717
11227
|
if (typeof exported === "function" && exported.prototype instanceof Factory2) {
|
|
10718
11228
|
Factory2.register(exported);
|
|
@@ -12482,9 +12992,9 @@ var MigrationCreator = class {
|
|
|
12482
12992
|
}
|
|
12483
12993
|
const stub = table === void 0 ? MIGRATION_STUB : create ? MIGRATION_CREATE_STUB : MIGRATION_UPDATE_STUB;
|
|
12484
12994
|
const file = join52(path, `${migrationDatePrefix(options.date)}_${snake}.ts`);
|
|
12485
|
-
if (
|
|
12486
|
-
|
|
12487
|
-
|
|
12995
|
+
if (existsSync23(file)) throw new LogicError(`Migration [${file}] already exists.`);
|
|
12996
|
+
mkdirSync2(path, { recursive: true });
|
|
12997
|
+
writeFileSync2(
|
|
12488
12998
|
file,
|
|
12489
12999
|
stub.replaceAll("{{ importSource }}", options.importSource ?? "@orieljs/framework").replaceAll("{{ table }}", table ?? "")
|
|
12490
13000
|
);
|
|
@@ -12879,7 +13389,7 @@ async function generateModelTypes(context) {
|
|
|
12879
13389
|
if (!context.models) return void 0;
|
|
12880
13390
|
const outputFile = context.app.basePath(join7("types", "models.d.ts"));
|
|
12881
13391
|
await new TypeGenerator({ outputFile, importSource: context.typesImportSource }).write((await context.models()).all());
|
|
12882
|
-
return
|
|
13392
|
+
return relative22(context.app.basePath(), outputFile);
|
|
12883
13393
|
}
|
|
12884
13394
|
async function writeModelTypes(context, command) {
|
|
12885
13395
|
if (context.app.isProduction() || !existsSync42(context.app.basePath("types"))) return;
|
|
@@ -12970,7 +13480,7 @@ function registerMigrationCommands(kernel, context) {
|
|
|
12970
13480
|
table: text(command.option("table")),
|
|
12971
13481
|
importSource: context.stubsImportSource
|
|
12972
13482
|
});
|
|
12973
|
-
command.info(`Migration [${
|
|
13483
|
+
command.info(`Migration [${relative22(context.app.basePath(), file)}] created successfully.`);
|
|
12974
13484
|
return 0;
|
|
12975
13485
|
});
|
|
12976
13486
|
}
|
|
@@ -13025,7 +13535,7 @@ var ModelRegistry = class {
|
|
|
13025
13535
|
function listFiles(directory) {
|
|
13026
13536
|
if (!existsSync52(directory)) return [];
|
|
13027
13537
|
const files3 = [];
|
|
13028
|
-
for (const entry of
|
|
13538
|
+
for (const entry of readdirSync32(directory).sort()) {
|
|
13029
13539
|
const path = join8(directory, entry);
|
|
13030
13540
|
if (statSync(path).isDirectory()) files3.push(...listFiles(path));
|
|
13031
13541
|
else if (MODEL_FILE.test(entry)) files3.push(path);
|
|
@@ -13505,7 +14015,7 @@ var DatabaseServiceProvider = class extends ServiceProvider {
|
|
|
13505
14015
|
};
|
|
13506
14016
|
|
|
13507
14017
|
// ../Encryption/dist/index.js
|
|
13508
|
-
import { createCipheriv, createDecipheriv, createHmac as
|
|
14018
|
+
import { createCipheriv, createDecipheriv, createHmac as createHmac5, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual5 } from "crypto";
|
|
13509
14019
|
import { readFile as readFile2, writeFile as writeFile4 } from "fs/promises";
|
|
13510
14020
|
var EncryptionFailure = class extends Error {
|
|
13511
14021
|
kind = "Server";
|
|
@@ -13541,7 +14051,7 @@ var cipherSpec = (cipher) => Object.hasOwn(CIPHERS, cipher.toLowerCase()) ? CIPH
|
|
|
13541
14051
|
function equals2(known, user) {
|
|
13542
14052
|
const a = Buffer.from(known);
|
|
13543
14053
|
const b = Buffer.from(user);
|
|
13544
|
-
return a.length === b.length &&
|
|
14054
|
+
return a.length === b.length && timingSafeEqual5(a, b);
|
|
13545
14055
|
}
|
|
13546
14056
|
function parseKey(key) {
|
|
13547
14057
|
if (typeof key !== "string" || key === "") throw new MissingAppKeyError();
|
|
@@ -13565,13 +14075,13 @@ var Encrypter2 = class _Encrypter {
|
|
|
13565
14075
|
static generateKey(cipher = "aes-256-gcm") {
|
|
13566
14076
|
const spec = cipherSpec(cipher);
|
|
13567
14077
|
if (spec === void 0) throw new UnsupportedCipherError();
|
|
13568
|
-
return new Uint8Array(
|
|
14078
|
+
return new Uint8Array(randomBytes3(spec.size));
|
|
13569
14079
|
}
|
|
13570
14080
|
encrypt(value2, serialize3 = true) {
|
|
13571
14081
|
const plain2 = serialize3 ? JSON.stringify(value2) : value2;
|
|
13572
14082
|
if (typeof plain2 !== "string") throw new EncryptError("Could not encrypt the data.");
|
|
13573
14083
|
const spec = CIPHERS[this.#cipher];
|
|
13574
|
-
const ivBytes =
|
|
14084
|
+
const ivBytes = randomBytes3(spec.ivLength);
|
|
13575
14085
|
let encrypted;
|
|
13576
14086
|
let tagBytes;
|
|
13577
14087
|
try {
|
|
@@ -13637,7 +14147,7 @@ var Encrypter2 = class _Encrypter {
|
|
|
13637
14147
|
return this;
|
|
13638
14148
|
}
|
|
13639
14149
|
#hash(iv, value2, key) {
|
|
13640
|
-
return
|
|
14150
|
+
return createHmac5("sha256", key).update(iv + value2).digest("hex");
|
|
13641
14151
|
}
|
|
13642
14152
|
#decryptWith(key, iv, value2, tag) {
|
|
13643
14153
|
try {
|
|
@@ -13717,8 +14227,8 @@ function decrypt(payload, unserialize = true) {
|
|
|
13717
14227
|
return inject(Encrypter).decrypt(payload, unserialize);
|
|
13718
14228
|
}
|
|
13719
14229
|
function previousKeys(value2) {
|
|
13720
|
-
const
|
|
13721
|
-
return
|
|
14230
|
+
const list6 = Array.isArray(value2) ? value2 : typeof value2 === "string" ? value2.split(",") : [];
|
|
14231
|
+
return list6.map((key) => String(key).trim()).filter((key) => key !== "");
|
|
13722
14232
|
}
|
|
13723
14233
|
var EncryptionServiceProvider = class extends ServiceProvider {
|
|
13724
14234
|
register() {
|
|
@@ -13921,9 +14431,9 @@ var Dispatcher2 = class {
|
|
|
13921
14431
|
throw new InvalidListenerError(label);
|
|
13922
14432
|
}
|
|
13923
14433
|
for (const event2 of Array.isArray(events) ? events : [events]) {
|
|
13924
|
-
const
|
|
13925
|
-
|
|
13926
|
-
this.listeners.set(event2,
|
|
14434
|
+
const list6 = this.listeners.get(event2) ?? [];
|
|
14435
|
+
list6.push(listener);
|
|
14436
|
+
this.listeners.set(event2, list6);
|
|
13927
14437
|
}
|
|
13928
14438
|
}
|
|
13929
14439
|
hasListeners(event2) {
|
|
@@ -13935,12 +14445,12 @@ var Dispatcher2 = class {
|
|
|
13935
14445
|
}
|
|
13936
14446
|
/** Laravel's getRawListeners(): each event's listeners as registered (closures, classes, [class, method]). A copy. */
|
|
13937
14447
|
getRawListeners() {
|
|
13938
|
-
return new Map([...this.listeners].map(([event2,
|
|
14448
|
+
return new Map([...this.listeners].map(([event2, list6]) => [event2, [...list6]]));
|
|
13939
14449
|
}
|
|
13940
14450
|
/** Introspection for the event:list command. Not part of the EventDispatcher contract. */
|
|
13941
14451
|
registeredEvents() {
|
|
13942
14452
|
const rows = [];
|
|
13943
|
-
for (const [key,
|
|
14453
|
+
for (const [key, list6] of this.listeners) rows.push({ event: nameOf(key), listeners: list6.length });
|
|
13944
14454
|
return rows.sort((a, b) => a.event.localeCompare(b.event));
|
|
13945
14455
|
}
|
|
13946
14456
|
subscribe(subscriber) {
|
|
@@ -13984,8 +14494,8 @@ var Dispatcher2 = class {
|
|
|
13984
14494
|
const exact = [...this.listeners.get(key) ?? []];
|
|
13985
14495
|
const wildcardLists = typeof key === "string" ? this.wildcardPatternsFor(key).map((pattern) => [...this.listeners.get(pattern) ?? []]) : [];
|
|
13986
14496
|
for (const listener of exact) yield { listener, wildcard: false };
|
|
13987
|
-
for (const
|
|
13988
|
-
for (const listener of
|
|
14497
|
+
for (const list6 of wildcardLists) {
|
|
14498
|
+
for (const listener of list6) yield { listener, wildcard: true };
|
|
13989
14499
|
}
|
|
13990
14500
|
}
|
|
13991
14501
|
wildcardPatternsFor(event2) {
|
|
@@ -14084,6 +14594,8 @@ import {
|
|
|
14084
14594
|
PutObjectCommand
|
|
14085
14595
|
} from "@aws-sdk/client-s3";
|
|
14086
14596
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
14597
|
+
import { copy, del, head, list as list4, put } from "@vercel/blob";
|
|
14598
|
+
import { posix as posix2 } from "path";
|
|
14087
14599
|
import { lstat, mkdir as mkdir23, symlink, unlink } from "fs/promises";
|
|
14088
14600
|
import { dirname as dirname23, relative as relative4 } from "path";
|
|
14089
14601
|
var PathTraversalError = class extends Error {
|
|
@@ -14209,6 +14721,9 @@ function joinDiskPath(directory, name) {
|
|
|
14209
14721
|
function uniqueFileName(extension) {
|
|
14210
14722
|
return /^[a-z0-9]+$/i.test(extension) ? `${Str.random(40)}.${extension}` : Str.random(40);
|
|
14211
14723
|
}
|
|
14724
|
+
function encodeKey(key) {
|
|
14725
|
+
return key.split("/").map((segment) => encodeURIComponent(segment).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`)).join("/");
|
|
14726
|
+
}
|
|
14212
14727
|
var FILE_MODE = { public: 420, private: 384 };
|
|
14213
14728
|
var DIR_MODE = { public: 493, private: 448 };
|
|
14214
14729
|
var LocalFilesystem = class {
|
|
@@ -14369,9 +14884,6 @@ var LocalFilesystem = class {
|
|
|
14369
14884
|
await chmod(full, mode);
|
|
14370
14885
|
}
|
|
14371
14886
|
};
|
|
14372
|
-
function encodeKey(key) {
|
|
14373
|
-
return key.split("/").map((segment) => encodeURIComponent(segment).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`)).join("/");
|
|
14374
|
-
}
|
|
14375
14887
|
var isNotFound = (error) => {
|
|
14376
14888
|
const name = error.name;
|
|
14377
14889
|
const status = error.$metadata?.httpStatusCode;
|
|
@@ -14605,6 +15117,176 @@ var S3Filesystem = class {
|
|
|
14605
15117
|
return { objects, prefixes: [...dirs] };
|
|
14606
15118
|
}
|
|
14607
15119
|
};
|
|
15120
|
+
var asBody = (body) => body instanceof Uint8Array && !Buffer.isBuffer(body) ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : body;
|
|
15121
|
+
function vercelBlobClient(token2) {
|
|
15122
|
+
return {
|
|
15123
|
+
put: (pathname, body, options) => put(pathname, asBody(body), { ...options, token: token2 }),
|
|
15124
|
+
head: (pathname) => head(pathname, { token: token2 }),
|
|
15125
|
+
del: (pathnames) => del(pathnames, { token: token2 }),
|
|
15126
|
+
list: (options) => list4({ ...options, token: token2 }),
|
|
15127
|
+
copy: (from, to, options) => copy(from, to, { ...options, token: token2 }),
|
|
15128
|
+
fetch: (url2) => fetch(url2)
|
|
15129
|
+
};
|
|
15130
|
+
}
|
|
15131
|
+
function blobStoreUrl(token2) {
|
|
15132
|
+
const match = /^vercel_blob_rw_([A-Za-z0-9]+)_/.exec(token2 ?? "");
|
|
15133
|
+
return match === null ? void 0 : `https://${match[1].toLowerCase()}.public.blob.vercel-storage.com`;
|
|
15134
|
+
}
|
|
15135
|
+
var CREATE = { access: "public", addRandomSuffix: false, allowOverwrite: true };
|
|
15136
|
+
var isNotFound2 = (error) => error.name === "BlobNotFoundError";
|
|
15137
|
+
var VercelBlobFilesystem = class {
|
|
15138
|
+
constructor(client, config2) {
|
|
15139
|
+
this.client = client;
|
|
15140
|
+
this.config = config2;
|
|
15141
|
+
if (config2.visibility === "private") throw new UnsupportedOperationError("visibility", "Vercel Blob disks are public");
|
|
15142
|
+
this.root = (config2.root ?? "").replace(/^\/+|\/+$/g, "");
|
|
15143
|
+
}
|
|
15144
|
+
client;
|
|
15145
|
+
config;
|
|
15146
|
+
root;
|
|
15147
|
+
/** The pathname in the store: the disk's root, then the path. */
|
|
15148
|
+
key(path) {
|
|
15149
|
+
return joinDiskPath(this.root, path.replace(/^\/+/, ""));
|
|
15150
|
+
}
|
|
15151
|
+
/** A store pathname back to the disk's path. */
|
|
15152
|
+
unroot(pathname) {
|
|
15153
|
+
return this.root === "" ? pathname : pathname.slice(this.root.length + 1);
|
|
15154
|
+
}
|
|
15155
|
+
async head(path) {
|
|
15156
|
+
try {
|
|
15157
|
+
return await this.client.head(this.key(path));
|
|
15158
|
+
} catch (error) {
|
|
15159
|
+
if (isNotFound2(error)) throw new FileNotFoundError(path);
|
|
15160
|
+
throw error;
|
|
15161
|
+
}
|
|
15162
|
+
}
|
|
15163
|
+
async exists(path) {
|
|
15164
|
+
try {
|
|
15165
|
+
await this.head(path);
|
|
15166
|
+
return true;
|
|
15167
|
+
} catch (error) {
|
|
15168
|
+
if (error instanceof FileNotFoundError) return false;
|
|
15169
|
+
throw error;
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15172
|
+
async missing(path) {
|
|
15173
|
+
return !await this.exists(path);
|
|
15174
|
+
}
|
|
15175
|
+
async read(path) {
|
|
15176
|
+
const response2 = await this.client.fetch((await this.head(path)).url);
|
|
15177
|
+
if (response2.status === 404) throw new FileNotFoundError(path);
|
|
15178
|
+
if (!response2.ok) throw new Error(`Vercel Blob answered ${response2.status} reading ${path}`);
|
|
15179
|
+
return response2;
|
|
15180
|
+
}
|
|
15181
|
+
async get(path) {
|
|
15182
|
+
return (await this.read(path)).text();
|
|
15183
|
+
}
|
|
15184
|
+
async getBytes(path) {
|
|
15185
|
+
return new Uint8Array(await (await this.read(path)).arrayBuffer());
|
|
15186
|
+
}
|
|
15187
|
+
async readStream(path) {
|
|
15188
|
+
const body = (await this.read(path)).body;
|
|
15189
|
+
return body ?? new ReadableStream({ start: (controller) => controller.close() });
|
|
15190
|
+
}
|
|
15191
|
+
async put(path, contents, options) {
|
|
15192
|
+
await this.store(path, contents, options?.mimeType ?? mimeFromExtension(posix2.extname(path)), options);
|
|
15193
|
+
return true;
|
|
15194
|
+
}
|
|
15195
|
+
async putFile(directory, file, options) {
|
|
15196
|
+
return this.putFileAs(directory, file, uniqueFileName(file.extension()), options);
|
|
15197
|
+
}
|
|
15198
|
+
/** Sends the upload's own type (its extension()), as the s3 disk does, never the type the client claimed. */
|
|
15199
|
+
async putFileAs(directory, file, name, options) {
|
|
15200
|
+
const path = joinDiskPath(directory, name);
|
|
15201
|
+
await this.store(path, new Uint8Array(await file.arrayBuffer()), options?.mimeType ?? mimeFromExtension(file.extension()), options);
|
|
15202
|
+
return path;
|
|
15203
|
+
}
|
|
15204
|
+
async store(path, body, contentType, options) {
|
|
15205
|
+
if (options?.visibility === "private") throw new UnsupportedOperationError("put", "Vercel Blob disks are public");
|
|
15206
|
+
await this.client.put(this.key(path), body, { ...CREATE, ...contentType === void 0 ? {} : { contentType } });
|
|
15207
|
+
}
|
|
15208
|
+
async delete(paths) {
|
|
15209
|
+
const list23 = Array.isArray(paths) ? paths : [paths];
|
|
15210
|
+
if (list23.length > 0) await this.client.del(list23.map((path) => this.key(path)));
|
|
15211
|
+
return true;
|
|
15212
|
+
}
|
|
15213
|
+
async copy(from, to) {
|
|
15214
|
+
try {
|
|
15215
|
+
await this.client.copy(this.key(from), this.key(to), CREATE);
|
|
15216
|
+
} catch (error) {
|
|
15217
|
+
if (isNotFound2(error)) throw new FileNotFoundError(from);
|
|
15218
|
+
throw error;
|
|
15219
|
+
}
|
|
15220
|
+
return true;
|
|
15221
|
+
}
|
|
15222
|
+
async move(from, to) {
|
|
15223
|
+
await this.copy(from, to);
|
|
15224
|
+
await this.delete(from);
|
|
15225
|
+
return true;
|
|
15226
|
+
}
|
|
15227
|
+
async size(path) {
|
|
15228
|
+
return (await this.head(path)).size;
|
|
15229
|
+
}
|
|
15230
|
+
async lastModified(path) {
|
|
15231
|
+
return new Date((await this.head(path)).uploadedAt);
|
|
15232
|
+
}
|
|
15233
|
+
async mimeType(path) {
|
|
15234
|
+
return (await this.head(path)).contentType || void 0;
|
|
15235
|
+
}
|
|
15236
|
+
url(path) {
|
|
15237
|
+
const base = this.config.url ?? blobStoreUrl(this.config.token);
|
|
15238
|
+
if (base === void 0) throw new UnsupportedOperationError("url", "set the disk's token (BLOB_READ_WRITE_TOKEN) or its url");
|
|
15239
|
+
return `${base.replace(/\/+$/, "")}/${encodeKey(this.key(path))}`;
|
|
15240
|
+
}
|
|
15241
|
+
/** A public store's files need no signature: the public URL. */
|
|
15242
|
+
async temporaryUrl(path, _expiresAt) {
|
|
15243
|
+
return this.url(path);
|
|
15244
|
+
}
|
|
15245
|
+
async files(directory = "", recursive = false) {
|
|
15246
|
+
const { blobs } = await this.list(directory, recursive);
|
|
15247
|
+
return blobs.map((pathname) => this.unroot(pathname)).sort();
|
|
15248
|
+
}
|
|
15249
|
+
async directories(directory = "", recursive = false) {
|
|
15250
|
+
if (!recursive) return (await this.list(directory, false)).folders.map((folder) => this.unroot(folder.replace(/\/+$/, ""))).sort();
|
|
15251
|
+
const found = /* @__PURE__ */ new Set();
|
|
15252
|
+
const start3 = this.key(directory).replace(/\/+$/, "");
|
|
15253
|
+
for (const pathname of (await this.list(directory, true)).blobs) {
|
|
15254
|
+
const parts = pathname.split("/").slice(0, -1);
|
|
15255
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
15256
|
+
const folder = parts.slice(0, i).join("/");
|
|
15257
|
+
if (folder.length > start3.length && (start3 === "" || folder.startsWith(`${start3}/`))) found.add(this.unroot(folder));
|
|
15258
|
+
}
|
|
15259
|
+
}
|
|
15260
|
+
return [...found].sort();
|
|
15261
|
+
}
|
|
15262
|
+
/** Blob folders exist through their files, so there's nothing to create. */
|
|
15263
|
+
async makeDirectory(_path) {
|
|
15264
|
+
return true;
|
|
15265
|
+
}
|
|
15266
|
+
async deleteDirectory(path) {
|
|
15267
|
+
const { blobs } = await this.list(path, true);
|
|
15268
|
+
for (let i = 0; i < blobs.length; i += 1e3) await this.client.del(blobs.slice(i, i + 1e3));
|
|
15269
|
+
return true;
|
|
15270
|
+
}
|
|
15271
|
+
path(_path) {
|
|
15272
|
+
throw new UnsupportedOperationError("path", "vercel-blob disks have no local filesystem path");
|
|
15273
|
+
}
|
|
15274
|
+
/** Every page of a listing: the store's pathnames, and in folded mode its folders (`prefix/name/`). */
|
|
15275
|
+
async list(directory, recursive) {
|
|
15276
|
+
const base = this.key(directory).replace(/\/+$/, "");
|
|
15277
|
+
const prefix = base === "" ? void 0 : `${base}/`;
|
|
15278
|
+
const blobs = [];
|
|
15279
|
+
const folders = [];
|
|
15280
|
+
let cursor;
|
|
15281
|
+
do {
|
|
15282
|
+
const page = await this.client.list({ prefix, cursor, mode: recursive ? "expanded" : "folded" });
|
|
15283
|
+
blobs.push(...page.blobs.map((blob) => blob.pathname));
|
|
15284
|
+
folders.push(...page.folders ?? []);
|
|
15285
|
+
cursor = page.hasMore ? page.cursor : void 0;
|
|
15286
|
+
} while (cursor !== void 0);
|
|
15287
|
+
return { blobs, folders };
|
|
15288
|
+
}
|
|
15289
|
+
};
|
|
14608
15290
|
var FilesystemManager2 = class {
|
|
14609
15291
|
constructor(container) {
|
|
14610
15292
|
this.container = container;
|
|
@@ -14617,6 +15299,15 @@ var FilesystemManager2 = class {
|
|
|
14617
15299
|
})
|
|
14618
15300
|
);
|
|
14619
15301
|
this.drivers.set("s3", (config2) => new S3Filesystem(this.buildS3Client(config2), this.s3Config(config2)));
|
|
15302
|
+
this.drivers.set("vercel-blob", (config2) => {
|
|
15303
|
+
const token2 = config2.token ?? process.env.BLOB_READ_WRITE_TOKEN;
|
|
15304
|
+
return new VercelBlobFilesystem(vercelBlobClient(token2), {
|
|
15305
|
+
token: token2,
|
|
15306
|
+
url: config2.url,
|
|
15307
|
+
root: config2.root,
|
|
15308
|
+
visibility: config2.visibility
|
|
15309
|
+
});
|
|
15310
|
+
});
|
|
14620
15311
|
}
|
|
14621
15312
|
container;
|
|
14622
15313
|
disks = /* @__PURE__ */ new Map();
|
|
@@ -14980,11 +15671,11 @@ var HashingServiceProvider = class extends ServiceProvider {
|
|
|
14980
15671
|
import { existsSync as existsSync7, readdirSync as readdirSync5 } from "fs";
|
|
14981
15672
|
import { join as join11, relative as relative5, sep as sep4 } from "path";
|
|
14982
15673
|
import { createHash as createHash7 } from "crypto";
|
|
14983
|
-
import { existsSync as
|
|
15674
|
+
import { existsSync as existsSync24, readFileSync } from "fs";
|
|
14984
15675
|
import { spawn as spawn2 } from "child_process";
|
|
14985
15676
|
import { existsSync as existsSync33 } from "fs";
|
|
14986
15677
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
14987
|
-
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join24, relative as
|
|
15678
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join24, relative as relative23 } from "path";
|
|
14988
15679
|
import { existsSync as existsSync43, readFileSync as readFileSync2 } from "fs";
|
|
14989
15680
|
var Header = {
|
|
14990
15681
|
INERTIA: "X-Inertia",
|
|
@@ -15862,7 +16553,7 @@ var InertiaMiddleware = class {
|
|
|
15862
16553
|
if (typeof assetUrl === "string" && assetUrl !== "") return md5(assetUrl);
|
|
15863
16554
|
const app2 = this.container.make(Application);
|
|
15864
16555
|
const manifest = app2.publicPath("build/manifest.json");
|
|
15865
|
-
return
|
|
16556
|
+
return existsSync24(manifest) ? md5(readFileSync(manifest)) : null;
|
|
15866
16557
|
}
|
|
15867
16558
|
/** Props shared with every page. Override and spread `await super.share(request)` into your own. */
|
|
15868
16559
|
async share(request2) {
|
|
@@ -15918,8 +16609,8 @@ var InertiaMiddleware = class {
|
|
|
15918
16609
|
bag,
|
|
15919
16610
|
Object.fromEntries(
|
|
15920
16611
|
Object.entries(fields ?? {}).map(([field, messages]) => {
|
|
15921
|
-
const
|
|
15922
|
-
return [field, this.withAllErrors ?
|
|
16612
|
+
const list6 = Array.isArray(messages) ? messages.map(String) : [String(messages)];
|
|
16613
|
+
return [field, this.withAllErrors ? list6 : list6[0] ?? ""];
|
|
15923
16614
|
})
|
|
15924
16615
|
)
|
|
15925
16616
|
])
|
|
@@ -16143,7 +16834,7 @@ export class {{ class }} extends InertiaMiddleware {
|
|
|
16143
16834
|
}
|
|
16144
16835
|
}
|
|
16145
16836
|
`;
|
|
16146
|
-
var DEFAULT_EXCEPT_ROUTES = ["live.*"];
|
|
16837
|
+
var DEFAULT_EXCEPT_ROUTES = ["live.*", "vercel.*"];
|
|
16147
16838
|
async function loadPageTypes() {
|
|
16148
16839
|
const specifier = "@orieljs/vite/types";
|
|
16149
16840
|
try {
|
|
@@ -16172,7 +16863,7 @@ function registerInertiaCommands(kernel, app2, gateway, options = {}) {
|
|
|
16172
16863
|
await writeFile6(output, renderRoutesModule(table));
|
|
16173
16864
|
await mkdir5(dirname5(server), { recursive: true });
|
|
16174
16865
|
await writeFile6(server, renderServerRouteTypes(routeTable(app2.make(Router).getRoutes())));
|
|
16175
|
-
this.info(`Route types for ${Object.keys(table).length} named routes written to ${
|
|
16866
|
+
this.info(`Route types for ${Object.keys(table).length} named routes written to ${relative23(app2.basePath(), output)}.`);
|
|
16176
16867
|
return 0;
|
|
16177
16868
|
}
|
|
16178
16869
|
).purpose("Generate the route table and route name types for the frontend");
|
|
@@ -16185,7 +16876,7 @@ function registerInertiaCommands(kernel, app2, gateway, options = {}) {
|
|
|
16185
16876
|
const output = absolute(optionText(this.option("output")) ?? config2("inertia.types.pages", "resources/js/oriel/pages.d.ts"));
|
|
16186
16877
|
const result = await module.generatePageTypes({ root: app2.basePath(), output });
|
|
16187
16878
|
for (const warning of result.warnings) this.warn(warning);
|
|
16188
|
-
this.info(`Page types for ${result.pages} pages written to ${
|
|
16879
|
+
this.info(`Page types for ${result.pages} pages written to ${relative23(app2.basePath(), result.output)}.`);
|
|
16189
16880
|
return 0;
|
|
16190
16881
|
}).purpose("Generate TypeScript types for the Inertia page props");
|
|
16191
16882
|
kernel.command("inertia:middleware {name=HandleInertiaRequests : The middleware class name} {--force : Overwrite an existing file}", async function() {
|
|
@@ -16196,12 +16887,12 @@ function registerInertiaCommands(kernel, app2, gateway, options = {}) {
|
|
|
16196
16887
|
}
|
|
16197
16888
|
const file = app2.appPath(join24("Http", "Middleware", `${name}.ts`));
|
|
16198
16889
|
if (existsSync33(file) && this.option("force") !== true) {
|
|
16199
|
-
this.error(`Middleware [${
|
|
16890
|
+
this.error(`Middleware [${relative23(app2.basePath(), file)}] already exists.`);
|
|
16200
16891
|
return 1;
|
|
16201
16892
|
}
|
|
16202
16893
|
await mkdir5(dirname5(file), { recursive: true });
|
|
16203
16894
|
await writeFile6(file, MIDDLEWARE_STUB2.replace("{{ class }}", () => name));
|
|
16204
|
-
this.info(`Middleware [${
|
|
16895
|
+
this.info(`Middleware [${relative23(app2.basePath(), file)}] created successfully.`);
|
|
16205
16896
|
return 0;
|
|
16206
16897
|
}).purpose("Create a new Inertia middleware");
|
|
16207
16898
|
kernel.command("inertia:start-ssr {--runtime= : The runtime that runs the SSR bundle (node or bun)}", async function() {
|
|
@@ -16400,7 +17091,7 @@ var InertiaServiceProvider = class extends ServiceProvider {
|
|
|
16400
17091
|
|
|
16401
17092
|
// ../Log/dist/index.js
|
|
16402
17093
|
import pino2 from "pino";
|
|
16403
|
-
import { createWriteStream as createWriteStream2, mkdirSync as
|
|
17094
|
+
import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync3, readdirSync as readdirSync6, rmSync } from "fs";
|
|
16404
17095
|
import { basename as basename2, dirname as dirname6, extname as extname3, join as join12 } from "path";
|
|
16405
17096
|
import { Writable } from "stream";
|
|
16406
17097
|
import "pino";
|
|
@@ -16529,7 +17220,7 @@ var DailyRotatingStream = class extends Writable {
|
|
|
16529
17220
|
this.now = now5;
|
|
16530
17221
|
this.dir = dirname6(basePath);
|
|
16531
17222
|
this.stem = basename2(basePath, extname3(basePath) || void 0);
|
|
16532
|
-
|
|
17223
|
+
mkdirSync3(this.dir, { recursive: true });
|
|
16533
17224
|
}
|
|
16534
17225
|
days;
|
|
16535
17226
|
now;
|
|
@@ -16940,8 +17631,8 @@ function normalizeAddress(recipient) {
|
|
|
16940
17631
|
}
|
|
16941
17632
|
function normalizeAddresses(recipients) {
|
|
16942
17633
|
if (recipients === void 0) return [];
|
|
16943
|
-
const
|
|
16944
|
-
return
|
|
17634
|
+
const list6 = Array.isArray(recipients) ? recipients : [recipients];
|
|
17635
|
+
return list6.map(normalizeAddress);
|
|
16945
17636
|
}
|
|
16946
17637
|
function formatAddress(address) {
|
|
16947
17638
|
return address.name ? `${address.name} <${address.address}>` : address.address;
|
|
@@ -17242,7 +17933,7 @@ function copyShouldQueueFields(target, source) {
|
|
|
17242
17933
|
}
|
|
17243
17934
|
function recipientsHash(to = [], cc = [], bcc = []) {
|
|
17244
17935
|
if (to.length + cc.length + bcc.length === 0) return void 0;
|
|
17245
|
-
const addresses = (
|
|
17936
|
+
const addresses = (list6) => list6.map((recipient) => recipient.address);
|
|
17246
17937
|
const recipients = JSON.stringify({ to: addresses(to), cc: addresses(cc), bcc: addresses(bcc) });
|
|
17247
17938
|
return createHash8("sha1").update(recipients).digest("hex");
|
|
17248
17939
|
}
|
|
@@ -17963,9 +18654,9 @@ function isQueueableNotification(notification) {
|
|
|
17963
18654
|
}
|
|
17964
18655
|
function forNotifiable(notification) {
|
|
17965
18656
|
if (notification.id) return notification;
|
|
17966
|
-
const
|
|
17967
|
-
|
|
17968
|
-
return
|
|
18657
|
+
const copy2 = Object.assign(Object.create(Object.getPrototypeOf(notification)), notification);
|
|
18658
|
+
copy2.id = randomUUID23();
|
|
18659
|
+
return copy2;
|
|
17969
18660
|
}
|
|
17970
18661
|
function toNotifiableList(notifiables) {
|
|
17971
18662
|
if (Array.isArray(notifiables)) return notifiables;
|
|
@@ -17996,12 +18687,12 @@ var Dispatcher3 = class {
|
|
|
17996
18687
|
return channel;
|
|
17997
18688
|
}
|
|
17998
18689
|
async send(notifiables, notification) {
|
|
17999
|
-
const
|
|
18000
|
-
if (
|
|
18690
|
+
const list6 = toNotifiableList(notifiables);
|
|
18691
|
+
if (list6.length === 0) return;
|
|
18001
18692
|
if (isQueueableNotification(notification)) {
|
|
18002
18693
|
if (!this.container.bound(BusDispatcher)) throw new NotificationQueueUnavailableError();
|
|
18003
18694
|
const bus = this.container.make(BusDispatcher);
|
|
18004
|
-
for (const notifiable of
|
|
18695
|
+
for (const notifiable of list6) {
|
|
18005
18696
|
const own = forNotifiable(notification);
|
|
18006
18697
|
for (const channel of notification.via(notifiable)) {
|
|
18007
18698
|
await bus.dispatch(new SendQueuedNotifications([notifiable], own, [channel]));
|
|
@@ -18009,11 +18700,11 @@ var Dispatcher3 = class {
|
|
|
18009
18700
|
}
|
|
18010
18701
|
return;
|
|
18011
18702
|
}
|
|
18012
|
-
await this.sendNow(
|
|
18703
|
+
await this.sendNow(list6, notification);
|
|
18013
18704
|
}
|
|
18014
18705
|
async sendNow(notifiables, notification, channels) {
|
|
18015
|
-
const
|
|
18016
|
-
for (const notifiable of
|
|
18706
|
+
const list6 = toNotifiableList(notifiables);
|
|
18707
|
+
for (const notifiable of list6) {
|
|
18017
18708
|
const own = forNotifiable(notification);
|
|
18018
18709
|
const names = channels ?? notification.via(notifiable);
|
|
18019
18710
|
for (const name of names) {
|
|
@@ -18440,7 +19131,7 @@ async function restoreValue(value2, registry2) {
|
|
|
18440
19131
|
}
|
|
18441
19132
|
function normalizeBackoff(backoff) {
|
|
18442
19133
|
if (typeof backoff === "number") return [backoff];
|
|
18443
|
-
if (typeof backoff === "string" && backoff !== "") return backoff.split(",").map((
|
|
19134
|
+
if (typeof backoff === "string" && backoff !== "") return backoff.split(",").map((part2) => Number(part2.trim()));
|
|
18444
19135
|
if (Array.isArray(backoff)) return backoff.map(Number);
|
|
18445
19136
|
return void 0;
|
|
18446
19137
|
}
|
|
@@ -19991,7 +20682,7 @@ function discoverJobs(directory, registry2) {
|
|
|
19991
20682
|
return discoverClasses(directory, registry2, isJobClass);
|
|
19992
20683
|
}
|
|
19993
20684
|
var text3 = (value2) => optionalString2(value2) || void 0;
|
|
19994
|
-
var
|
|
20685
|
+
var list5 = (value2) => Array.isArray(value2) ? value2.map(String) : text3(value2) ? [String(value2)] : [];
|
|
19995
20686
|
var number2 = (value2, fallback) => {
|
|
19996
20687
|
const parsed = Number(value2);
|
|
19997
20688
|
return value2 === void 0 || value2 === null || value2 === "" || Number.isNaN(parsed) ? fallback : parsed;
|
|
@@ -20116,7 +20807,7 @@ function registerQueueCommands(kernel, context) {
|
|
|
20116
20807
|
'queue:retry {id?* : The ID of the failed job or "all" to retry all jobs} {--queue= : Retry all of the failed jobs for the specified queue}',
|
|
20117
20808
|
"Retry a failed queue job",
|
|
20118
20809
|
async (out) => {
|
|
20119
|
-
const ids = await retryableIds(
|
|
20810
|
+
const ids = await retryableIds(list5(out.argument("id")), text3(out.option("queue")));
|
|
20120
20811
|
if (ids.length === 0) {
|
|
20121
20812
|
out.info("No retryable jobs found.");
|
|
20122
20813
|
return 0;
|
|
@@ -20229,7 +20920,7 @@ var QueueServiceProvider = class extends ServiceProvider {
|
|
|
20229
20920
|
};
|
|
20230
20921
|
|
|
20231
20922
|
// ../Routing/dist/index.js
|
|
20232
|
-
import { createHmac as
|
|
20923
|
+
import { createHmac as createHmac6, timingSafeEqual as timingSafeEqual6 } from "crypto";
|
|
20233
20924
|
import { pathToFileURL as pathToFileURL7 } from "url";
|
|
20234
20925
|
import { createHash as createHash10 } from "crypto";
|
|
20235
20926
|
import { createHash as createHash23 } from "crypto";
|
|
@@ -20690,7 +21381,7 @@ async function dispatchRoute(router, request2, container) {
|
|
|
20690
21381
|
}
|
|
20691
21382
|
var ALL_VERBS = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
|
|
20692
21383
|
function mergeGroup(parent, child) {
|
|
20693
|
-
const prefix = [parent.prefix, child.prefix].map((
|
|
21384
|
+
const prefix = [parent.prefix, child.prefix].map((part2) => (part2 ?? "").replace(/^\/+|\/+$/g, "")).filter((part2) => part2 !== "").join("/");
|
|
20694
21385
|
const merged = {
|
|
20695
21386
|
middleware: [...parent.middleware ?? [], ...child.middleware ?? []]
|
|
20696
21387
|
};
|
|
@@ -20845,7 +21536,7 @@ var BaseRouter = class {
|
|
|
20845
21536
|
}
|
|
20846
21537
|
const prefix = (group.prefix ?? "").replace(/^\/+|\/+$/g, "");
|
|
20847
21538
|
const path = uri.replace(/^\/+|\/+$/g, "");
|
|
20848
|
-
const route2 = new RouteDefinition(verbs, [prefix, path].filter((
|
|
21539
|
+
const route2 = new RouteDefinition(verbs, [prefix, path].filter((part2) => part2 !== "").join("/"), action, {
|
|
20849
21540
|
...group.as !== void 0 && { namePrefix: group.as },
|
|
20850
21541
|
...group.middleware !== void 0 && { middleware: group.middleware },
|
|
20851
21542
|
...group.controller !== void 0 && { controller: group.controller },
|
|
@@ -20994,20 +21685,20 @@ var BaseRouter = class {
|
|
|
20994
21685
|
return this;
|
|
20995
21686
|
}
|
|
20996
21687
|
pushMiddlewareToGroup(group, middleware) {
|
|
20997
|
-
const
|
|
20998
|
-
if (!
|
|
21688
|
+
const list6 = this.#groups[group] ??= [];
|
|
21689
|
+
if (!list6.includes(middleware)) list6.push(middleware);
|
|
20999
21690
|
return this;
|
|
21000
21691
|
}
|
|
21001
21692
|
prependMiddlewareToGroup(group, middleware) {
|
|
21002
|
-
const
|
|
21003
|
-
if (!
|
|
21693
|
+
const list6 = this.#groups[group] ??= [];
|
|
21694
|
+
if (!list6.includes(middleware)) list6.unshift(middleware);
|
|
21004
21695
|
return this;
|
|
21005
21696
|
}
|
|
21006
21697
|
hasMiddlewareGroup(name) {
|
|
21007
21698
|
return Object.hasOwn(this.#groups, name);
|
|
21008
21699
|
}
|
|
21009
21700
|
getMiddlewareGroups() {
|
|
21010
|
-
return Object.fromEntries(Object.entries(this.#groups).map(([name,
|
|
21701
|
+
return Object.fromEntries(Object.entries(this.#groups).map(([name, list6]) => [name, [...list6]]));
|
|
21011
21702
|
}
|
|
21012
21703
|
/** Every route, in registration order. */
|
|
21013
21704
|
getRoutes() {
|
|
@@ -21070,12 +21761,12 @@ function routeKey(value2, field) {
|
|
|
21070
21761
|
return typeof model.getRouteKey === "function" ? model.getRouteKey() : value2;
|
|
21071
21762
|
}
|
|
21072
21763
|
function sign(url2, key) {
|
|
21073
|
-
return
|
|
21764
|
+
return createHmac6("sha256", key).update(url2).digest("hex");
|
|
21074
21765
|
}
|
|
21075
21766
|
function safeEquals(expected, given) {
|
|
21076
21767
|
const a = Buffer.from(expected);
|
|
21077
21768
|
const b = Buffer.from(given);
|
|
21078
|
-
return a.length === b.length &&
|
|
21769
|
+
return a.length === b.length && timingSafeEqual6(a, b);
|
|
21079
21770
|
}
|
|
21080
21771
|
function availableAt(expiration) {
|
|
21081
21772
|
return expiration instanceof Date ? Math.floor(expiration.getTime() / 1e3) : Math.floor(Date.now() / 1e3) + expiration;
|
|
@@ -21306,9 +21997,9 @@ function withHeaders2(response2, headers) {
|
|
|
21306
21997
|
for (const [name, value2] of Object.entries(headers)) response2.headers.set(name, value2);
|
|
21307
21998
|
return response2;
|
|
21308
21999
|
} catch {
|
|
21309
|
-
const
|
|
21310
|
-
for (const [name, value2] of Object.entries(headers))
|
|
21311
|
-
return new Response(response2.body, { status: response2.status, statusText: response2.statusText, headers:
|
|
22000
|
+
const copy2 = new Headers(response2.headers);
|
|
22001
|
+
for (const [name, value2] of Object.entries(headers)) copy2.set(name, value2);
|
|
22002
|
+
return new Response(response2.body, { status: response2.status, statusText: response2.statusText, headers: copy2 });
|
|
21312
22003
|
}
|
|
21313
22004
|
}
|
|
21314
22005
|
var ThrottleRequests = class {
|
|
@@ -21446,8 +22137,8 @@ var RoutingServiceProvider = class extends ServiceProvider {
|
|
|
21446
22137
|
import { mkdir as mkdir6, readdir as readdir3, readFile as readFile4, rename as rename3, rm as rm3, stat as stat3, writeFile as writeFile7 } from "fs/promises";
|
|
21447
22138
|
import { join as join14 } from "path";
|
|
21448
22139
|
import { Redis as Redis3 } from "ioredis";
|
|
21449
|
-
import { randomInt } from "crypto";
|
|
21450
|
-
import { timingSafeEqual as
|
|
22140
|
+
import { randomInt as randomInt3 } from "crypto";
|
|
22141
|
+
import { timingSafeEqual as timingSafeEqual7 } from "crypto";
|
|
21451
22142
|
var unique = (values) => [...new Set(values)];
|
|
21452
22143
|
var Store = class {
|
|
21453
22144
|
#id = "";
|
|
@@ -21515,8 +22206,8 @@ var Store = class {
|
|
|
21515
22206
|
for (const [name, item] of Object.entries(values)) Arr.set(this.#attributes, name, item);
|
|
21516
22207
|
}
|
|
21517
22208
|
push(key, value2) {
|
|
21518
|
-
const
|
|
21519
|
-
this.put(key, [...Array.isArray(
|
|
22209
|
+
const list6 = this.get(key, []);
|
|
22210
|
+
this.put(key, [...Array.isArray(list6) ? list6 : [list6], value2]);
|
|
21520
22211
|
}
|
|
21521
22212
|
increment(key, amount = 1) {
|
|
21522
22213
|
const value2 = Number(this.get(key, 0)) + amount;
|
|
@@ -22004,7 +22695,7 @@ var StartSession = class {
|
|
|
22004
22695
|
/** Laravel's lottery: on average lottery[0] requests out of lottery[1] clean up expired sessions. */
|
|
22005
22696
|
async #collectGarbage(session2, config2) {
|
|
22006
22697
|
const [chances, outOf] = config2.lottery ?? [2, 100];
|
|
22007
|
-
if (session2.getHandler !== void 0 &&
|
|
22698
|
+
if (session2.getHandler !== void 0 && randomInt3(1, outOf + 1) <= chances) {
|
|
22008
22699
|
await session2.getHandler().gc((config2.lifetime ?? 120) * 60);
|
|
22009
22700
|
}
|
|
22010
22701
|
}
|
|
@@ -22016,7 +22707,7 @@ var StartSession = class {
|
|
|
22016
22707
|
function equals3(known, user) {
|
|
22017
22708
|
const a = Buffer.from(known);
|
|
22018
22709
|
const b = Buffer.from(user);
|
|
22019
|
-
return a.length === b.length &&
|
|
22710
|
+
return a.length === b.length && timingSafeEqual7(a, b);
|
|
22020
22711
|
}
|
|
22021
22712
|
var VerifyCsrfToken = class {
|
|
22022
22713
|
/** URIs (wildcards allowed) excluded from the check, in addition to config('http.csrf_except'). */
|
|
@@ -22190,7 +22881,7 @@ var ValidationErrorImpl = class extends Error {
|
|
|
22190
22881
|
var ValidationException = class _ValidationException extends ValidationErrorImpl {
|
|
22191
22882
|
/** Messages per field, each a string or a list. */
|
|
22192
22883
|
static withMessages(messages) {
|
|
22193
|
-
const errors2 = Object.fromEntries(Object.entries(messages).map(([field,
|
|
22884
|
+
const errors2 = Object.fromEntries(Object.entries(messages).map(([field, list6]) => [field, typeof list6 === "string" ? [list6] : [...list6]]));
|
|
22194
22885
|
return new _ValidationException(errors2);
|
|
22195
22886
|
}
|
|
22196
22887
|
constructor(errors2, errorBag = "default", redirectTo2) {
|
|
@@ -23442,11 +24133,11 @@ var urlPatterns = /* @__PURE__ */ new Map();
|
|
|
23442
24133
|
function isUrl(value2, protocols) {
|
|
23443
24134
|
if (typeof value2 !== "string") return false;
|
|
23444
24135
|
if (protocols.length === 0) return URL_RE.test(value2);
|
|
23445
|
-
const
|
|
23446
|
-
let pattern = urlPatterns.get(
|
|
24136
|
+
const list6 = protocols.join("|");
|
|
24137
|
+
let pattern = urlPatterns.get(list6);
|
|
23447
24138
|
if (pattern === void 0) {
|
|
23448
24139
|
try {
|
|
23449
|
-
pattern = urlPattern(
|
|
24140
|
+
pattern = urlPattern(list6);
|
|
23450
24141
|
} catch (error) {
|
|
23451
24142
|
throw new InvalidRuleParameterError(
|
|
23452
24143
|
"url",
|
|
@@ -23454,7 +24145,7 @@ function isUrl(value2, protocols) {
|
|
|
23454
24145
|
error instanceof Error ? error.message : String(error)
|
|
23455
24146
|
);
|
|
23456
24147
|
}
|
|
23457
|
-
urlPatterns.set(
|
|
24148
|
+
urlPatterns.set(list6, pattern);
|
|
23458
24149
|
}
|
|
23459
24150
|
return pattern.test(value2);
|
|
23460
24151
|
}
|
|
@@ -23880,11 +24571,11 @@ var sizeRules = {
|
|
|
23880
24571
|
in: { validate: (ctx) => isListed(ctx.value, ctx.parameters, ctx.ruleNames), messageKey: () => "in" },
|
|
23881
24572
|
not_in: { validate: (ctx) => !isListed(ctx.value, ctx.parameters, ctx.ruleNames), messageKey: () => "not_in" }
|
|
23882
24573
|
};
|
|
23883
|
-
function isListed(value2,
|
|
24574
|
+
function isListed(value2, list6, ruleNames) {
|
|
23884
24575
|
if (Array.isArray(value2) && ruleNames.includes("array")) {
|
|
23885
|
-
return value2.every((item) => (typeof item !== "object" || item === null) &&
|
|
24576
|
+
return value2.every((item) => (typeof item !== "object" || item === null) && list6.includes(String(item)));
|
|
23886
24577
|
}
|
|
23887
|
-
return (typeof value2 !== "object" || value2 === null) &&
|
|
24578
|
+
return (typeof value2 !== "object" || value2 === null) && list6.includes(String(value2));
|
|
23888
24579
|
}
|
|
23889
24580
|
var REGIONS = ["Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", "Australia", "Europe", "Indian", "Pacific"];
|
|
23890
24581
|
var ZONE_RE = /^[A-Z][A-Za-z_-]*(?:\/[A-Z][A-Za-z0-9_+-]*)+$/;
|
|
@@ -24033,13 +24724,13 @@ var PasswordRule = class _PasswordRule {
|
|
|
24033
24724
|
if (this.#numbers && !/\p{N}/u.test(value2)) say("password.numbers");
|
|
24034
24725
|
}
|
|
24035
24726
|
#copy() {
|
|
24036
|
-
const
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
return
|
|
24727
|
+
const copy2 = new _PasswordRule(this.#min);
|
|
24728
|
+
copy2.#max = this.#max;
|
|
24729
|
+
copy2.#letters = this.#letters;
|
|
24730
|
+
copy2.#mixedCase = this.#mixedCase;
|
|
24731
|
+
copy2.#numbers = this.#numbers;
|
|
24732
|
+
copy2.#symbols = this.#symbols;
|
|
24733
|
+
return copy2;
|
|
24043
24734
|
}
|
|
24044
24735
|
};
|
|
24045
24736
|
var BuiltinRuleObject = class {
|
|
@@ -24344,17 +25035,17 @@ function buildNested(flat) {
|
|
|
24344
25035
|
const created = /* @__PURE__ */ new Set([root]);
|
|
24345
25036
|
for (const [path, value2] of Object.entries(flat)) {
|
|
24346
25037
|
const parts = path.split(".");
|
|
24347
|
-
if (parts.some((
|
|
25038
|
+
if (parts.some((part2) => UNSAFE_KEYS.has(part2))) continue;
|
|
24348
25039
|
let current = root;
|
|
24349
|
-
for (const
|
|
24350
|
-
const existing = Object.hasOwn(current,
|
|
25040
|
+
for (const part2 of parts.slice(0, -1)) {
|
|
25041
|
+
const existing = Object.hasOwn(current, part2) ? current[part2] : void 0;
|
|
24351
25042
|
if (typeof existing === "object" && existing !== null && created.has(existing)) {
|
|
24352
25043
|
current = existing;
|
|
24353
25044
|
continue;
|
|
24354
25045
|
}
|
|
24355
25046
|
const container = cloneContainer(existing);
|
|
24356
25047
|
created.add(container);
|
|
24357
|
-
current[
|
|
25048
|
+
current[part2] = container;
|
|
24358
25049
|
current = container;
|
|
24359
25050
|
}
|
|
24360
25051
|
current[parts.at(-1)] = value2;
|
|
@@ -24383,16 +25074,16 @@ function withoutPaths(data, paths) {
|
|
|
24383
25074
|
const parts = path.split(".");
|
|
24384
25075
|
const leaf = parts.pop();
|
|
24385
25076
|
let node = root;
|
|
24386
|
-
for (const
|
|
24387
|
-
const child = Object.hasOwn(node,
|
|
25077
|
+
for (const part2 of parts) {
|
|
25078
|
+
const child = Object.hasOwn(node, part2) ? node[part2] : void 0;
|
|
24388
25079
|
if (!isPlainContainer(child)) {
|
|
24389
25080
|
node = void 0;
|
|
24390
25081
|
break;
|
|
24391
25082
|
}
|
|
24392
|
-
const
|
|
24393
|
-
copies.add(
|
|
24394
|
-
node[
|
|
24395
|
-
node =
|
|
25083
|
+
const copy2 = copies.has(child) ? child : Array.isArray(child) ? [...child] : { ...child };
|
|
25084
|
+
copies.add(copy2);
|
|
25085
|
+
node[part2] = copy2;
|
|
25086
|
+
node = copy2;
|
|
24396
25087
|
}
|
|
24397
25088
|
if (node === void 0 || !Object.hasOwn(node, leaf)) continue;
|
|
24398
25089
|
if (Array.isArray(node)) node[Number(leaf)] = EXCLUDED;
|
|
@@ -24676,6 +25367,52 @@ var ValidationServiceProvider = class extends ServiceProvider {
|
|
|
24676
25367
|
import { existsSync as existsSync9, readdirSync as readdirSync8, readFileSync as readFileSync3, realpathSync, statSync as statSync4 } from "fs";
|
|
24677
25368
|
import { cp, mkdir as mkdir7, rm as rm4, writeFile as writeFile8 } from "fs/promises";
|
|
24678
25369
|
import { basename as basename3, dirname as dirname7, join as join15, relative as relative6, resolve as resolve4 } from "path";
|
|
25370
|
+
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
25371
|
+
import { timingSafeEqual as timingSafeEqual8 } from "crypto";
|
|
25372
|
+
import { CronExpressionParser as CronExpressionParser3 } from "cron-parser";
|
|
25373
|
+
var SCHEDULE_PATH = "/_oriel/schedule";
|
|
25374
|
+
var DEFAULT_CRON = "0 3 * * *";
|
|
25375
|
+
var LAST_RUN = "oriel:vercel:schedule:last";
|
|
25376
|
+
var LOCK = "oriel:vercel:schedule";
|
|
25377
|
+
var json2 = (body, status = 200) => Response.json(body, { status, headers: { "cache-control": "no-store" } });
|
|
25378
|
+
function authorized(header, secret) {
|
|
25379
|
+
const expected = Buffer.from(`Bearer ${secret}`);
|
|
25380
|
+
const given = Buffer.from(header ?? "");
|
|
25381
|
+
return given.length === expected.length && timingSafeEqual8(given, expected);
|
|
25382
|
+
}
|
|
25383
|
+
function previousTrigger(expression, now5) {
|
|
25384
|
+
const parsed = CronExpressionParser3.parse(expression, { currentDate: new Date(now5.getTime() + 6e4), tz: "UTC" });
|
|
25385
|
+
parsed.prev();
|
|
25386
|
+
return parsed.prev().toDate();
|
|
25387
|
+
}
|
|
25388
|
+
async function runVercelSchedule(authorization, options = {}) {
|
|
25389
|
+
const secret = process.env.CRON_SECRET;
|
|
25390
|
+
if (!secret) return new Response("Not Found", { status: 404 });
|
|
25391
|
+
if (!authorized(authorization, secret)) return json2({ error: "Unauthorized" }, 401);
|
|
25392
|
+
const cache2 = inject(CacheManager);
|
|
25393
|
+
const lock = cache2.lock(LOCK, 900);
|
|
25394
|
+
if (!await lock.get()) return json2({ ran: 0, failed: 0, skipped: "busy" });
|
|
25395
|
+
try {
|
|
25396
|
+
await inject(ConsoleKernel).bootstrap();
|
|
25397
|
+
const now5 = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
25398
|
+
const last = await cache2.get(LAST_RUN);
|
|
25399
|
+
const from = last === void 0 ? previousTrigger(options.trigger ?? process.env.ORIEL_VERCEL_CRON ?? DEFAULT_CRON, now5) : new Date(last);
|
|
25400
|
+
const due = inject(Schedule).dueBetween(from, now5);
|
|
25401
|
+
let failed = 0;
|
|
25402
|
+
for (const event2 of due) {
|
|
25403
|
+
try {
|
|
25404
|
+
await event2.run();
|
|
25405
|
+
} catch (error) {
|
|
25406
|
+
failed++;
|
|
25407
|
+
await inject(ExceptionHandler).report(error);
|
|
25408
|
+
}
|
|
25409
|
+
}
|
|
25410
|
+
await cache2.forever(LAST_RUN, now5.toISOString());
|
|
25411
|
+
return json2({ ran: due.length, failed }, failed > 0 ? 500 : 200);
|
|
25412
|
+
} finally {
|
|
25413
|
+
await lock.release();
|
|
25414
|
+
}
|
|
25415
|
+
}
|
|
24679
25416
|
var LAUNCHER = `// Oriel on Vercel: boots the app once per function instance and hands it each request.
|
|
24680
25417
|
import { fileURLToPath } from 'node:url'
|
|
24681
25418
|
|
|
@@ -24758,8 +25495,10 @@ async function buildVercelOutput(app2, options = {}) {
|
|
|
24758
25495
|
const vcConfig = { runtime: options.runtime ?? "nodejs22.x", handler: "index.mjs", launcherType: "Nodejs", shouldAddHelpers: false, supportsResponseStreaming: true };
|
|
24759
25496
|
await writeFile8(join15(fn, ".vc-config.json"), `${JSON.stringify(vcConfig, null, 2)}
|
|
24760
25497
|
`);
|
|
25498
|
+
const { crons, cronWarnings } = await scheduleCron(app2, options.cron);
|
|
24761
25499
|
const config2 = {
|
|
24762
25500
|
version: 3,
|
|
25501
|
+
...crons === void 0 ? {} : { crons },
|
|
24763
25502
|
routes: [
|
|
24764
25503
|
// The built assets' names change with their content, so they're cached for a year, as serve() does.
|
|
24765
25504
|
{ src: "^/build/assets/(.*)$", headers: { "cache-control": "public, max-age=31536000, immutable" }, continue: true },
|
|
@@ -24778,7 +25517,26 @@ async function buildVercelOutput(app2, options = {}) {
|
|
|
24778
25517
|
const warnings = platform === "linux" ? [] : [
|
|
24779
25518
|
`Built on ${PLATFORM_NAMES[platform] ?? platform}: the function's native packages (such as esbuild, which tsx needs) only run on this kind of machine, and Vercel's functions run on Linux. Let Vercel run the build, or build on Linux.`
|
|
24780
25519
|
];
|
|
24781
|
-
return { functionBytes, staticFiles: countFiles(staticFolder), warnings };
|
|
25520
|
+
return { functionBytes, staticFiles: countFiles(staticFolder), warnings: [...warnings, ...cronWarnings] };
|
|
25521
|
+
}
|
|
25522
|
+
async function scheduleCron(app2, cron) {
|
|
25523
|
+
if (cron === false) return { cronWarnings: [] };
|
|
25524
|
+
if (app2.bound(ConsoleKernel)) await app2.make(ConsoleKernel).bootstrap();
|
|
25525
|
+
if (!app2.bound(Schedule)) return { cronWarnings: [] };
|
|
25526
|
+
const schedule = app2.make(Schedule);
|
|
25527
|
+
if (schedule.events().length === 0) return { cronWarnings: [] };
|
|
25528
|
+
const expression = cron ?? DEFAULT_CRON;
|
|
25529
|
+
const fires = CronExpressionParser2.parse(expression, { tz: "UTC" });
|
|
25530
|
+
const start3 = fires.next().toDate();
|
|
25531
|
+
const end = fires.next().toDate();
|
|
25532
|
+
const middle = new Date((start3.getTime() + end.getTime()) / 2);
|
|
25533
|
+
const later = new Set(schedule.dueBetween(middle, end));
|
|
25534
|
+
const often = schedule.dueBetween(start3, middle).filter((event2) => later.has(event2));
|
|
25535
|
+
const plan = cron === void 0 ? " (once a day on Vercel's Hobby plan)" : "";
|
|
25536
|
+
return {
|
|
25537
|
+
crons: [{ path: SCHEDULE_PATH, schedule: expression }],
|
|
25538
|
+
cronWarnings: often.map((event2) => `"${event2.getSummaryForDisplay()}" (${event2.expression}) is due more often than the cron "${expression}", so it runs once per trigger${plan}.`)
|
|
25539
|
+
};
|
|
24782
25540
|
}
|
|
24783
25541
|
async function copyDependencies(base, fn) {
|
|
24784
25542
|
const manifest = JSON.parse(readFileSync3(join15(base, "package.json"), "utf8"));
|
|
@@ -24823,11 +25581,19 @@ var megabytes = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
|
24823
25581
|
var megabytes2 = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
24824
25582
|
var VercelServiceProvider = class extends ServiceProvider {
|
|
24825
25583
|
boot() {
|
|
25584
|
+
if (this.app.bound(Router)) {
|
|
25585
|
+
this.app.make(Router).get(SCHEDULE_PATH, () => {
|
|
25586
|
+
const request2 = inject(HttpRequest);
|
|
25587
|
+
return runVercelSchedule(request2.header("authorization"), { trigger: request2.header("x-vercel-cron-schedule") });
|
|
25588
|
+
}).name("vercel.schedule");
|
|
25589
|
+
}
|
|
24826
25590
|
if (!this.app.bound(ConsoleKernel)) return;
|
|
24827
25591
|
const app2 = this.app;
|
|
24828
|
-
this.app.make(ConsoleKernel).command("vercel:build", async function() {
|
|
25592
|
+
this.app.make(ConsoleKernel).command("vercel:build {--cron= : When Vercel calls the scheduler, as a cron expression (UTC), or false for never}", async function() {
|
|
24829
25593
|
try {
|
|
24830
|
-
const
|
|
25594
|
+
const option = this.option("cron");
|
|
25595
|
+
const cron = typeof option === "string" && option !== "" ? option === "false" ? false : option : void 0;
|
|
25596
|
+
const { staticFiles, functionBytes, warnings } = await buildVercelOutput(app2, { cron });
|
|
24831
25597
|
const files3 = staticFiles === 1 ? "1 static file" : `${staticFiles} static files`;
|
|
24832
25598
|
this.info(`Wrote .vercel/output: ${files3}, a function of ${megabytes2(functionBytes)}.`);
|
|
24833
25599
|
for (const warning of warnings) this.warn(warning);
|
|
@@ -24851,7 +25617,7 @@ import { parse as parse2 } from "dotenv";
|
|
|
24851
25617
|
var SECRET = /key|secret|password|token|credential|salt|private/i;
|
|
24852
25618
|
var CREDENTIALS = /^[a-z][a-z0-9+.-]*:\/\/[^/?#\s]*@/i;
|
|
24853
25619
|
var byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
24854
|
-
var
|
|
25620
|
+
var posix3 = (path) => path.split(sep5).join("/");
|
|
24855
25621
|
function commandSection(all) {
|
|
24856
25622
|
return Object.entries(all).map(([name, command]) => {
|
|
24857
25623
|
const signature = parseSignature(command.signature);
|
|
@@ -24893,8 +25659,8 @@ function viewSection(paths, hints, extensions, basePath) {
|
|
|
24893
25659
|
const extension = extensions.find((candidate) => item.name.endsWith(`.${candidate}`));
|
|
24894
25660
|
if (extension === void 0) continue;
|
|
24895
25661
|
const file = join16(item.parentPath, item.name);
|
|
24896
|
-
const name = prefix +
|
|
24897
|
-
if (!views.has(name)) views.set(name,
|
|
25662
|
+
const name = prefix + posix3(relative7(root, file)).slice(0, -(extension.length + 1)).split("/").join(".");
|
|
25663
|
+
if (!views.has(name)) views.set(name, posix3(relative7(basePath, file)));
|
|
24898
25664
|
}
|
|
24899
25665
|
};
|
|
24900
25666
|
for (const path of paths) collect2(path, "");
|
|
@@ -24903,7 +25669,7 @@ function viewSection(paths, hints, extensions, basePath) {
|
|
|
24903
25669
|
}
|
|
24904
25670
|
|
|
24905
25671
|
// ide/IdeInfoCommand.ts
|
|
24906
|
-
var
|
|
25672
|
+
var posix4 = (path) => path.split(sep6).join("/");
|
|
24907
25673
|
var read = (file) => existsSync11(file) ? readFileSync4(file, "utf8") : null;
|
|
24908
25674
|
var IdeInfoCommand = class extends Command {
|
|
24909
25675
|
signature = "ide:info {--pretty : Indent the JSON}";
|
|
@@ -24935,7 +25701,7 @@ function describe(app2, container) {
|
|
|
24935
25701
|
const basePath = app2.basePath();
|
|
24936
25702
|
const config2 = optional(ConfigRepository);
|
|
24937
25703
|
const get = (key, fallback) => config2 ? config2.get(key, fallback) : fallback;
|
|
24938
|
-
const relativeTo = (file) =>
|
|
25704
|
+
const relativeTo = (file) => posix4(relative8(basePath, file));
|
|
24939
25705
|
const configDirectory = app2.configPath();
|
|
24940
25706
|
const configFiles = existsSync11(configDirectory) ? readdirSync10(configDirectory).filter((file) => /\.(ts|js|mjs)$/.test(file)).map((file) => relativeTo(join17(configDirectory, file))) : [];
|
|
24941
25707
|
const factory = optional(ViewFactory);
|
|
@@ -25369,6 +26135,7 @@ export {
|
|
|
25369
26135
|
QueuedMailSender,
|
|
25370
26136
|
RESTART_CACHE_KEY,
|
|
25371
26137
|
RateLimiter2 as RateLimiter,
|
|
26138
|
+
RecoveryCodeUsed,
|
|
25372
26139
|
RedirectIfAuthenticated,
|
|
25373
26140
|
RedisJob,
|
|
25374
26141
|
RedisLock,
|
|
@@ -25395,6 +26162,7 @@ export {
|
|
|
25395
26162
|
Rule,
|
|
25396
26163
|
S3Filesystem,
|
|
25397
26164
|
SCHEDULE_INTERRUPT_KEY,
|
|
26165
|
+
SESSION_URI,
|
|
25398
26166
|
SSR_RENDER_FAILED,
|
|
25399
26167
|
STORAGE_LINK_SIGNATURE,
|
|
25400
26168
|
Schedule5 as Schedule,
|
|
@@ -25436,6 +26204,7 @@ export {
|
|
|
25436
26204
|
SyncJob,
|
|
25437
26205
|
SyncQueue,
|
|
25438
26206
|
TEMPORARY_UPLOAD_DEFAULTS,
|
|
26207
|
+
TWO_FACTOR_COLUMNS_MIGRATION,
|
|
25439
26208
|
TagsNotSupportedError,
|
|
25440
26209
|
TemporaryFileNotFoundError,
|
|
25441
26210
|
TemporaryUploadedFile,
|
|
@@ -25451,6 +26220,15 @@ export {
|
|
|
25451
26220
|
TokenRepository,
|
|
25452
26221
|
TrimStrings,
|
|
25453
26222
|
TsxView,
|
|
26223
|
+
TwoFactor,
|
|
26224
|
+
TwoFactorAppDisabled,
|
|
26225
|
+
TwoFactorAppEnabled,
|
|
26226
|
+
TwoFactorAuthenticated,
|
|
26227
|
+
TwoFactorChallenge,
|
|
26228
|
+
TwoFactorChallenged,
|
|
26229
|
+
TwoFactorCode,
|
|
26230
|
+
TwoFactorFailed,
|
|
26231
|
+
TwoFactorManager,
|
|
25454
26232
|
TypeGenerator,
|
|
25455
26233
|
UNIQUE,
|
|
25456
26234
|
UNIQUE_LOCK,
|
|
@@ -25486,6 +26264,7 @@ export {
|
|
|
25486
26264
|
ValidationServiceProvider,
|
|
25487
26265
|
Validator,
|
|
25488
26266
|
ValidatorImpl,
|
|
26267
|
+
VercelBlobFilesystem,
|
|
25489
26268
|
VercelServiceProvider,
|
|
25490
26269
|
Verified,
|
|
25491
26270
|
VerifyCsrfToken,
|