@stacksjs/defaults 0.75.16 → 0.75.18
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.
|
@@ -77,9 +77,7 @@ bcryptEncode(value, rounds?) // bcrypt hash
|
|
|
77
77
|
bcryptVerify(value, hash) // bcrypt verify
|
|
78
78
|
argon2Encode(value, options?) // argon2 hash
|
|
79
79
|
argon2Verify(value, hash) // argon2 verify
|
|
80
|
-
md5Encode(value) // MD5 (legacy only)
|
|
81
80
|
base64Encode(value) // base64
|
|
82
|
-
base64Verify(value, encoded) // base64 compare
|
|
83
81
|
```
|
|
84
82
|
|
|
85
83
|
## HashMakeOptions
|
|
@@ -124,6 +122,7 @@ interface HashMakeOptions {
|
|
|
124
122
|
- APP_KEY is used for `encrypt()`/`decrypt()` — generate via `buddy key:generate`
|
|
125
123
|
- APP_KEY format is colon-separated (validated during deployment)
|
|
126
124
|
- Argon2 requires more memory/time but is more resistant to GPU attacks
|
|
127
|
-
-
|
|
125
|
+
- Base64 is encoding, not hashing. Never use `base64Encode()` for credentials.
|
|
126
|
+
- MD5 and base64 password verification are intentionally absent from the main security API.
|
|
128
127
|
- The firewall config is used by cloud deployment for WAF rules
|
|
129
128
|
- Rate limiting is per-minute per-IP (500 default)
|
|
@@ -309,7 +309,7 @@ Convention over configuration, while staying wholly configurable. _No more boile
|
|
|
309
309
|
- 🔤 **Spell Checker** _be notified once there are typos_
|
|
310
310
|
- 🛠️ **Essential Utilities** _powers at your fingertips. Collections, STX composables, and more_
|
|
311
311
|
- 👥 **Team Management** _manage your team & their permissions_
|
|
312
|
-
- 🧪 **Streamlined Testing** _unit &
|
|
312
|
+
- 🧪 **Streamlined Testing** _unit, feature & component tests powered by Bun & very-happy-dom_
|
|
313
313
|
|
|
314
314
|
No matter whether you are a beginner or an expert, the approachable Stacks design allows you to learn at your own pace, using our thorough documentation covering every aspect of the framework. Stacks is extremely beginner & expert-friendly.
|
|
315
315
|
|
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.18",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.27",
|
|
58
|
-
"@stacksjs/mobile": "^0.75.
|
|
58
|
+
"@stacksjs/mobile": "^0.75.18",
|
|
59
59
|
"@stacksjs/sanitizer": "^0.2.113",
|
|
60
60
|
"ts-qr-codes": "^0.1.8"
|
|
61
61
|
}
|
|
@@ -6,6 +6,49 @@
|
|
|
6
6
|
* automatically be injected into the Bun process.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Every global the runtime defined before this file assigned any.
|
|
11
|
+
*
|
|
12
|
+
* Taken first, at module load, because the auto-import loops below write
|
|
13
|
+
* framework exports onto globalThis, and a name the runtime already owns must
|
|
14
|
+
* never be one of them. A hand-kept list of such names is how `Worker` slipped
|
|
15
|
+
* through: `@stacksjs/queue` exports a queue `Worker`, it replaced Bun's Web
|
|
16
|
+
* Worker in every Stacks process, and anything calling `new Worker(url)` built
|
|
17
|
+
* a queue worker instead - stx's image warm-up failed silently, an app's own
|
|
18
|
+
* syntax-highlighter worker broke. `reportError` from `@stacksjs/validation`
|
|
19
|
+
* was overwriting the web `reportError` the same way.
|
|
20
|
+
*/
|
|
21
|
+
const HOST_GLOBALS: ReadonlySet<string> = new Set(Object.getOwnPropertyNames(globalThis))
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Names never auto-imported onto globalThis, even when the runtime does not
|
|
25
|
+
* define them: another runtime's globals (`window`, `Deno`), module-scope
|
|
26
|
+
* bindings (`require`, `__dirname`), and the ones listed long before the
|
|
27
|
+
* snapshot above existed, kept so nothing that relied on them changes.
|
|
28
|
+
*/
|
|
29
|
+
const RESERVED_GLOBALS: ReadonlySet<string> = new Set([
|
|
30
|
+
'process', 'globalThis', 'global', 'window', 'self',
|
|
31
|
+
'console', 'require', 'module', 'exports', '__dirname', '__filename',
|
|
32
|
+
'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
33
|
+
'setImmediate', 'clearImmediate', 'queueMicrotask',
|
|
34
|
+
'fetch', 'Request', 'Response', 'Headers', 'URL', 'URLSearchParams',
|
|
35
|
+
'TextEncoder', 'TextDecoder', 'Blob', 'File', 'FormData',
|
|
36
|
+
'crypto', 'performance', 'navigator', 'location',
|
|
37
|
+
'Promise', 'Symbol', 'Proxy', 'Reflect', 'WeakMap', 'WeakSet', 'Map', 'Set',
|
|
38
|
+
'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'RegExp', 'Error',
|
|
39
|
+
'JSON', 'Math', 'Intl', 'eval', 'isNaN', 'isFinite', 'parseInt', 'parseFloat',
|
|
40
|
+
'encodeURI', 'encodeURIComponent', 'decodeURI', 'decodeURIComponent',
|
|
41
|
+
'Bun', 'Deno', 'Node',
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Whether an auto-import must leave `name` alone: the runtime already defines
|
|
46
|
+
* it, or it is reserved. `hostGlobals` is injectable for tests.
|
|
47
|
+
*/
|
|
48
|
+
export function isProtectedGlobal(name: string, hostGlobals: ReadonlySet<string> = HOST_GLOBALS): boolean {
|
|
49
|
+
return RESERVED_GLOBALS.has(name) || hostGlobals.has(name)
|
|
50
|
+
}
|
|
51
|
+
|
|
9
52
|
// Skip preloader for fast CLI commands (e.g. `buddy dev`, `buddy --version`) to
|
|
10
53
|
// maximize startup speed. We must NOT skip when running a server script directly
|
|
11
54
|
// (e.g. `bun --watch storage/framework/core/actions/src/dev/api.ts`) — Bun
|
|
@@ -214,22 +257,6 @@ export async function loadAutoImports() {
|
|
|
214
257
|
const path = await import('../../../core/path/src/index.ts')
|
|
215
258
|
.catch(() => import(pathPackage))
|
|
216
259
|
|
|
217
|
-
// CRITICAL: Never overwrite these built-in globals
|
|
218
|
-
const protectedGlobals = new Set([
|
|
219
|
-
'process', 'globalThis', 'global', 'window', 'self',
|
|
220
|
-
'console', 'require', 'module', 'exports', '__dirname', '__filename',
|
|
221
|
-
'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
222
|
-
'setImmediate', 'clearImmediate', 'queueMicrotask',
|
|
223
|
-
'fetch', 'Request', 'Response', 'Headers', 'URL', 'URLSearchParams',
|
|
224
|
-
'TextEncoder', 'TextDecoder', 'Blob', 'File', 'FormData',
|
|
225
|
-
'crypto', 'performance', 'navigator', 'location',
|
|
226
|
-
'Promise', 'Symbol', 'Proxy', 'Reflect', 'WeakMap', 'WeakSet', 'Map', 'Set',
|
|
227
|
-
'Array', 'Object', 'String', 'Number', 'Boolean', 'Date', 'RegExp', 'Error',
|
|
228
|
-
'JSON', 'Math', 'Intl', 'eval', 'isNaN', 'isFinite', 'parseInt', 'parseFloat',
|
|
229
|
-
'encodeURI', 'encodeURIComponent', 'decodeURI', 'decodeURIComponent',
|
|
230
|
-
'Bun', 'Deno', 'Node',
|
|
231
|
-
])
|
|
232
|
-
|
|
233
260
|
// 1. Load Stacks framework packages into globalThis
|
|
234
261
|
const stacksPackages = [
|
|
235
262
|
'@stacksjs/actions',
|
|
@@ -275,8 +302,8 @@ export async function loadAutoImports() {
|
|
|
275
302
|
try {
|
|
276
303
|
const module = await import(pkg)
|
|
277
304
|
for (const [name, value] of Object.entries(module)) {
|
|
278
|
-
// Skip default exports and
|
|
279
|
-
if (name === 'default' ||
|
|
305
|
+
// Skip default exports and anything the runtime already owns
|
|
306
|
+
if (name === 'default' || isProtectedGlobal(name)) continue
|
|
280
307
|
if (typeof value !== 'undefined') {
|
|
281
308
|
Reflect.set(globalThis, name, value)
|
|
282
309
|
}
|
|
@@ -295,8 +322,8 @@ export async function loadAutoImports() {
|
|
|
295
322
|
try {
|
|
296
323
|
const module = await import(file)
|
|
297
324
|
for (const [name, value] of Object.entries(module)) {
|
|
298
|
-
// Skip default exports and
|
|
299
|
-
if (name === 'default' ||
|
|
325
|
+
// Skip default exports and anything the runtime already owns
|
|
326
|
+
if (name === 'default' || isProtectedGlobal(name)) continue
|
|
300
327
|
if (typeof value !== 'undefined') {
|
|
301
328
|
Reflect.set(globalThis, name, value)
|
|
302
329
|
}
|
|
@@ -326,7 +353,7 @@ export async function loadAutoImports() {
|
|
|
326
353
|
if (file.endsWith('.d.ts') || file.endsWith('/index.ts')) continue
|
|
327
354
|
|
|
328
355
|
const modelName = file.split('/').pop()?.replace('.ts', '') || ''
|
|
329
|
-
if (!modelName || loadedModels.has(modelName) ||
|
|
356
|
+
if (!modelName || loadedModels.has(modelName) || isProtectedGlobal(modelName)) continue
|
|
330
357
|
|
|
331
358
|
try {
|
|
332
359
|
const module = await import(file)
|
|
@@ -357,7 +384,7 @@ export async function loadAutoImports() {
|
|
|
357
384
|
if (file.endsWith('.d.ts') || file.endsWith('/index.ts')) continue
|
|
358
385
|
|
|
359
386
|
const jobName = file.split('/').pop()?.replace('.ts', '') || ''
|
|
360
|
-
if (!jobName || loadedJobs.has(jobName) ||
|
|
387
|
+
if (!jobName || loadedJobs.has(jobName) || isProtectedGlobal(jobName)) continue
|
|
361
388
|
|
|
362
389
|
try {
|
|
363
390
|
const module = await import(file)
|
|
@@ -392,7 +419,7 @@ export async function loadAutoImports() {
|
|
|
392
419
|
if (file.endsWith('.d.ts') || file.endsWith('/index.ts')) continue
|
|
393
420
|
|
|
394
421
|
const controllerName = file.split('/').pop()?.replace('.ts', '') || ''
|
|
395
|
-
if (!controllerName || loadedControllers.has(controllerName) ||
|
|
422
|
+
if (!controllerName || loadedControllers.has(controllerName) || isProtectedGlobal(controllerName)) continue
|
|
396
423
|
|
|
397
424
|
try {
|
|
398
425
|
const module = await import(file)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
import type { OAuthAuthorizationConsentView } from '@stacksjs/auth'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The default OAuth consent screen. Applications may replace this file at
|
|
6
|
+
* `resources/views/auth/oauth/consent.stx` without changing the provider.
|
|
7
|
+
*
|
|
8
|
+
* The action supplies display-only identity labels and the consent model. The
|
|
9
|
+
* form returns only middleware-owned CSRF proof, the opaque request handle,
|
|
10
|
+
* and the user's decision. Client callback, PKCE, subject, and workspace
|
|
11
|
+
* authority remain in the server-side authorization-request record.
|
|
12
|
+
*/
|
|
13
|
+
declare const consent: OAuthAuthorizationConsentView
|
|
14
|
+
declare const signedInIdentity: string
|
|
15
|
+
declare const selectedWorkspace: string | null
|
|
16
|
+
declare const consentAction: string
|
|
17
|
+
declare const csrfToken: string
|
|
18
|
+
|
|
19
|
+
const clientTypeLabel = consent.client.type === 'public' ? 'Public client' : 'Confidential client'
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<main class="flex min-h-dvh items-center justify-center bg-neutral-50 px-4 py-10 text-neutral-950 dark:bg-neutral-950 dark:text-white sm:px-6">
|
|
23
|
+
<section class="w-full max-w-xl" aria-labelledby="oauth-consent-title">
|
|
24
|
+
<div class="mb-5 flex items-center justify-center gap-2 text-neutral-500 text-sm dark:text-neutral-400">
|
|
25
|
+
<i class="h-5 w-5 i-hugeicons-shield-key" aria-hidden="true"></i>
|
|
26
|
+
<span>Secure authorization</span>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="overflow-hidden border border-neutral-200 rounded-2xl bg-white shadow-sm dark:border-neutral-800 dark:bg-neutral-900">
|
|
30
|
+
<header class="border-neutral-200 border-b px-6 py-6 dark:border-neutral-800 sm:px-8">
|
|
31
|
+
<p class="font-medium text-neutral-500 text-sm dark:text-neutral-400">Authorization request</p>
|
|
32
|
+
<h1 id="oauth-consent-title" class="mt-2 font-semibold text-2xl tracking-tight sm:text-3xl">
|
|
33
|
+
{{ consent.client.name }} wants to connect
|
|
34
|
+
</h1>
|
|
35
|
+
<p class="mt-3 text-neutral-600 text-sm leading-6 dark:text-neutral-300">
|
|
36
|
+
Review exactly what this application can access before you continue.
|
|
37
|
+
</p>
|
|
38
|
+
</header>
|
|
39
|
+
|
|
40
|
+
<div class="space-y-7 px-6 py-6 sm:px-8">
|
|
41
|
+
<dl class="grid gap-4 rounded-xl bg-neutral-50 p-4 dark:bg-neutral-800/60">
|
|
42
|
+
<div class="flex gap-3 items-start">
|
|
43
|
+
<i class="mt-0.5 h-5 w-5 shrink-0 text-neutral-500 i-hugeicons-user-circle dark:text-neutral-400" aria-hidden="true"></i>
|
|
44
|
+
<div>
|
|
45
|
+
<dt class="font-medium text-neutral-500 text-xs tracking-wide uppercase dark:text-neutral-400">Signed in as</dt>
|
|
46
|
+
<dd class="mt-1 font-medium text-neutral-900 text-sm dark:text-white">{{ signedInIdentity }}</dd>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
@if (selectedWorkspace)
|
|
51
|
+
<div class="flex gap-3 items-start">
|
|
52
|
+
<i class="mt-0.5 h-5 w-5 shrink-0 text-neutral-500 i-hugeicons-building-03 dark:text-neutral-400" aria-hidden="true"></i>
|
|
53
|
+
<div>
|
|
54
|
+
<dt class="font-medium text-neutral-500 text-xs tracking-wide uppercase dark:text-neutral-400">Workspace</dt>
|
|
55
|
+
<dd class="mt-1 font-medium text-neutral-900 text-sm dark:text-white">{{ selectedWorkspace }}</dd>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
@endif
|
|
59
|
+
|
|
60
|
+
<div class="flex gap-3 items-start">
|
|
61
|
+
<i class="mt-0.5 h-5 w-5 shrink-0 text-neutral-500 i-hugeicons-computer-programming-01 dark:text-neutral-400" aria-hidden="true"></i>
|
|
62
|
+
<div>
|
|
63
|
+
<dt class="font-medium text-neutral-500 text-xs tracking-wide uppercase dark:text-neutral-400">Application type</dt>
|
|
64
|
+
<dd class="mt-1 font-medium text-neutral-900 text-sm dark:text-white">{{ clientTypeLabel }}</dd>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</dl>
|
|
68
|
+
|
|
69
|
+
<section aria-labelledby="oauth-permissions-title">
|
|
70
|
+
<h2 id="oauth-permissions-title" class="font-semibold text-base">Permissions requested</h2>
|
|
71
|
+
<ul class="mt-3 divide-y divide-neutral-200 border border-neutral-200 rounded-xl dark:divide-neutral-800 dark:border-neutral-800">
|
|
72
|
+
@foreach (consent.permissions as permission)
|
|
73
|
+
<li class="flex gap-3 px-4 py-4 items-start">
|
|
74
|
+
<i class="mt-0.5 h-5 w-5 shrink-0 text-emerald-600 i-hugeicons-key-01 dark:text-emerald-400" aria-hidden="true"></i>
|
|
75
|
+
<div>
|
|
76
|
+
<p class="font-medium text-neutral-900 text-sm dark:text-white">{{ permission.description }}</p>
|
|
77
|
+
<p class="mt-1 font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ permission.name }}</p>
|
|
78
|
+
</div>
|
|
79
|
+
</li>
|
|
80
|
+
@endforeach
|
|
81
|
+
</ul>
|
|
82
|
+
</section>
|
|
83
|
+
|
|
84
|
+
@if (consent.resources.length)
|
|
85
|
+
<section aria-labelledby="oauth-resources-title">
|
|
86
|
+
<h2 id="oauth-resources-title" class="font-semibold text-base">Data access</h2>
|
|
87
|
+
<ul class="mt-3 space-y-2">
|
|
88
|
+
@foreach (consent.resources as resource)
|
|
89
|
+
<li class="flex gap-3 rounded-xl bg-neutral-50 px-4 py-3 items-start dark:bg-neutral-800/60">
|
|
90
|
+
<i class="mt-0.5 h-5 w-5 shrink-0 text-neutral-500 i-hugeicons-database-02 dark:text-neutral-400" aria-hidden="true"></i>
|
|
91
|
+
<div>
|
|
92
|
+
<p class="font-medium text-neutral-900 text-sm dark:text-white">{{ resource.name }}</p>
|
|
93
|
+
@if (resource.description)
|
|
94
|
+
<p class="mt-1 text-neutral-600 text-sm leading-5 dark:text-neutral-300">{{ resource.description }}</p>
|
|
95
|
+
@endif
|
|
96
|
+
</div>
|
|
97
|
+
</li>
|
|
98
|
+
@endforeach
|
|
99
|
+
</ul>
|
|
100
|
+
</section>
|
|
101
|
+
@endif
|
|
102
|
+
|
|
103
|
+
<p class="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-amber-950 text-sm leading-5 dark:border-amber-900/70 dark:bg-amber-950/40 dark:text-amber-100">
|
|
104
|
+
Only approve if you recognize this application and trust it with the permissions listed above.
|
|
105
|
+
</p>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<form method="POST" action="{{ consentAction }}" class="flex flex-col-reverse gap-3 border-neutral-200 border-t px-6 py-5 dark:border-neutral-800 sm:flex-row sm:justify-end sm:px-8">
|
|
109
|
+
<input type="hidden" name="_token" value="{{ csrfToken }}">
|
|
110
|
+
<input type="hidden" name="request_id" value="{{ consent.requestId }}">
|
|
111
|
+
<button
|
|
112
|
+
type="submit"
|
|
113
|
+
name="decision"
|
|
114
|
+
value="deny"
|
|
115
|
+
class="inline-flex min-h-11 items-center justify-center rounded-lg border border-neutral-300 px-5 py-2.5 font-semibold text-neutral-800 text-sm hover:bg-neutral-50 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 dark:border-neutral-700 dark:text-neutral-100 dark:hover:bg-neutral-800 dark:focus:ring-offset-neutral-900"
|
|
116
|
+
>
|
|
117
|
+
Deny
|
|
118
|
+
</button>
|
|
119
|
+
<button
|
|
120
|
+
type="submit"
|
|
121
|
+
name="decision"
|
|
122
|
+
value="approve"
|
|
123
|
+
class="inline-flex min-h-11 items-center justify-center rounded-lg bg-neutral-950 px-5 py-2.5 font-semibold text-sm text-white hover:bg-neutral-800 focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 dark:bg-white dark:text-neutral-950 dark:hover:bg-neutral-200 dark:focus:ring-white dark:focus:ring-offset-neutral-900"
|
|
124
|
+
>
|
|
125
|
+
Allow access
|
|
126
|
+
</button>
|
|
127
|
+
</form>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<p class="mt-5 text-center text-neutral-500 text-xs leading-5 dark:text-neutral-400">
|
|
131
|
+
You can revoke this connection later from your connected applications settings.
|
|
132
|
+
</p>
|
|
133
|
+
</section>
|
|
134
|
+
</main>
|