zitejs 0.9.91 → 0.9.93
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/cjs/auth/index.js +11 -3
- package/dist/cjs/auth/index.test.js +18 -0
- package/dist/cjs/auth/server.d.ts +66 -0
- package/dist/cjs/auth/server.js +16 -0
- package/dist/cjs/bundle/index.js +86 -12
- package/dist/cjs/meta/index.d.ts +14 -0
- package/dist/cjs/meta/index.js +12 -0
- package/dist/cjs/runtime/index.d.ts +7 -0
- package/dist/cjs/sync/lib.js +3 -0
- package/dist/esm/auth/index.js +11 -3
- package/dist/esm/auth/index.test.js +18 -0
- package/dist/esm/auth/server.d.ts +66 -0
- package/dist/esm/auth/server.js +12 -0
- package/dist/esm/bundle/index.js +86 -12
- package/dist/esm/meta/index.d.ts +14 -0
- package/dist/esm/meta/index.js +8 -0
- package/dist/esm/runtime/index.d.ts +7 -0
- package/dist/esm/sync/lib.js +3 -0
- package/package.json +7 -1
package/dist/cjs/auth/index.js
CHANGED
|
@@ -42,9 +42,17 @@ function loginWithRedirect(opts) {
|
|
|
42
42
|
// any non-ASCII in the path fails its regex and the destination is dropped —
|
|
43
43
|
// but never inspects the path of an absolute one. Normalising here means a
|
|
44
44
|
// caller can pass whichever form reads best without having to know that.
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const target = new URL(opts?.redirectUrl ?? window.location.href, window.location.href);
|
|
46
|
+
// Say nothing when there's nothing to say. The sign-in page already returns
|
|
47
|
+
// people to the app root when it isn't told otherwise, so naming the root
|
|
48
|
+
// spells out the default — and puts an encoded copy of the app's own URL in
|
|
49
|
+
// the address bar of every logged-out visitor to the front page, which is the
|
|
50
|
+
// common case. Only pass a destination when it IS one.
|
|
51
|
+
const isRoot = target.pathname === '/' && target.search === '' && target.hash === '';
|
|
52
|
+
window.location.href = isRoot
|
|
53
|
+
? '/auth/login'
|
|
54
|
+
: '/auth/login?' +
|
|
55
|
+
new URLSearchParams({ redirectUrl: target.toString() }).toString();
|
|
48
56
|
}
|
|
49
57
|
function logout(opts) {
|
|
50
58
|
(0, exports.signOut)().then(() => {
|
|
@@ -83,6 +83,24 @@ function redirectParamOf(href) {
|
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
(0, vitest_1.describe)('loginWithRedirect', () => {
|
|
86
|
+
(0, vitest_1.it)('says nothing when the destination is the app root', () => {
|
|
87
|
+
// The sign-in page already returns people to the root, so naming it would
|
|
88
|
+
// just put an encoded copy of the app's own URL in the address bar — and a
|
|
89
|
+
// logged-out visitor to the front page is the common case.
|
|
90
|
+
const location = stubLocation('https://app.zite.so/');
|
|
91
|
+
authExports.loginWithRedirect();
|
|
92
|
+
(0, vitest_1.expect)(location.href).toBe('/auth/login');
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.it)('says nothing when an explicit redirectUrl is the app root', () => {
|
|
95
|
+
const location = stubLocation('https://app.zite.so/pricing');
|
|
96
|
+
authExports.loginWithRedirect({ redirectUrl: '/' });
|
|
97
|
+
(0, vitest_1.expect)(location.href).toBe('/auth/login');
|
|
98
|
+
});
|
|
99
|
+
(0, vitest_1.it)('still names a root path that carries a query or hash', () => {
|
|
100
|
+
const location = stubLocation('https://app.zite.so/?invite=abc');
|
|
101
|
+
authExports.loginWithRedirect();
|
|
102
|
+
(0, vitest_1.expect)(redirectParamOf(location.href)).toBe('https://app.zite.so/?invite=abc');
|
|
103
|
+
});
|
|
86
104
|
(0, vitest_1.it)('captures the current URL when called with no arguments', () => {
|
|
87
105
|
const location = stubLocation('https://app.zite.so/orders/123?tab=open');
|
|
88
106
|
authExports.loginWithRedirect();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side auth hooks — the `ziteAuth({ hooks })` config an app
|
|
3
|
+
* default-exports from `zite.auth.ts` (app root). The platform bundles that
|
|
4
|
+
* file at build time and invokes the hooks at auth lifecycle moments
|
|
5
|
+
* (sign-up, sign-in, session checks). Hooks run like backend app code, so
|
|
6
|
+
* they can use the app's integrations and `zitejs/db`.
|
|
7
|
+
*
|
|
8
|
+
* All hooks fail open on error/timeout — a broken hook never locks users
|
|
9
|
+
* out. Gate hooks return allow / deny / defer; `verifySession` returns an
|
|
10
|
+
* active verdict and may enrich the app-visible user.
|
|
11
|
+
*/
|
|
12
|
+
export interface ZiteAuthHookUser {
|
|
13
|
+
id: string;
|
|
14
|
+
email: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* allow admits past disabled/domain-restricted signup; deny blocks with an
|
|
19
|
+
* optional reason; anything else (empty object / no return) defers to the
|
|
20
|
+
* platform rules.
|
|
21
|
+
*/
|
|
22
|
+
export type ZiteAuthGateDecision = {
|
|
23
|
+
allow: true;
|
|
24
|
+
} | {
|
|
25
|
+
deny: true;
|
|
26
|
+
reason?: string;
|
|
27
|
+
} | {
|
|
28
|
+
allow?: undefined;
|
|
29
|
+
deny?: undefined;
|
|
30
|
+
} | void;
|
|
31
|
+
/**
|
|
32
|
+
* active: false revokes the session; active: true keeps it, and `user`
|
|
33
|
+
* merges extra fields into the app-visible user.
|
|
34
|
+
*/
|
|
35
|
+
export type ZiteAuthSessionVerdict = {
|
|
36
|
+
active: false;
|
|
37
|
+
reason?: string;
|
|
38
|
+
} | {
|
|
39
|
+
active: true;
|
|
40
|
+
user?: Record<string, unknown>;
|
|
41
|
+
} | {
|
|
42
|
+
active?: undefined;
|
|
43
|
+
} | void;
|
|
44
|
+
export interface ZiteAuthHooks {
|
|
45
|
+
beforeSignUp?(input: {
|
|
46
|
+
email: string;
|
|
47
|
+
firstName?: string;
|
|
48
|
+
lastName?: string;
|
|
49
|
+
}): Promise<ZiteAuthGateDecision> | ZiteAuthGateDecision;
|
|
50
|
+
beforeSignIn?(input: {
|
|
51
|
+
email: string;
|
|
52
|
+
}): Promise<ZiteAuthGateDecision> | ZiteAuthGateDecision;
|
|
53
|
+
afterSignUp?(input: {
|
|
54
|
+
user: ZiteAuthHookUser;
|
|
55
|
+
}): Promise<unknown> | unknown;
|
|
56
|
+
afterSignIn?(input: {
|
|
57
|
+
user: ZiteAuthHookUser;
|
|
58
|
+
}): Promise<unknown> | unknown;
|
|
59
|
+
verifySession?(input: {
|
|
60
|
+
user: ZiteAuthHookUser;
|
|
61
|
+
}): Promise<ZiteAuthSessionVerdict> | ZiteAuthSessionVerdict;
|
|
62
|
+
}
|
|
63
|
+
export interface ZiteAuthConfig {
|
|
64
|
+
hooks?: ZiteAuthHooks;
|
|
65
|
+
}
|
|
66
|
+
export declare const ziteAuth: (config: ZiteAuthConfig) => ZiteAuthConfig;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Server-side auth hooks — the `ziteAuth({ hooks })` config an app
|
|
4
|
+
* default-exports from `zite.auth.ts` (app root). The platform bundles that
|
|
5
|
+
* file at build time and invokes the hooks at auth lifecycle moments
|
|
6
|
+
* (sign-up, sign-in, session checks). Hooks run like backend app code, so
|
|
7
|
+
* they can use the app's integrations and `zitejs/db`.
|
|
8
|
+
*
|
|
9
|
+
* All hooks fail open on error/timeout — a broken hook never locks users
|
|
10
|
+
* out. Gate hooks return allow / deny / defer; `verifySession` returns an
|
|
11
|
+
* active verdict and may enrich the app-visible user.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.ziteAuth = void 0;
|
|
15
|
+
const ziteAuth = (config) => config;
|
|
16
|
+
exports.ziteAuth = ziteAuth;
|
package/dist/cjs/bundle/index.js
CHANGED
|
@@ -268,6 +268,16 @@ function createAliasPlugin(opts) {
|
|
|
268
268
|
}
|
|
269
269
|
return { path: 'zitejs/email', external: true };
|
|
270
270
|
});
|
|
271
|
+
// zitejs/auth/server is types + an identity wrapper — inline a shim so
|
|
272
|
+
// zite.auth.ts bundles without resolving the installed package.
|
|
273
|
+
build.onResolve({ filter: /^zitejs\/auth\/server$/ }, () => ({
|
|
274
|
+
path: 'zitejs-auth-server-shim',
|
|
275
|
+
namespace: 'zite-virtual',
|
|
276
|
+
}));
|
|
277
|
+
build.onLoad({ filter: /.*/, namespace: 'zite-virtual' }, () => ({
|
|
278
|
+
contents: 'export const ziteAuth = (config) => config;',
|
|
279
|
+
loader: 'js',
|
|
280
|
+
}));
|
|
271
281
|
// zitejs/runtime is NOT in PREBUNDLED_LIBS — it's a thin fetch()
|
|
272
282
|
// wrapper that gets bundled inline by esbuild (no special handling).
|
|
273
283
|
for (const [pkgName, modulePath] of Object.entries(PREBUNDLED_LIBS)) {
|
|
@@ -392,9 +402,78 @@ async function bundleEndpointsImpl(baseDir, endpointNames) {
|
|
|
392
402
|
}
|
|
393
403
|
}
|
|
394
404
|
}
|
|
405
|
+
let bundledAuthHooks;
|
|
406
|
+
let authHooksError;
|
|
407
|
+
if (fs.existsSync(path.join(baseDir, 'zite.auth.ts'))) {
|
|
408
|
+
const runtimeInit = `import { __wrapSdkCall, initRuntime } from '@zite/endpoints-runtime-sdk';
|
|
409
|
+
globalThis.__wrapSdkCall = __wrapSdkCall;
|
|
410
|
+
initRuntime();`;
|
|
411
|
+
const dispatcherCode = `
|
|
412
|
+
${runtimeInit}
|
|
413
|
+
import { z } from 'zod';
|
|
414
|
+
import { createEndpoint } from '${sdkSource}';
|
|
415
|
+
import auth from '../zite.auth';
|
|
416
|
+
|
|
417
|
+
const endpoint = createEndpoint({
|
|
418
|
+
description: 'Runs the auth lifecycle hooks defined in zite.auth.ts',
|
|
419
|
+
inputSchema: z.object({
|
|
420
|
+
hook: z.enum([
|
|
421
|
+
'beforeSignUp',
|
|
422
|
+
'afterSignUp',
|
|
423
|
+
'beforeSignIn',
|
|
424
|
+
'afterSignIn',
|
|
425
|
+
'verifySession',
|
|
426
|
+
]),
|
|
427
|
+
payload: z.record(z.unknown()).default({}),
|
|
428
|
+
}),
|
|
429
|
+
execute: async ({ input }) => {
|
|
430
|
+
const hook = auth.hooks?.[input.hook];
|
|
431
|
+
if (!hook) return {};
|
|
432
|
+
return (await hook(input.payload as never)) ?? {};
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
globalThis.__endpoint = endpoint;
|
|
436
|
+
`;
|
|
437
|
+
try {
|
|
438
|
+
const result = await esbuild.build({
|
|
439
|
+
...BASE_BUILD_OPTIONS,
|
|
440
|
+
stdin: {
|
|
441
|
+
contents: dispatcherCode,
|
|
442
|
+
resolveDir: `${baseDir}/src`,
|
|
443
|
+
loader: 'ts',
|
|
444
|
+
},
|
|
445
|
+
logLevel: 'warning',
|
|
446
|
+
plugins: [aliasPlugin],
|
|
447
|
+
});
|
|
448
|
+
if (result.outputFiles && result.outputFiles.length > 0) {
|
|
449
|
+
bundledAuthHooks = result.outputFiles[0].text;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
authHooksError = 'No output generated';
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (err) {
|
|
456
|
+
const e = err;
|
|
457
|
+
if (e.errors && Array.isArray(e.errors)) {
|
|
458
|
+
authHooksError = e.errors
|
|
459
|
+
.map(er => {
|
|
460
|
+
const loc = er.location
|
|
461
|
+
? `${er.location.file || 'unknown'}:${er.location.line || '?'}:${er.location.column || '?'}`
|
|
462
|
+
: 'unknown';
|
|
463
|
+
return `${loc} - ${er.text}`;
|
|
464
|
+
})
|
|
465
|
+
.join('\n');
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
authHooksError = e.message || String(err);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
395
472
|
const output = JSON.stringify({
|
|
396
473
|
bundledEndpoints,
|
|
397
474
|
endpointErrors: Object.keys(endpointErrors).length > 0 ? endpointErrors : undefined,
|
|
475
|
+
bundledAuthHooks,
|
|
476
|
+
authHooksError,
|
|
398
477
|
});
|
|
399
478
|
fs.writeFileSync('/tmp/zitejs-bundle-result.json', output);
|
|
400
479
|
console.log(output);
|
|
@@ -504,18 +583,13 @@ async function runBundle() {
|
|
|
504
583
|
}
|
|
505
584
|
else {
|
|
506
585
|
const apiDir = path.join(baseDir, 'src', 'api');
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
.filter(f => f.endsWith('.ts'))
|
|
514
|
-
.map(f => f.replace('.ts', ''));
|
|
515
|
-
}
|
|
516
|
-
if (endpointNames.length === 0) {
|
|
517
|
-
console.log(JSON.stringify({ bundledEndpoints: {} }));
|
|
518
|
-
return;
|
|
586
|
+
endpointNames = fs.existsSync(apiDir)
|
|
587
|
+
? fs
|
|
588
|
+
.readdirSync(apiDir)
|
|
589
|
+
.filter(f => f.endsWith('.ts'))
|
|
590
|
+
.map(f => f.replace('.ts', ''))
|
|
591
|
+
: [];
|
|
519
592
|
}
|
|
593
|
+
// Even with no endpoints, zite.auth.ts may still need bundling.
|
|
520
594
|
return bundleEndpointsImpl(baseDir, endpointNames);
|
|
521
595
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ZiteProjectUser = {
|
|
2
|
+
uuid: string;
|
|
3
|
+
firstName: string | null;
|
|
4
|
+
lastName: string | null;
|
|
5
|
+
email: string;
|
|
6
|
+
profilePictureUrl: string | null;
|
|
7
|
+
};
|
|
8
|
+
export type MetaListUsersResult = {
|
|
9
|
+
users: ZiteProjectUser[];
|
|
10
|
+
};
|
|
11
|
+
export declare class ZiteMeta {
|
|
12
|
+
static listUsers(): Promise<MetaListUsersResult>;
|
|
13
|
+
}
|
|
14
|
+
export declare const Meta: typeof ZiteMeta;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Meta = exports.ZiteMeta = void 0;
|
|
4
|
+
const sdkCall_js_1 = require("../internal/sdkCall.js");
|
|
5
|
+
const META_SDK_INTEGRATION_ID = '__meta__';
|
|
6
|
+
class ZiteMeta {
|
|
7
|
+
static listUsers() {
|
|
8
|
+
return (0, sdkCall_js_1.getSdkCall)()(META_SDK_INTEGRATION_ID, 'ZiteMeta', 'listUsers', {});
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.ZiteMeta = ZiteMeta;
|
|
12
|
+
exports.Meta = ZiteMeta;
|
|
@@ -146,6 +146,13 @@ export interface SendEmailParams {
|
|
|
146
146
|
replyTo?: string;
|
|
147
147
|
/** 'formatted' (default) for styled HTML, 'plain' for text-only. */
|
|
148
148
|
layout?: "formatted" | "plain";
|
|
149
|
+
/**
|
|
150
|
+
* Text direction of the email. Use 'rtl' for right-to-left languages
|
|
151
|
+
* (Hebrew, Arabic) — sets dir="rtl" on the email wrapper and right-aligns
|
|
152
|
+
* text blocks. HTML like `<div dir="rtl">` inside text blocks is stripped
|
|
153
|
+
* by the sanitizer and will NOT work. Defaults to 'ltr'.
|
|
154
|
+
*/
|
|
155
|
+
direction?: "ltr" | "rtl";
|
|
149
156
|
/** Custom logo shown above the email content (header area), for branding. */
|
|
150
157
|
logo?: {
|
|
151
158
|
url: string;
|
package/dist/cjs/sync/lib.js
CHANGED
|
@@ -738,6 +738,9 @@ function generateEmailSdk(integrationId) {
|
|
|
738
738
|
"//",
|
|
739
739
|
"// await Email.send({ to, subject, body: [{ type: 'text', content: '...' }] })",
|
|
740
740
|
"// => { success: boolean; messageId: string }",
|
|
741
|
+
"//",
|
|
742
|
+
"// RTL languages (Hebrew, Arabic): pass direction: 'rtl' — dir attributes",
|
|
743
|
+
"// inside body text are stripped by the sanitizer and will not work.",
|
|
741
744
|
"",
|
|
742
745
|
"import { createEmailClient } from 'zitejs/runtime';",
|
|
743
746
|
"",
|
package/dist/esm/auth/index.js
CHANGED
|
@@ -35,9 +35,17 @@ export function loginWithRedirect(opts) {
|
|
|
35
35
|
// any non-ASCII in the path fails its regex and the destination is dropped —
|
|
36
36
|
// but never inspects the path of an absolute one. Normalising here means a
|
|
37
37
|
// caller can pass whichever form reads best without having to know that.
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const target = new URL(opts?.redirectUrl ?? window.location.href, window.location.href);
|
|
39
|
+
// Say nothing when there's nothing to say. The sign-in page already returns
|
|
40
|
+
// people to the app root when it isn't told otherwise, so naming the root
|
|
41
|
+
// spells out the default — and puts an encoded copy of the app's own URL in
|
|
42
|
+
// the address bar of every logged-out visitor to the front page, which is the
|
|
43
|
+
// common case. Only pass a destination when it IS one.
|
|
44
|
+
const isRoot = target.pathname === '/' && target.search === '' && target.hash === '';
|
|
45
|
+
window.location.href = isRoot
|
|
46
|
+
? '/auth/login'
|
|
47
|
+
: '/auth/login?' +
|
|
48
|
+
new URLSearchParams({ redirectUrl: target.toString() }).toString();
|
|
41
49
|
}
|
|
42
50
|
export function logout(opts) {
|
|
43
51
|
signOut().then(() => {
|
|
@@ -48,6 +48,24 @@ describe('zitejs/auth exports', () => {
|
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
describe('loginWithRedirect', () => {
|
|
51
|
+
it('says nothing when the destination is the app root', () => {
|
|
52
|
+
// The sign-in page already returns people to the root, so naming it would
|
|
53
|
+
// just put an encoded copy of the app's own URL in the address bar — and a
|
|
54
|
+
// logged-out visitor to the front page is the common case.
|
|
55
|
+
const location = stubLocation('https://app.zite.so/');
|
|
56
|
+
authExports.loginWithRedirect();
|
|
57
|
+
expect(location.href).toBe('/auth/login');
|
|
58
|
+
});
|
|
59
|
+
it('says nothing when an explicit redirectUrl is the app root', () => {
|
|
60
|
+
const location = stubLocation('https://app.zite.so/pricing');
|
|
61
|
+
authExports.loginWithRedirect({ redirectUrl: '/' });
|
|
62
|
+
expect(location.href).toBe('/auth/login');
|
|
63
|
+
});
|
|
64
|
+
it('still names a root path that carries a query or hash', () => {
|
|
65
|
+
const location = stubLocation('https://app.zite.so/?invite=abc');
|
|
66
|
+
authExports.loginWithRedirect();
|
|
67
|
+
expect(redirectParamOf(location.href)).toBe('https://app.zite.so/?invite=abc');
|
|
68
|
+
});
|
|
51
69
|
it('captures the current URL when called with no arguments', () => {
|
|
52
70
|
const location = stubLocation('https://app.zite.so/orders/123?tab=open');
|
|
53
71
|
authExports.loginWithRedirect();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side auth hooks — the `ziteAuth({ hooks })` config an app
|
|
3
|
+
* default-exports from `zite.auth.ts` (app root). The platform bundles that
|
|
4
|
+
* file at build time and invokes the hooks at auth lifecycle moments
|
|
5
|
+
* (sign-up, sign-in, session checks). Hooks run like backend app code, so
|
|
6
|
+
* they can use the app's integrations and `zitejs/db`.
|
|
7
|
+
*
|
|
8
|
+
* All hooks fail open on error/timeout — a broken hook never locks users
|
|
9
|
+
* out. Gate hooks return allow / deny / defer; `verifySession` returns an
|
|
10
|
+
* active verdict and may enrich the app-visible user.
|
|
11
|
+
*/
|
|
12
|
+
export interface ZiteAuthHookUser {
|
|
13
|
+
id: string;
|
|
14
|
+
email: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* allow admits past disabled/domain-restricted signup; deny blocks with an
|
|
19
|
+
* optional reason; anything else (empty object / no return) defers to the
|
|
20
|
+
* platform rules.
|
|
21
|
+
*/
|
|
22
|
+
export type ZiteAuthGateDecision = {
|
|
23
|
+
allow: true;
|
|
24
|
+
} | {
|
|
25
|
+
deny: true;
|
|
26
|
+
reason?: string;
|
|
27
|
+
} | {
|
|
28
|
+
allow?: undefined;
|
|
29
|
+
deny?: undefined;
|
|
30
|
+
} | void;
|
|
31
|
+
/**
|
|
32
|
+
* active: false revokes the session; active: true keeps it, and `user`
|
|
33
|
+
* merges extra fields into the app-visible user.
|
|
34
|
+
*/
|
|
35
|
+
export type ZiteAuthSessionVerdict = {
|
|
36
|
+
active: false;
|
|
37
|
+
reason?: string;
|
|
38
|
+
} | {
|
|
39
|
+
active: true;
|
|
40
|
+
user?: Record<string, unknown>;
|
|
41
|
+
} | {
|
|
42
|
+
active?: undefined;
|
|
43
|
+
} | void;
|
|
44
|
+
export interface ZiteAuthHooks {
|
|
45
|
+
beforeSignUp?(input: {
|
|
46
|
+
email: string;
|
|
47
|
+
firstName?: string;
|
|
48
|
+
lastName?: string;
|
|
49
|
+
}): Promise<ZiteAuthGateDecision> | ZiteAuthGateDecision;
|
|
50
|
+
beforeSignIn?(input: {
|
|
51
|
+
email: string;
|
|
52
|
+
}): Promise<ZiteAuthGateDecision> | ZiteAuthGateDecision;
|
|
53
|
+
afterSignUp?(input: {
|
|
54
|
+
user: ZiteAuthHookUser;
|
|
55
|
+
}): Promise<unknown> | unknown;
|
|
56
|
+
afterSignIn?(input: {
|
|
57
|
+
user: ZiteAuthHookUser;
|
|
58
|
+
}): Promise<unknown> | unknown;
|
|
59
|
+
verifySession?(input: {
|
|
60
|
+
user: ZiteAuthHookUser;
|
|
61
|
+
}): Promise<ZiteAuthSessionVerdict> | ZiteAuthSessionVerdict;
|
|
62
|
+
}
|
|
63
|
+
export interface ZiteAuthConfig {
|
|
64
|
+
hooks?: ZiteAuthHooks;
|
|
65
|
+
}
|
|
66
|
+
export declare const ziteAuth: (config: ZiteAuthConfig) => ZiteAuthConfig;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side auth hooks — the `ziteAuth({ hooks })` config an app
|
|
3
|
+
* default-exports from `zite.auth.ts` (app root). The platform bundles that
|
|
4
|
+
* file at build time and invokes the hooks at auth lifecycle moments
|
|
5
|
+
* (sign-up, sign-in, session checks). Hooks run like backend app code, so
|
|
6
|
+
* they can use the app's integrations and `zitejs/db`.
|
|
7
|
+
*
|
|
8
|
+
* All hooks fail open on error/timeout — a broken hook never locks users
|
|
9
|
+
* out. Gate hooks return allow / deny / defer; `verifySession` returns an
|
|
10
|
+
* active verdict and may enrich the app-visible user.
|
|
11
|
+
*/
|
|
12
|
+
export const ziteAuth = (config) => config;
|
package/dist/esm/bundle/index.js
CHANGED
|
@@ -232,6 +232,16 @@ function createAliasPlugin(opts) {
|
|
|
232
232
|
}
|
|
233
233
|
return { path: 'zitejs/email', external: true };
|
|
234
234
|
});
|
|
235
|
+
// zitejs/auth/server is types + an identity wrapper — inline a shim so
|
|
236
|
+
// zite.auth.ts bundles without resolving the installed package.
|
|
237
|
+
build.onResolve({ filter: /^zitejs\/auth\/server$/ }, () => ({
|
|
238
|
+
path: 'zitejs-auth-server-shim',
|
|
239
|
+
namespace: 'zite-virtual',
|
|
240
|
+
}));
|
|
241
|
+
build.onLoad({ filter: /.*/, namespace: 'zite-virtual' }, () => ({
|
|
242
|
+
contents: 'export const ziteAuth = (config) => config;',
|
|
243
|
+
loader: 'js',
|
|
244
|
+
}));
|
|
235
245
|
// zitejs/runtime is NOT in PREBUNDLED_LIBS — it's a thin fetch()
|
|
236
246
|
// wrapper that gets bundled inline by esbuild (no special handling).
|
|
237
247
|
for (const [pkgName, modulePath] of Object.entries(PREBUNDLED_LIBS)) {
|
|
@@ -356,9 +366,78 @@ async function bundleEndpointsImpl(baseDir, endpointNames) {
|
|
|
356
366
|
}
|
|
357
367
|
}
|
|
358
368
|
}
|
|
369
|
+
let bundledAuthHooks;
|
|
370
|
+
let authHooksError;
|
|
371
|
+
if (fs.existsSync(path.join(baseDir, 'zite.auth.ts'))) {
|
|
372
|
+
const runtimeInit = `import { __wrapSdkCall, initRuntime } from '@zite/endpoints-runtime-sdk';
|
|
373
|
+
globalThis.__wrapSdkCall = __wrapSdkCall;
|
|
374
|
+
initRuntime();`;
|
|
375
|
+
const dispatcherCode = `
|
|
376
|
+
${runtimeInit}
|
|
377
|
+
import { z } from 'zod';
|
|
378
|
+
import { createEndpoint } from '${sdkSource}';
|
|
379
|
+
import auth from '../zite.auth';
|
|
380
|
+
|
|
381
|
+
const endpoint = createEndpoint({
|
|
382
|
+
description: 'Runs the auth lifecycle hooks defined in zite.auth.ts',
|
|
383
|
+
inputSchema: z.object({
|
|
384
|
+
hook: z.enum([
|
|
385
|
+
'beforeSignUp',
|
|
386
|
+
'afterSignUp',
|
|
387
|
+
'beforeSignIn',
|
|
388
|
+
'afterSignIn',
|
|
389
|
+
'verifySession',
|
|
390
|
+
]),
|
|
391
|
+
payload: z.record(z.unknown()).default({}),
|
|
392
|
+
}),
|
|
393
|
+
execute: async ({ input }) => {
|
|
394
|
+
const hook = auth.hooks?.[input.hook];
|
|
395
|
+
if (!hook) return {};
|
|
396
|
+
return (await hook(input.payload as never)) ?? {};
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
globalThis.__endpoint = endpoint;
|
|
400
|
+
`;
|
|
401
|
+
try {
|
|
402
|
+
const result = await esbuild.build({
|
|
403
|
+
...BASE_BUILD_OPTIONS,
|
|
404
|
+
stdin: {
|
|
405
|
+
contents: dispatcherCode,
|
|
406
|
+
resolveDir: `${baseDir}/src`,
|
|
407
|
+
loader: 'ts',
|
|
408
|
+
},
|
|
409
|
+
logLevel: 'warning',
|
|
410
|
+
plugins: [aliasPlugin],
|
|
411
|
+
});
|
|
412
|
+
if (result.outputFiles && result.outputFiles.length > 0) {
|
|
413
|
+
bundledAuthHooks = result.outputFiles[0].text;
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
authHooksError = 'No output generated';
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
const e = err;
|
|
421
|
+
if (e.errors && Array.isArray(e.errors)) {
|
|
422
|
+
authHooksError = e.errors
|
|
423
|
+
.map(er => {
|
|
424
|
+
const loc = er.location
|
|
425
|
+
? `${er.location.file || 'unknown'}:${er.location.line || '?'}:${er.location.column || '?'}`
|
|
426
|
+
: 'unknown';
|
|
427
|
+
return `${loc} - ${er.text}`;
|
|
428
|
+
})
|
|
429
|
+
.join('\n');
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
authHooksError = e.message || String(err);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
359
436
|
const output = JSON.stringify({
|
|
360
437
|
bundledEndpoints,
|
|
361
438
|
endpointErrors: Object.keys(endpointErrors).length > 0 ? endpointErrors : undefined,
|
|
439
|
+
bundledAuthHooks,
|
|
440
|
+
authHooksError,
|
|
362
441
|
});
|
|
363
442
|
fs.writeFileSync('/tmp/zitejs-bundle-result.json', output);
|
|
364
443
|
console.log(output);
|
|
@@ -468,18 +547,13 @@ export async function runBundle() {
|
|
|
468
547
|
}
|
|
469
548
|
else {
|
|
470
549
|
const apiDir = path.join(baseDir, 'src', 'api');
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
.filter(f => f.endsWith('.ts'))
|
|
478
|
-
.map(f => f.replace('.ts', ''));
|
|
479
|
-
}
|
|
480
|
-
if (endpointNames.length === 0) {
|
|
481
|
-
console.log(JSON.stringify({ bundledEndpoints: {} }));
|
|
482
|
-
return;
|
|
550
|
+
endpointNames = fs.existsSync(apiDir)
|
|
551
|
+
? fs
|
|
552
|
+
.readdirSync(apiDir)
|
|
553
|
+
.filter(f => f.endsWith('.ts'))
|
|
554
|
+
.map(f => f.replace('.ts', ''))
|
|
555
|
+
: [];
|
|
483
556
|
}
|
|
557
|
+
// Even with no endpoints, zite.auth.ts may still need bundling.
|
|
484
558
|
return bundleEndpointsImpl(baseDir, endpointNames);
|
|
485
559
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ZiteProjectUser = {
|
|
2
|
+
uuid: string;
|
|
3
|
+
firstName: string | null;
|
|
4
|
+
lastName: string | null;
|
|
5
|
+
email: string;
|
|
6
|
+
profilePictureUrl: string | null;
|
|
7
|
+
};
|
|
8
|
+
export type MetaListUsersResult = {
|
|
9
|
+
users: ZiteProjectUser[];
|
|
10
|
+
};
|
|
11
|
+
export declare class ZiteMeta {
|
|
12
|
+
static listUsers(): Promise<MetaListUsersResult>;
|
|
13
|
+
}
|
|
14
|
+
export declare const Meta: typeof ZiteMeta;
|
|
@@ -146,6 +146,13 @@ export interface SendEmailParams {
|
|
|
146
146
|
replyTo?: string;
|
|
147
147
|
/** 'formatted' (default) for styled HTML, 'plain' for text-only. */
|
|
148
148
|
layout?: "formatted" | "plain";
|
|
149
|
+
/**
|
|
150
|
+
* Text direction of the email. Use 'rtl' for right-to-left languages
|
|
151
|
+
* (Hebrew, Arabic) — sets dir="rtl" on the email wrapper and right-aligns
|
|
152
|
+
* text blocks. HTML like `<div dir="rtl">` inside text blocks is stripped
|
|
153
|
+
* by the sanitizer and will NOT work. Defaults to 'ltr'.
|
|
154
|
+
*/
|
|
155
|
+
direction?: "ltr" | "rtl";
|
|
149
156
|
/** Custom logo shown above the email content (header area), for branding. */
|
|
150
157
|
logo?: {
|
|
151
158
|
url: string;
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -728,6 +728,9 @@ export function generateEmailSdk(integrationId) {
|
|
|
728
728
|
"//",
|
|
729
729
|
"// await Email.send({ to, subject, body: [{ type: 'text', content: '...' }] })",
|
|
730
730
|
"// => { success: boolean; messageId: string }",
|
|
731
|
+
"//",
|
|
732
|
+
"// RTL languages (Hebrew, Arabic): pass direction: 'rtl' — dir attributes",
|
|
733
|
+
"// inside body text are stripped by the sanitizer and will not work.",
|
|
731
734
|
"",
|
|
732
735
|
"import { createEmailClient } from 'zitejs/runtime';",
|
|
733
736
|
"",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zitejs",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.93",
|
|
4
4
|
"description": "The Zite framework — build apps on Zite Database",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -47,6 +47,12 @@
|
|
|
47
47
|
"import": "./dist/esm/auth/index.js",
|
|
48
48
|
"require": "./dist/cjs/auth/index.js"
|
|
49
49
|
},
|
|
50
|
+
"./auth/server": {
|
|
51
|
+
"types": "./dist/esm/auth/server.d.ts",
|
|
52
|
+
"import": "./dist/esm/auth/server.js",
|
|
53
|
+
"require": "./dist/cjs/auth/server.js",
|
|
54
|
+
"default": "./dist/esm/auth/server.js"
|
|
55
|
+
},
|
|
50
56
|
"./auth-server": {
|
|
51
57
|
"types": "./dist/esm/auth-server/index.d.ts",
|
|
52
58
|
"import": "./dist/esm/auth-server/index.js",
|