zitejs 0.9.119 → 0.9.121
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/config.d.ts +3 -0
- package/dist/cjs/auth/config.js +7 -0
- package/dist/cjs/auth/index.js +30 -4
- package/dist/cjs/auth/useAuth.test.d.ts +1 -0
- package/dist/cjs/auth/useAuth.test.js +82 -0
- package/dist/cjs/backend/index.d.ts +30 -3
- package/dist/cjs/backend/index.js +2 -2
- package/dist/cjs/bundle/index.d.ts +1 -1
- package/dist/cjs/bundle/index.js +6 -2
- package/dist/cjs/check/index.js +5 -13
- package/dist/cjs/cli.js +2 -2
- package/dist/cjs/dev/index.js +31 -44
- package/dist/cjs/meta/index.d.ts +14 -0
- package/dist/cjs/meta/index.js +12 -0
- package/dist/cjs/notifications/index.d.ts +25 -0
- package/dist/cjs/notifications/index.js +12 -0
- package/dist/cjs/sourceRoots.d.ts +19 -0
- package/dist/cjs/sourceRoots.js +33 -0
- package/dist/cjs/sync/lib.d.ts +6 -0
- package/dist/cjs/sync/lib.js +13 -0
- package/dist/cjs/sync/lib.test.js +28 -0
- package/dist/cjs/upload/index.js +19 -0
- package/dist/cjs/upload/index.test.js +30 -3
- package/dist/cjs/vite/domTagNames.d.ts +1 -0
- package/dist/cjs/vite/domTagNames.js +257 -0
- package/dist/cjs/vite/index.js +20 -11
- package/dist/cjs/vite/index.test.d.ts +1 -0
- package/dist/cjs/vite/index.test.js +53 -0
- package/dist/esm/auth/config.d.ts +3 -0
- package/dist/esm/auth/config.js +6 -0
- package/dist/esm/auth/index.js +28 -2
- package/dist/esm/auth/useAuth.test.d.ts +1 -0
- package/dist/esm/auth/useAuth.test.js +80 -0
- package/dist/esm/backend/index.d.ts +30 -3
- package/dist/esm/backend/index.js +2 -2
- package/dist/esm/bundle/index.d.ts +1 -1
- package/dist/esm/bundle/index.js +6 -2
- package/dist/esm/check/index.js +6 -14
- package/dist/esm/cli.js +2 -2
- package/dist/esm/dev/index.js +32 -45
- package/dist/esm/meta/index.d.ts +14 -0
- package/dist/esm/meta/index.js +8 -0
- package/dist/esm/notifications/index.d.ts +25 -0
- package/dist/esm/notifications/index.js +8 -0
- package/dist/esm/sourceRoots.d.ts +19 -0
- package/dist/esm/sourceRoots.js +28 -0
- package/dist/esm/sync/lib.d.ts +6 -0
- package/dist/esm/sync/lib.js +12 -0
- package/dist/esm/sync/lib.test.js +29 -1
- package/dist/esm/upload/index.js +19 -0
- package/dist/esm/upload/index.test.js +30 -3
- package/dist/esm/vite/domTagNames.d.ts +1 -0
- package/dist/esm/vite/domTagNames.js +254 -0
- package/dist/esm/vite/index.js +20 -11
- package/dist/esm/vite/index.test.d.ts +1 -0
- package/dist/esm/vite/index.test.js +48 -0
- package/package.json +1 -1
package/dist/cjs/sync/lib.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.generateApiTs = generateApiTs;
|
|
|
7
7
|
exports.normalizeAirtableLockNames = normalizeAirtableLockNames;
|
|
8
8
|
exports.generateAirtableTs = generateAirtableTs;
|
|
9
9
|
exports.generateBackendWrapperTs = generateBackendWrapperTs;
|
|
10
|
+
exports.findEmailIntegrationId = findEmailIntegrationId;
|
|
10
11
|
exports.generateEmailSdk = generateEmailSdk;
|
|
11
12
|
const parser_1 = require("@babel/parser");
|
|
12
13
|
const sdkNames_js_1 = require("./sdkNames.js");
|
|
@@ -1346,6 +1347,18 @@ function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1346
1347
|
"",
|
|
1347
1348
|
].join("\n");
|
|
1348
1349
|
}
|
|
1350
|
+
/**
|
|
1351
|
+
* The key of the email integration an app sends through, which is the
|
|
1352
|
+
* integrationId the runtime SDK bridge dispatches on. The app's own entry wins;
|
|
1353
|
+
* otherwise it is the workspace email the app has `integrationSettings` for.
|
|
1354
|
+
*/
|
|
1355
|
+
function findEmailIntegrationId(appConfig, workspaceConfig) {
|
|
1356
|
+
const app = (appConfig ?? {});
|
|
1357
|
+
const workspace = (workspaceConfig ?? {});
|
|
1358
|
+
const isEmail = (config, id) => config.integrations?.[id]?.type === "email";
|
|
1359
|
+
return (Object.keys(app.integrations ?? {}).find((id) => isEmail(app, id)) ??
|
|
1360
|
+
Object.keys(app.integrationSettings ?? {}).find((id) => isEmail(workspace, id)));
|
|
1361
|
+
}
|
|
1349
1362
|
/**
|
|
1350
1363
|
* Generate `.zite/integrations/email.ts` — the `Email` client for an app with
|
|
1351
1364
|
* an email integration connected. Mirrors the airtable SDK generation: a thin
|
|
@@ -385,3 +385,31 @@ const duplicateIdentifierErrorsIn = (source) => {
|
|
|
385
385
|
(0, vitest_1.expect)(output).toContain('ZiteError');
|
|
386
386
|
});
|
|
387
387
|
});
|
|
388
|
+
(0, vitest_1.describe)('findEmailIntegrationId', () => {
|
|
389
|
+
const workspace = {
|
|
390
|
+
integrations: {
|
|
391
|
+
slack: { type: 'slack' },
|
|
392
|
+
'team-email': { type: 'email' },
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
(0, vitest_1.it)('finds the app\'s own email entry', () => {
|
|
396
|
+
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)({ integrations: { mail: { type: 'email' } } }, workspace)).toBe('mail');
|
|
397
|
+
});
|
|
398
|
+
(0, vitest_1.it)('finds the workspace email the app has settings for', () => {
|
|
399
|
+
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)({ integrationSettings: { slack: {}, 'team-email': { fromName: 'Ops' } } }, workspace)).toBe('team-email');
|
|
400
|
+
});
|
|
401
|
+
// An app that never opted in generates no client, so `zitejs/email` stays
|
|
402
|
+
// unresolved there instead of sending as a sender it never chose.
|
|
403
|
+
(0, vitest_1.it)('ignores a workspace email the app has no settings for', () => {
|
|
404
|
+
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)({}, workspace)).toBeUndefined();
|
|
405
|
+
});
|
|
406
|
+
(0, vitest_1.it)('prefers the app\'s own entry over a workspace one', () => {
|
|
407
|
+
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)({
|
|
408
|
+
integrations: { mail: { type: 'email' } },
|
|
409
|
+
integrationSettings: { 'team-email': {} },
|
|
410
|
+
}, workspace)).toBe('mail');
|
|
411
|
+
});
|
|
412
|
+
(0, vitest_1.it)('tolerates missing configs', () => {
|
|
413
|
+
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)(undefined, undefined)).toBeUndefined();
|
|
414
|
+
});
|
|
415
|
+
});
|
package/dist/cjs/upload/index.js
CHANGED
|
@@ -113,6 +113,25 @@ async function performUpload(data, filename) {
|
|
|
113
113
|
if (!putRes.ok) {
|
|
114
114
|
throw new FileUploadError('Upload failed');
|
|
115
115
|
}
|
|
116
|
+
// Gateways that predate the storage ledger omit the token. The file is
|
|
117
|
+
// already at fileUrl, so those uploads still succeed.
|
|
118
|
+
if (session.completionToken) {
|
|
119
|
+
const completeRes = await fetch((0, config_js_1.getApiUrl)() +
|
|
120
|
+
'/v1/zite/public/' +
|
|
121
|
+
flowId +
|
|
122
|
+
'/complete-upload?mode=' +
|
|
123
|
+
mode, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: {
|
|
126
|
+
'Content-Type': 'application/json',
|
|
127
|
+
...authHeaders(),
|
|
128
|
+
},
|
|
129
|
+
body: JSON.stringify({ completionToken: session.completionToken }),
|
|
130
|
+
});
|
|
131
|
+
if (!completeRes.ok) {
|
|
132
|
+
throw new FileUploadError(await readErrorMessage(completeRes, 'Upload failed'));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
116
135
|
return session.fileUrl;
|
|
117
136
|
}
|
|
118
137
|
async function uploadFile({ data, filename, }) {
|
|
@@ -25,7 +25,7 @@ const fetchMock = vitest_1.vi.fn();
|
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
27
|
(0, vitest_1.describe)('uploadFile', () => {
|
|
28
|
-
(0, vitest_1.it)('requests a session
|
|
28
|
+
(0, vitest_1.it)('requests a session, PUTs the bytes, then completes the upload', async () => {
|
|
29
29
|
const file = new File(['hello-world'], 'hello.txt', { type: 'text/plain' });
|
|
30
30
|
fetchMock
|
|
31
31
|
.mockResolvedValueOnce({
|
|
@@ -35,12 +35,14 @@ const fetchMock = vitest_1.vi.fn();
|
|
|
35
35
|
presignedUrl: 'https://s3.example.com/put',
|
|
36
36
|
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
37
37
|
contentType: 'text/plain',
|
|
38
|
+
completionToken: 'signed-token',
|
|
38
39
|
}),
|
|
39
40
|
})
|
|
40
|
-
.mockResolvedValueOnce({ ok: true })
|
|
41
|
+
.mockResolvedValueOnce({ ok: true })
|
|
42
|
+
.mockResolvedValueOnce({ ok: true, json: async () => ({ success: true }) });
|
|
41
43
|
const result = await (0, index_js_1.uploadFile)({ data: file, filename: 'hello.txt' });
|
|
42
44
|
(0, vitest_1.expect)(result.fileUrl).toContain('hello.txt');
|
|
43
|
-
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(
|
|
45
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
|
|
44
46
|
(0, vitest_1.expect)(fetchMock.mock.calls[0][0]).toBe('https://api.example.com/v1/zite/public/app_123/upload-session?mode=live');
|
|
45
47
|
const sessionInit = fetchMock.mock.calls[0][1];
|
|
46
48
|
(0, vitest_1.expect)(JSON.parse(sessionInit.body)).toEqual({
|
|
@@ -50,6 +52,31 @@ const fetchMock = vitest_1.vi.fn();
|
|
|
50
52
|
});
|
|
51
53
|
(0, vitest_1.expect)(fetchMock.mock.calls[1][0]).toBe('https://s3.example.com/put');
|
|
52
54
|
(0, vitest_1.expect)(fetchMock.mock.calls[1][1].method).toBe('PUT');
|
|
55
|
+
(0, vitest_1.expect)(fetchMock.mock.calls[2][0]).toBe('https://api.example.com/v1/zite/public/app_123/complete-upload?mode=live');
|
|
56
|
+
const completeInit = fetchMock.mock.calls[2][1];
|
|
57
|
+
(0, vitest_1.expect)(completeInit.method).toBe('POST');
|
|
58
|
+
(0, vitest_1.expect)(JSON.parse(completeInit.body)).toEqual({
|
|
59
|
+
completionToken: 'signed-token',
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.it)('skips completion when the gateway does not issue a token', async () => {
|
|
63
|
+
fetchMock
|
|
64
|
+
.mockResolvedValueOnce({
|
|
65
|
+
ok: true,
|
|
66
|
+
json: async () => ({
|
|
67
|
+
success: true,
|
|
68
|
+
presignedUrl: 'https://s3.example.com/put',
|
|
69
|
+
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
70
|
+
contentType: 'text/plain',
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
.mockResolvedValueOnce({ ok: true });
|
|
74
|
+
const result = await (0, index_js_1.uploadFile)({
|
|
75
|
+
data: new File(['hello-world'], 'hello.txt', { type: 'text/plain' }),
|
|
76
|
+
filename: 'hello.txt',
|
|
77
|
+
});
|
|
78
|
+
(0, vitest_1.expect)(result.fileUrl).toContain('hello.txt');
|
|
79
|
+
(0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(2);
|
|
53
80
|
});
|
|
54
81
|
(0, vitest_1.it)('surfaces the plan-limit message from the session endpoint', async () => {
|
|
55
82
|
fetchMock.mockResolvedValueOnce({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const domTagNames: ReadonlySet<string>;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.domTagNames = void 0;
|
|
4
|
+
exports.domTagNames = new Set([
|
|
5
|
+
"a",
|
|
6
|
+
"abbr",
|
|
7
|
+
"acronym",
|
|
8
|
+
"address",
|
|
9
|
+
"altGlyph",
|
|
10
|
+
"altGlyphDef",
|
|
11
|
+
"altGlyphItem",
|
|
12
|
+
"animate",
|
|
13
|
+
"animateColor",
|
|
14
|
+
"animateMotion",
|
|
15
|
+
"animateTransform",
|
|
16
|
+
"annotation",
|
|
17
|
+
"annotation-xml",
|
|
18
|
+
"applet",
|
|
19
|
+
"area",
|
|
20
|
+
"article",
|
|
21
|
+
"aside",
|
|
22
|
+
"audio",
|
|
23
|
+
"b",
|
|
24
|
+
"base",
|
|
25
|
+
"basefont",
|
|
26
|
+
"bdi",
|
|
27
|
+
"bdo",
|
|
28
|
+
"bgsound",
|
|
29
|
+
"big",
|
|
30
|
+
"blink",
|
|
31
|
+
"blockquote",
|
|
32
|
+
"body",
|
|
33
|
+
"br",
|
|
34
|
+
"button",
|
|
35
|
+
"canvas",
|
|
36
|
+
"caption",
|
|
37
|
+
"center",
|
|
38
|
+
"circle",
|
|
39
|
+
"cite",
|
|
40
|
+
"clipPath",
|
|
41
|
+
"code",
|
|
42
|
+
"col",
|
|
43
|
+
"colgroup",
|
|
44
|
+
"color-profile",
|
|
45
|
+
"command",
|
|
46
|
+
"content",
|
|
47
|
+
"cursor",
|
|
48
|
+
"data",
|
|
49
|
+
"datalist",
|
|
50
|
+
"dd",
|
|
51
|
+
"defs",
|
|
52
|
+
"del",
|
|
53
|
+
"desc",
|
|
54
|
+
"details",
|
|
55
|
+
"dfn",
|
|
56
|
+
"dialog",
|
|
57
|
+
"dir",
|
|
58
|
+
"discard",
|
|
59
|
+
"div",
|
|
60
|
+
"dl",
|
|
61
|
+
"dt",
|
|
62
|
+
"ellipse",
|
|
63
|
+
"em",
|
|
64
|
+
"embed",
|
|
65
|
+
"feBlend",
|
|
66
|
+
"feColorMatrix",
|
|
67
|
+
"feComponentTransfer",
|
|
68
|
+
"feComposite",
|
|
69
|
+
"feConvolveMatrix",
|
|
70
|
+
"feDiffuseLighting",
|
|
71
|
+
"feDisplacementMap",
|
|
72
|
+
"feDistantLight",
|
|
73
|
+
"feDropShadow",
|
|
74
|
+
"feFlood",
|
|
75
|
+
"feFuncA",
|
|
76
|
+
"feFuncB",
|
|
77
|
+
"feFuncG",
|
|
78
|
+
"feFuncR",
|
|
79
|
+
"feGaussianBlur",
|
|
80
|
+
"feImage",
|
|
81
|
+
"feMerge",
|
|
82
|
+
"feMergeNode",
|
|
83
|
+
"feMorphology",
|
|
84
|
+
"feOffset",
|
|
85
|
+
"fePointLight",
|
|
86
|
+
"feSpecularLighting",
|
|
87
|
+
"feSpotLight",
|
|
88
|
+
"feTile",
|
|
89
|
+
"feTurbulence",
|
|
90
|
+
"fieldset",
|
|
91
|
+
"figcaption",
|
|
92
|
+
"figure",
|
|
93
|
+
"filter",
|
|
94
|
+
"font",
|
|
95
|
+
"font-face",
|
|
96
|
+
"font-face-format",
|
|
97
|
+
"font-face-name",
|
|
98
|
+
"font-face-src",
|
|
99
|
+
"font-face-uri",
|
|
100
|
+
"footer",
|
|
101
|
+
"foreignObject",
|
|
102
|
+
"form",
|
|
103
|
+
"frame",
|
|
104
|
+
"frameset",
|
|
105
|
+
"g",
|
|
106
|
+
"glyph",
|
|
107
|
+
"glyphRef",
|
|
108
|
+
"h1",
|
|
109
|
+
"h2",
|
|
110
|
+
"h3",
|
|
111
|
+
"h4",
|
|
112
|
+
"h5",
|
|
113
|
+
"h6",
|
|
114
|
+
"head",
|
|
115
|
+
"header",
|
|
116
|
+
"hgroup",
|
|
117
|
+
"hkern",
|
|
118
|
+
"hr",
|
|
119
|
+
"html",
|
|
120
|
+
"i",
|
|
121
|
+
"iframe",
|
|
122
|
+
"image",
|
|
123
|
+
"img",
|
|
124
|
+
"input",
|
|
125
|
+
"ins",
|
|
126
|
+
"isindex",
|
|
127
|
+
"kbd",
|
|
128
|
+
"keygen",
|
|
129
|
+
"label",
|
|
130
|
+
"legend",
|
|
131
|
+
"li",
|
|
132
|
+
"line",
|
|
133
|
+
"linearGradient",
|
|
134
|
+
"link",
|
|
135
|
+
"listing",
|
|
136
|
+
"maction",
|
|
137
|
+
"main",
|
|
138
|
+
"map",
|
|
139
|
+
"mark",
|
|
140
|
+
"marker",
|
|
141
|
+
"marquee",
|
|
142
|
+
"mask",
|
|
143
|
+
"math",
|
|
144
|
+
"menu",
|
|
145
|
+
"menuitem",
|
|
146
|
+
"merror",
|
|
147
|
+
"meta",
|
|
148
|
+
"metadata",
|
|
149
|
+
"meter",
|
|
150
|
+
"mfrac",
|
|
151
|
+
"mi",
|
|
152
|
+
"missing-glyph",
|
|
153
|
+
"mmultiscripts",
|
|
154
|
+
"mn",
|
|
155
|
+
"mo",
|
|
156
|
+
"mover",
|
|
157
|
+
"mpadded",
|
|
158
|
+
"mpath",
|
|
159
|
+
"mphantom",
|
|
160
|
+
"mprescripts",
|
|
161
|
+
"mroot",
|
|
162
|
+
"mrow",
|
|
163
|
+
"ms",
|
|
164
|
+
"mspace",
|
|
165
|
+
"msqrt",
|
|
166
|
+
"mstyle",
|
|
167
|
+
"msub",
|
|
168
|
+
"msubsup",
|
|
169
|
+
"msup",
|
|
170
|
+
"mtable",
|
|
171
|
+
"mtd",
|
|
172
|
+
"mtext",
|
|
173
|
+
"mtr",
|
|
174
|
+
"multicol",
|
|
175
|
+
"munder",
|
|
176
|
+
"munderover",
|
|
177
|
+
"nav",
|
|
178
|
+
"nextid",
|
|
179
|
+
"nobr",
|
|
180
|
+
"noembed",
|
|
181
|
+
"noframes",
|
|
182
|
+
"noscript",
|
|
183
|
+
"object",
|
|
184
|
+
"ol",
|
|
185
|
+
"optgroup",
|
|
186
|
+
"option",
|
|
187
|
+
"output",
|
|
188
|
+
"p",
|
|
189
|
+
"param",
|
|
190
|
+
"path",
|
|
191
|
+
"pattern",
|
|
192
|
+
"picture",
|
|
193
|
+
"plaintext",
|
|
194
|
+
"polygon",
|
|
195
|
+
"polyline",
|
|
196
|
+
"pre",
|
|
197
|
+
"progress",
|
|
198
|
+
"q",
|
|
199
|
+
"radialGradient",
|
|
200
|
+
"rb",
|
|
201
|
+
"rbc",
|
|
202
|
+
"rect",
|
|
203
|
+
"rp",
|
|
204
|
+
"rt",
|
|
205
|
+
"rtc",
|
|
206
|
+
"ruby",
|
|
207
|
+
"s",
|
|
208
|
+
"samp",
|
|
209
|
+
"script",
|
|
210
|
+
"search",
|
|
211
|
+
"section",
|
|
212
|
+
"select",
|
|
213
|
+
"semantics",
|
|
214
|
+
"set",
|
|
215
|
+
"shadow",
|
|
216
|
+
"slot",
|
|
217
|
+
"small",
|
|
218
|
+
"source",
|
|
219
|
+
"spacer",
|
|
220
|
+
"span",
|
|
221
|
+
"stop",
|
|
222
|
+
"strike",
|
|
223
|
+
"strong",
|
|
224
|
+
"style",
|
|
225
|
+
"sub",
|
|
226
|
+
"summary",
|
|
227
|
+
"sup",
|
|
228
|
+
"svg",
|
|
229
|
+
"switch",
|
|
230
|
+
"symbol",
|
|
231
|
+
"table",
|
|
232
|
+
"tbody",
|
|
233
|
+
"td",
|
|
234
|
+
"template",
|
|
235
|
+
"text",
|
|
236
|
+
"textPath",
|
|
237
|
+
"textarea",
|
|
238
|
+
"tfoot",
|
|
239
|
+
"th",
|
|
240
|
+
"thead",
|
|
241
|
+
"time",
|
|
242
|
+
"title",
|
|
243
|
+
"tr",
|
|
244
|
+
"track",
|
|
245
|
+
"tref",
|
|
246
|
+
"tspan",
|
|
247
|
+
"tt",
|
|
248
|
+
"u",
|
|
249
|
+
"ul",
|
|
250
|
+
"use",
|
|
251
|
+
"var",
|
|
252
|
+
"video",
|
|
253
|
+
"view",
|
|
254
|
+
"vkern",
|
|
255
|
+
"wbr",
|
|
256
|
+
"xmp",
|
|
257
|
+
]);
|
package/dist/cjs/vite/index.js
CHANGED
|
@@ -8,7 +8,9 @@ const parser_1 = require("@babel/parser");
|
|
|
8
8
|
const magic_string_1 = __importDefault(require("magic-string"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const estree_walker_1 = require("estree-walker");
|
|
11
|
+
const domTagNames_js_1 = require("./domTagNames.js");
|
|
11
12
|
const validExtensions = new Set([".jsx", ".tsx"]);
|
|
13
|
+
const fragmentNames = new Set(["Fragment", "React.Fragment"]);
|
|
12
14
|
function getComponentName(elementName) {
|
|
13
15
|
if (elementName.type === "JSXIdentifier") {
|
|
14
16
|
return elementName.name;
|
|
@@ -20,8 +22,17 @@ function getComponentName(elementName) {
|
|
|
20
22
|
}
|
|
21
23
|
return null;
|
|
22
24
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
+
function classifyElement(name) {
|
|
26
|
+
if (fragmentNames.has(name)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (!/^[a-z]/.test(name) || name.includes(".")) {
|
|
30
|
+
return "component";
|
|
31
|
+
}
|
|
32
|
+
if (domTagNames_js_1.domTagNames.has(name) || name.includes("-")) {
|
|
33
|
+
return "dom";
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
25
36
|
}
|
|
26
37
|
function ziteId() {
|
|
27
38
|
const cwd = process.cwd();
|
|
@@ -46,21 +57,19 @@ function ziteId() {
|
|
|
46
57
|
if (node.type === "JSXElement") {
|
|
47
58
|
const openingElement = node.openingElement;
|
|
48
59
|
const children = node.children;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (isFragment) {
|
|
60
|
+
const componentName = getComponentName(openingElement.name);
|
|
61
|
+
if (!componentName) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const kind = classifyElement(componentName);
|
|
65
|
+
if (!kind) {
|
|
56
66
|
return;
|
|
57
67
|
}
|
|
58
68
|
const line = openingElement.loc?.start?.line ?? 0;
|
|
59
69
|
const col = openingElement.loc?.start?.column ?? 0;
|
|
60
70
|
const dataComponentId = `${relativePath}|${line}|${col}`;
|
|
61
71
|
let attributes = ` data-zite-id="${dataComponentId}"`;
|
|
62
|
-
|
|
63
|
-
if (componentName && !isNativeElement(componentName)) {
|
|
72
|
+
if (kind === "component") {
|
|
64
73
|
attributes += ` data-zite-component="${componentName}"`;
|
|
65
74
|
}
|
|
66
75
|
if (children.length === 1 &&
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const vitest_1 = require("vitest");
|
|
8
|
+
const index_js_1 = require("./index.js");
|
|
9
|
+
async function transform(code) {
|
|
10
|
+
const plugin = (0, index_js_1.ziteId)();
|
|
11
|
+
const handler = plugin.transform;
|
|
12
|
+
const result = await handler(code, path_1.default.join(process.cwd(), 'src/App.tsx'));
|
|
13
|
+
if (!result) {
|
|
14
|
+
throw new Error('transform returned null');
|
|
15
|
+
}
|
|
16
|
+
return result.code;
|
|
17
|
+
}
|
|
18
|
+
(0, vitest_1.describe)('ziteId', () => {
|
|
19
|
+
(0, vitest_1.it)('tags html and svg elements without a component name', async () => {
|
|
20
|
+
const code = await transform('const a = <div><svg><path d="M0" /></svg><p>Hello</p></div>;');
|
|
21
|
+
(0, vitest_1.expect)(code).toContain('<div data-zite-id="src/App.tsx|1|10">');
|
|
22
|
+
(0, vitest_1.expect)(code).toContain('<svg data-zite-id="src/App.tsx|1|15">');
|
|
23
|
+
(0, vitest_1.expect)(code).toContain('<path data-zite-id="src/App.tsx|1|20" d="M0" />');
|
|
24
|
+
(0, vitest_1.expect)(code).toContain('<p data-zite-id="src/App.tsx|1|41" data-zite-editable="true">');
|
|
25
|
+
(0, vitest_1.expect)(code).not.toContain('data-zite-component');
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.it)('tags components with their name', async () => {
|
|
28
|
+
const code = await transform('const a = <><Card /><motion.div /><_Private /></>;');
|
|
29
|
+
(0, vitest_1.expect)(code).toContain('<Card data-zite-id="src/App.tsx|1|12" data-zite-component="Card" />');
|
|
30
|
+
(0, vitest_1.expect)(code).toContain('<motion.div data-zite-id="src/App.tsx|1|20" data-zite-component="motion.div" />');
|
|
31
|
+
(0, vitest_1.expect)(code).toContain('<_Private data-zite-id="src/App.tsx|1|34" data-zite-component="_Private" />');
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)('tags MathML elements', async () => {
|
|
34
|
+
const code = await transform('const a = <math><mi>x</mi></math>;');
|
|
35
|
+
(0, vitest_1.expect)(code).toBe('const a = <math data-zite-id="src/App.tsx|1|10"><mi data-zite-id="src/App.tsx|1|16" data-zite-editable="true">x</mi></math>;');
|
|
36
|
+
});
|
|
37
|
+
(0, vitest_1.it)('tags custom elements', async () => {
|
|
38
|
+
const code = await transform('const a = <my-widget />;');
|
|
39
|
+
(0, vitest_1.expect)(code).toBe('const a = <my-widget data-zite-id="src/App.tsx|1|10" />;');
|
|
40
|
+
});
|
|
41
|
+
(0, vitest_1.it)('leaves non-DOM intrinsic elements untouched', async () => {
|
|
42
|
+
const source = 'const a = <group><mesh position-x={1} /><ambientLight intensity={0.5} /></group>;';
|
|
43
|
+
(0, vitest_1.expect)(await transform(source)).toBe(source);
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)('tags DOM elements nested inside non-DOM intrinsics', async () => {
|
|
46
|
+
const code = await transform('const a = <group><Html><div /></Html></group>;');
|
|
47
|
+
(0, vitest_1.expect)(code).toBe('const a = <group><Html data-zite-id="src/App.tsx|1|17" data-zite-component="Html"><div data-zite-id="src/App.tsx|1|23" /></Html></group>;');
|
|
48
|
+
});
|
|
49
|
+
(0, vitest_1.it)('leaves fragments untouched', async () => {
|
|
50
|
+
const source = 'const a = <Fragment><React.Fragment key="a" /></Fragment>;';
|
|
51
|
+
(0, vitest_1.expect)(await transform(source)).toBe(source);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -2,3 +2,6 @@ export declare function getFlowId(): string;
|
|
|
2
2
|
export declare function getApiUrl(): string;
|
|
3
3
|
export declare function getAuthPageUrl(): string;
|
|
4
4
|
export declare function getDbToken(): string;
|
|
5
|
+
/** Undefined for apps built before the platform sent this — treat as unknown,
|
|
6
|
+
* never as a default side. */
|
|
7
|
+
export declare function getAccessMode(): 'internal' | 'external' | undefined;
|
package/dist/esm/auth/config.js
CHANGED
|
@@ -30,3 +30,9 @@ export function getAuthPageUrl() {
|
|
|
30
30
|
export function getDbToken() {
|
|
31
31
|
return getEnv('ZITE_DB_TOKEN', 'VITE_ZITE_DB_TOKEN') ?? '';
|
|
32
32
|
}
|
|
33
|
+
/** Undefined for apps built before the platform sent this — treat as unknown,
|
|
34
|
+
* never as a default side. */
|
|
35
|
+
export function getAccessMode() {
|
|
36
|
+
const mode = getEnv('ZITE_ACCESS_MODE', 'VITE_ZITE_ACCESS_MODE');
|
|
37
|
+
return mode === 'internal' || mode === 'external' ? mode : undefined;
|
|
38
|
+
}
|
package/dist/esm/auth/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
1
2
|
import { createAuthClient } from 'better-auth/react';
|
|
2
3
|
import { magicLinkClient, inferAdditionalFields, } from 'better-auth/client/plugins';
|
|
3
4
|
import { USAGE_TOKEN_QUERY_PARAM } from './constants.js';
|
|
5
|
+
import { getAccessMode } from './config.js';
|
|
4
6
|
const authClient = createAuthClient({
|
|
5
7
|
baseURL: '',
|
|
6
8
|
plugins: [
|
|
@@ -14,6 +16,22 @@ const authClient = createAuthClient({
|
|
|
14
16
|
],
|
|
15
17
|
});
|
|
16
18
|
export const useSession = authClient.useSession;
|
|
19
|
+
/**
|
|
20
|
+
* An internal app has no signed-out state — SSO runs before the bundle — so an
|
|
21
|
+
* empty session means it died underneath the app. Only `internal`: elsewhere a
|
|
22
|
+
* signed-out visitor is legitimate.
|
|
23
|
+
*/
|
|
24
|
+
function shouldRedirectSignedOut(isPending, hasUser) {
|
|
25
|
+
if (isPending || hasUser)
|
|
26
|
+
return false;
|
|
27
|
+
if (getAccessMode() !== 'internal')
|
|
28
|
+
return false;
|
|
29
|
+
if (typeof window === 'undefined')
|
|
30
|
+
return false;
|
|
31
|
+
// `loginWithRedirect` won't navigate from an auth page, so claiming a
|
|
32
|
+
// redirect here would hang `isLoading` forever.
|
|
33
|
+
return !window.location.pathname.startsWith('/auth/');
|
|
34
|
+
}
|
|
17
35
|
/**
|
|
18
36
|
* `loginWithRedirect` and `logout` are returned as well as exported. The
|
|
19
37
|
* pre-monorepo `useAuth()` returned them, so app code that destructures them
|
|
@@ -22,9 +40,17 @@ export const useSession = authClient.useSession;
|
|
|
22
40
|
*/
|
|
23
41
|
export function useAuth() {
|
|
24
42
|
const { data, isPending } = useSession();
|
|
43
|
+
const user = data?.user ?? null;
|
|
44
|
+
const redirecting = shouldRedirectSignedOut(isPending, user !== null);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (redirecting)
|
|
47
|
+
loginWithRedirect();
|
|
48
|
+
}, [redirecting]);
|
|
25
49
|
return {
|
|
26
|
-
user
|
|
27
|
-
|
|
50
|
+
user,
|
|
51
|
+
// Loading, not signed out: the signed-out branch is the empty screen this
|
|
52
|
+
// exists to prevent.
|
|
53
|
+
isLoading: isPending || redirecting,
|
|
28
54
|
loginWithRedirect,
|
|
29
55
|
logout,
|
|
30
56
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, beforeEach, vi } from 'vitest';
|
|
2
|
+
// Separate from index.test.ts: driving `useSession` means replacing
|
|
3
|
+
// `better-auth/react`, which would strip the real client asserted against there.
|
|
4
|
+
const { useSessionMock } = vi.hoisted(() => ({ useSessionMock: vi.fn() }));
|
|
5
|
+
vi.mock('better-auth/react', () => ({
|
|
6
|
+
createAuthClient: () => ({
|
|
7
|
+
useSession: useSessionMock,
|
|
8
|
+
signIn: {},
|
|
9
|
+
signUp: {},
|
|
10
|
+
signOut: vi.fn(),
|
|
11
|
+
updateUser: vi.fn(),
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
vi.mock('better-auth/client/plugins', () => ({
|
|
15
|
+
magicLinkClient: () => ({}),
|
|
16
|
+
inferAdditionalFields: () => ({}),
|
|
17
|
+
}));
|
|
18
|
+
// `useAuth` uses only `useEffect`; running it inline avoids pulling in a renderer.
|
|
19
|
+
vi.mock('react', () => ({ useEffect: (fn) => fn() }));
|
|
20
|
+
import { useAuth } from './index.js';
|
|
21
|
+
const APP_URL = 'https://app.zite.so/dashboard';
|
|
22
|
+
function stubLocation(href) {
|
|
23
|
+
const location = { pathname: new URL(href).pathname, href };
|
|
24
|
+
vi.stubGlobal('window', { location });
|
|
25
|
+
return location;
|
|
26
|
+
}
|
|
27
|
+
const signedOut = { data: null, isPending: false };
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
delete process.env.ZITE_ACCESS_MODE;
|
|
30
|
+
});
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
delete process.env.ZITE_ACCESS_MODE;
|
|
33
|
+
vi.unstubAllGlobals();
|
|
34
|
+
useSessionMock.mockReset();
|
|
35
|
+
});
|
|
36
|
+
const render = (session, { accessMode, href = APP_URL } = {}) => {
|
|
37
|
+
if (accessMode !== undefined)
|
|
38
|
+
process.env.ZITE_ACCESS_MODE = accessMode;
|
|
39
|
+
const location = stubLocation(href);
|
|
40
|
+
useSessionMock.mockReturnValue(session);
|
|
41
|
+
return { result: useAuth(), location };
|
|
42
|
+
};
|
|
43
|
+
describe('useAuth signed-out handling', () => {
|
|
44
|
+
it('sends a signed-out visitor on an internal app to sign-in', () => {
|
|
45
|
+
const { result, location } = render(signedOut, { accessMode: 'internal' });
|
|
46
|
+
expect(location.href.startsWith('/auth/login')).toBe(true);
|
|
47
|
+
expect(result.isLoading).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it('leaves a signed-out visitor on an external app alone', () => {
|
|
50
|
+
const { result, location } = render(signedOut, { accessMode: 'external' });
|
|
51
|
+
expect(location.href).toBe(APP_URL);
|
|
52
|
+
expect(result.isLoading).toBe(false);
|
|
53
|
+
expect(result.user).toBe(null);
|
|
54
|
+
});
|
|
55
|
+
// i.e. every app published before the platform sent an access mode.
|
|
56
|
+
it('leaves an app that never declared an access mode alone', () => {
|
|
57
|
+
const { result, location } = render(signedOut);
|
|
58
|
+
expect(location.href).toBe(APP_URL);
|
|
59
|
+
expect(result.isLoading).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('does not redirect while the session is still resolving', () => {
|
|
62
|
+
const { result, location } = render({ data: null, isPending: true }, { accessMode: 'internal' });
|
|
63
|
+
expect(location.href).toBe(APP_URL);
|
|
64
|
+
expect(result.isLoading).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it('does not redirect a signed-in visitor', () => {
|
|
67
|
+
const { result, location } = render({ data: { user: { id: 'u1', email: 'a@b.com' } }, isPending: false }, { accessMode: 'internal' });
|
|
68
|
+
expect(location.href).toBe(APP_URL);
|
|
69
|
+
expect(result.isLoading).toBe(false);
|
|
70
|
+
expect(result.user).toMatchObject({ id: 'u1' });
|
|
71
|
+
});
|
|
72
|
+
// Claiming a redirect it can't make would hang `isLoading` forever.
|
|
73
|
+
it('does not claim to be loading on an auth page it cannot leave', () => {
|
|
74
|
+
const { result } = render(signedOut, {
|
|
75
|
+
accessMode: 'internal',
|
|
76
|
+
href: 'https://app.zite.so/auth/login',
|
|
77
|
+
});
|
|
78
|
+
expect(result.isLoading).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|