@sudobility/screenwriter_types 0.1.1
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/CLAUDE.md +50 -0
- package/README.md +34 -0
- package/dist/account/index.d.ts +440 -0
- package/dist/account/index.js +274 -0
- package/dist/admin/index.d.ts +95 -0
- package/dist/admin/index.js +20 -0
- package/dist/ai/index.d.ts +564 -0
- package/dist/ai/index.js +179 -0
- package/dist/api/envelope.d.ts +33 -0
- package/dist/api/envelope.js +140 -0
- package/dist/api/pagination.d.ts +7 -0
- package/dist/api/pagination.js +9 -0
- package/dist/api/routes.d.ts +273 -0
- package/dist/api/routes.js +306 -0
- package/dist/assets/index.d.ts +722 -0
- package/dist/assets/index.js +452 -0
- package/dist/collab/index.d.ts +214 -0
- package/dist/collab/index.js +211 -0
- package/dist/commands/index.d.ts +92 -0
- package/dist/commands/index.js +20 -0
- package/dist/credits/index.d.ts +26 -0
- package/dist/credits/index.js +7 -0
- package/dist/formats/index.d.ts +87 -0
- package/dist/formats/index.js +26 -0
- package/dist/ids/index.d.ts +2 -0
- package/dist/ids/index.js +2 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/io/index.d.ts +395 -0
- package/dist/io/index.js +161 -0
- package/dist/jobs/index.d.ts +278 -0
- package/dist/jobs/index.js +138 -0
- package/dist/keys/index.d.ts +53 -0
- package/dist/keys/index.js +22 -0
- package/dist/lifecycle/index.d.ts +263 -0
- package/dist/lifecycle/index.js +148 -0
- package/dist/me/index.d.ts +35 -0
- package/dist/me/index.js +18 -0
- package/dist/packets/index.d.ts +887 -0
- package/dist/packets/index.js +312 -0
- package/dist/projects/index.d.ts +201 -0
- package/dist/projects/index.js +75 -0
- package/dist/public/index.d.ts +95 -0
- package/dist/public/index.js +25 -0
- package/dist/reads/index.d.ts +387 -0
- package/dist/reads/index.js +118 -0
- package/dist/reports/index.d.ts +168 -0
- package/dist/reports/index.js +466 -0
- package/dist/sharing/index.d.ts +256 -0
- package/dist/sharing/index.js +91 -0
- package/dist/sync/codec.d.ts +7 -0
- package/dist/sync/codec.js +91 -0
- package/dist/sync/index.d.ts +382 -0
- package/dist/sync/index.js +262 -0
- package/dist/sync/permissions.d.ts +22 -0
- package/dist/sync/permissions.js +11 -0
- package/dist/templates/index.d.ts +1699 -0
- package/dist/templates/index.js +70 -0
- package/dist/tenancy/index.d.ts +116 -0
- package/dist/tenancy/index.js +102 -0
- package/dist/test/index.d.ts +8 -0
- package/dist/test/index.js +88 -0
- package/dist/versions/index.d.ts +335 -0
- package/dist/versions/index.js +107 -0
- package/package.json +62 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenancy management and sharing DTOs (spec 05 §5.3, §5.4, §6.2-6.4, §6.7, §6.21, §8.3), slice B8.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { type Role } from '../tenancy/index.js';
|
|
6
|
+
export declare const TEAM_WORKSPACE_LIMIT = 50;
|
|
7
|
+
export declare const workspaceCreateSchema: z.ZodObject<{
|
|
8
|
+
name: z.ZodString;
|
|
9
|
+
avatarAssetId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type WorkspaceCreateRequest = z.infer<typeof workspaceCreateSchema>;
|
|
12
|
+
export declare const workspaceUpdateSchema: z.ZodObject<{
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
avatarAssetId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
15
|
+
defaultTemplateId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
|
+
defaultLanguage: z.ZodOptional<z.ZodString>;
|
|
17
|
+
aiEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
allowPublicLinks: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
export type WorkspaceUpdateRequest = z.infer<typeof workspaceUpdateSchema>;
|
|
21
|
+
export declare const workspaceDeleteSchema: z.ZodObject<{
|
|
22
|
+
confirmName: z.ZodString;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type WorkspaceDeleteRequest = z.infer<typeof workspaceDeleteSchema>;
|
|
25
|
+
/** `DELETE /workspaces/:wid` response (soft delete). */
|
|
26
|
+
export interface WorkspaceDeleteResponse {
|
|
27
|
+
deletedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export declare const workspaceTransferSchema: z.ZodObject<{
|
|
30
|
+
toUserId: z.ZodString;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
export type WorkspaceTransferRequest = z.infer<typeof workspaceTransferSchema>;
|
|
33
|
+
/** `GET /workspaces/:wid/audit.csv` query: an ISO-8601 range of at most one year. */
|
|
34
|
+
export declare const workspaceAuditQuerySchema: z.ZodObject<{
|
|
35
|
+
from: z.ZodOptional<z.ZodISODateTime>;
|
|
36
|
+
to: z.ZodOptional<z.ZodISODateTime>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export type WorkspaceAuditQuery = z.infer<typeof workspaceAuditQuerySchema>;
|
|
39
|
+
export declare const AUDIT_CSV_COLUMNS: readonly ["created_at", "actor", "action", "target_type", "target_id", "ip"];
|
|
40
|
+
export declare const AUDIT_MAX_RANGE_DAYS = 366;
|
|
41
|
+
export interface WorkspaceUsage {
|
|
42
|
+
storageBytes: number;
|
|
43
|
+
quotaBytes: number;
|
|
44
|
+
documentCount: number;
|
|
45
|
+
assetCount: number;
|
|
46
|
+
}
|
|
47
|
+
export interface Member {
|
|
48
|
+
userId: string;
|
|
49
|
+
email: string | null;
|
|
50
|
+
displayName: string | null;
|
|
51
|
+
role: Role;
|
|
52
|
+
invitedBy: string | null;
|
|
53
|
+
joinedAt: string;
|
|
54
|
+
}
|
|
55
|
+
export declare const memberUpdateSchema: z.ZodObject<{
|
|
56
|
+
role: z.ZodEnum<{
|
|
57
|
+
owner: "owner";
|
|
58
|
+
admin: "admin";
|
|
59
|
+
writer: "writer";
|
|
60
|
+
commenter: "commenter";
|
|
61
|
+
viewer: "viewer";
|
|
62
|
+
}>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
export type MemberUpdateRequest = z.infer<typeof memberUpdateSchema>;
|
|
65
|
+
export declare const INVITATION_TARGETS: readonly ["workspace", "project", "document"];
|
|
66
|
+
export type InvitationTarget = (typeof INVITATION_TARGETS)[number];
|
|
67
|
+
export declare const INVITATION_TTL_DAYS = 14;
|
|
68
|
+
export declare const INVITATION_STATUSES: readonly ["pending", "accepted", "declined", "cancelled", "expired"];
|
|
69
|
+
export type InvitationStatus = (typeof INVITATION_STATUSES)[number];
|
|
70
|
+
/** `role` for a workspace invitation is `admin|writer|commenter|viewer` (never `owner`); for a project or document it is a grant role. */
|
|
71
|
+
export declare const invitationCreateSchema: z.ZodObject<{
|
|
72
|
+
email: z.ZodEmail;
|
|
73
|
+
role: z.ZodEnum<{
|
|
74
|
+
owner: "owner";
|
|
75
|
+
admin: "admin";
|
|
76
|
+
writer: "writer";
|
|
77
|
+
commenter: "commenter";
|
|
78
|
+
viewer: "viewer";
|
|
79
|
+
}>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type InvitationCreateRequest = z.infer<typeof invitationCreateSchema>;
|
|
82
|
+
/** The token is never in this shape: it travels only in the invitation email (`notifyInvitation`, slice B12). */
|
|
83
|
+
export interface Invitation {
|
|
84
|
+
id: string;
|
|
85
|
+
targetType: InvitationTarget;
|
|
86
|
+
workspaceId: string;
|
|
87
|
+
projectId: string | null;
|
|
88
|
+
documentId: string | null;
|
|
89
|
+
email: string;
|
|
90
|
+
role: Role;
|
|
91
|
+
invitedBy: string;
|
|
92
|
+
/** Inviter's display name or email, and the target's name, so an invitee can recognise it. */
|
|
93
|
+
invitedByName: string | null;
|
|
94
|
+
targetName: string | null;
|
|
95
|
+
status: InvitationStatus;
|
|
96
|
+
expiresAt: string;
|
|
97
|
+
createdAt: string;
|
|
98
|
+
}
|
|
99
|
+
export declare const invitationAcceptSchema: z.ZodObject<{
|
|
100
|
+
token: z.ZodString;
|
|
101
|
+
}, z.core.$strip>;
|
|
102
|
+
export type InvitationAcceptRequest = z.infer<typeof invitationAcceptSchema>;
|
|
103
|
+
export interface InvitationAcceptResult {
|
|
104
|
+
workspaceId?: string;
|
|
105
|
+
projectId?: string;
|
|
106
|
+
documentId?: string;
|
|
107
|
+
}
|
|
108
|
+
/** Where an invitation, grant list or link list points. */
|
|
109
|
+
export type ShareTarget = {
|
|
110
|
+
type: 'workspace';
|
|
111
|
+
id: string;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'project';
|
|
114
|
+
id: string;
|
|
115
|
+
} | {
|
|
116
|
+
type: 'document';
|
|
117
|
+
id: string;
|
|
118
|
+
};
|
|
119
|
+
export interface ShareGrant {
|
|
120
|
+
id: string;
|
|
121
|
+
targetType: 'project' | 'document';
|
|
122
|
+
projectId: string | null;
|
|
123
|
+
documentId: string | null;
|
|
124
|
+
userId: string;
|
|
125
|
+
email: string | null;
|
|
126
|
+
displayName: string | null;
|
|
127
|
+
role: Role;
|
|
128
|
+
grantedBy: string | null;
|
|
129
|
+
createdAt: string;
|
|
130
|
+
updatedAt: string;
|
|
131
|
+
}
|
|
132
|
+
export declare const grantUpdateSchema: z.ZodObject<{
|
|
133
|
+
role: z.ZodEnum<{
|
|
134
|
+
owner: "owner";
|
|
135
|
+
admin: "admin";
|
|
136
|
+
writer: "writer";
|
|
137
|
+
commenter: "commenter";
|
|
138
|
+
viewer: "viewer";
|
|
139
|
+
}>;
|
|
140
|
+
}, z.core.$strip>;
|
|
141
|
+
export type GrantUpdateRequest = z.infer<typeof grantUpdateSchema>;
|
|
142
|
+
/** `GET /me/shared` item: a project or document reached through a grant, not through a workspace membership. */
|
|
143
|
+
export interface SharedItem {
|
|
144
|
+
type: 'project' | 'document';
|
|
145
|
+
id: string;
|
|
146
|
+
title: string;
|
|
147
|
+
role: Role;
|
|
148
|
+
workspaceId: string;
|
|
149
|
+
/** For a document: its project. */
|
|
150
|
+
projectId: string | null;
|
|
151
|
+
projectName: string | null;
|
|
152
|
+
sharedBy: string | null;
|
|
153
|
+
grantedAt: string;
|
|
154
|
+
}
|
|
155
|
+
export declare const SHARE_LINK_ACCESS: readonly ["view", "comment"];
|
|
156
|
+
export type ShareLinkAccess = (typeof SHARE_LINK_ACCESS)[number];
|
|
157
|
+
/** `anyone`: the URL alone opens it at `access`. `restricted`: only people on the access list (grants/membership). */
|
|
158
|
+
export declare const SHARE_LINK_GENERAL_ACCESS: readonly ["anyone", "restricted"];
|
|
159
|
+
export type ShareLinkGeneralAccess = (typeof SHARE_LINK_GENERAL_ACCESS)[number];
|
|
160
|
+
export declare const SHARE_LINK_TARGETS: readonly ["project", "document", "snapshot"];
|
|
161
|
+
export type ShareLinkTarget = (typeof SHARE_LINK_TARGETS)[number];
|
|
162
|
+
export declare const SHARE_LINK_TOKEN_PREFIX = "fws_";
|
|
163
|
+
/** Link session (`fls_...`) lifetime, seconds (spec 05 §5.4). */
|
|
164
|
+
export declare const LINK_SESSION_TTL_S = 900;
|
|
165
|
+
export declare const LINK_SESSION_PREFIX = "fls_";
|
|
166
|
+
export declare const shareLinkCreateSchema: z.ZodObject<{
|
|
167
|
+
access: z.ZodEnum<{
|
|
168
|
+
view: "view";
|
|
169
|
+
comment: "comment";
|
|
170
|
+
}>;
|
|
171
|
+
generalAccess: z.ZodDefault<z.ZodEnum<{
|
|
172
|
+
anyone: "anyone";
|
|
173
|
+
restricted: "restricted";
|
|
174
|
+
}>>;
|
|
175
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
176
|
+
password: z.ZodOptional<z.ZodString>;
|
|
177
|
+
allowDownload: z.ZodDefault<z.ZodBoolean>;
|
|
178
|
+
watermarkText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
179
|
+
}, z.core.$strip>;
|
|
180
|
+
export type ShareLinkCreateRequest = z.input<typeof shareLinkCreateSchema>;
|
|
181
|
+
export declare const shareLinkUpdateSchema: z.ZodObject<{
|
|
182
|
+
access: z.ZodOptional<z.ZodEnum<{
|
|
183
|
+
view: "view";
|
|
184
|
+
comment: "comment";
|
|
185
|
+
}>>;
|
|
186
|
+
generalAccess: z.ZodOptional<z.ZodEnum<{
|
|
187
|
+
anyone: "anyone";
|
|
188
|
+
restricted: "restricted";
|
|
189
|
+
}>>;
|
|
190
|
+
expiresAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
191
|
+
password: z.ZodOptional<z.ZodString>;
|
|
192
|
+
clearPassword: z.ZodOptional<z.ZodBoolean>;
|
|
193
|
+
allowDownload: z.ZodOptional<z.ZodBoolean>;
|
|
194
|
+
watermarkText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
195
|
+
}, z.core.$strip>;
|
|
196
|
+
export type ShareLinkUpdateRequest = z.infer<typeof shareLinkUpdateSchema>;
|
|
197
|
+
export interface ShareLink {
|
|
198
|
+
id: string;
|
|
199
|
+
targetType: ShareLinkTarget;
|
|
200
|
+
projectId: string | null;
|
|
201
|
+
documentId: string | null;
|
|
202
|
+
snapshotId: string | null;
|
|
203
|
+
access: ShareLinkAccess;
|
|
204
|
+
generalAccess: ShareLinkGeneralAccess;
|
|
205
|
+
/** Last 4 characters of the token, for the UI list. */
|
|
206
|
+
tokenHint: string;
|
|
207
|
+
hasPassword: boolean;
|
|
208
|
+
allowDownload: boolean;
|
|
209
|
+
watermarkText: string | null;
|
|
210
|
+
expiresAt: string | null;
|
|
211
|
+
revokedAt: string | null;
|
|
212
|
+
accessCount: number;
|
|
213
|
+
lastAccessedAt: string | null;
|
|
214
|
+
createdBy: string | null;
|
|
215
|
+
createdAt: string;
|
|
216
|
+
}
|
|
217
|
+
/** The full token (and so the URL) is returned once, at creation. */
|
|
218
|
+
export type ShareLinkCreated = ShareLink & {
|
|
219
|
+
url: string;
|
|
220
|
+
token: string;
|
|
221
|
+
};
|
|
222
|
+
export interface ShareLinkRevokeResponse {
|
|
223
|
+
revokedAt: string;
|
|
224
|
+
}
|
|
225
|
+
/** `GET /public/share/:token` (no content, no auth). A restricted link hides the title. */
|
|
226
|
+
export interface PublicShareInfo {
|
|
227
|
+
targetType: ShareLinkTarget;
|
|
228
|
+
title: string | null;
|
|
229
|
+
access: ShareLinkAccess;
|
|
230
|
+
generalAccess: ShareLinkGeneralAccess;
|
|
231
|
+
requiresPassword: boolean;
|
|
232
|
+
/** True for a `comment` link: adding a note needs a signed-in user. */
|
|
233
|
+
requiresSignInToComment: boolean;
|
|
234
|
+
expiresAt: string | null;
|
|
235
|
+
allowDownload: boolean;
|
|
236
|
+
}
|
|
237
|
+
export declare const shareUnlockSchema: z.ZodObject<{
|
|
238
|
+
password: z.ZodOptional<z.ZodString>;
|
|
239
|
+
}, z.core.$strip>;
|
|
240
|
+
export type ShareUnlockRequest = z.infer<typeof shareUnlockSchema>;
|
|
241
|
+
/** Also the way to obtain (and renew) a session for a password-less link. Send `linkSession` as `Authorization: Bearer` on `/share/:token/*` and as the sync `link` auth token. */
|
|
242
|
+
export interface ShareUnlockResponse {
|
|
243
|
+
linkSession: string;
|
|
244
|
+
expiresAt: string;
|
|
245
|
+
/** What the session allows: `comment` only for a comment link opened by a signed-in user. */
|
|
246
|
+
access: ShareLinkAccess;
|
|
247
|
+
}
|
|
248
|
+
export declare const UNLOCK_SESSION_TTL_S = 1800;
|
|
249
|
+
export declare const REAUTH_MAX_AGE_S = 300;
|
|
250
|
+
export interface DocumentLockResponse {
|
|
251
|
+
locked: boolean;
|
|
252
|
+
}
|
|
253
|
+
export interface UnlockSession {
|
|
254
|
+
sessionToken: string;
|
|
255
|
+
expiresAt: string;
|
|
256
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenancy management and sharing DTOs (spec 05 §5.3, §5.4, §6.2-6.4, §6.7, §6.21, §8.3), slice B8.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { roleSchema } from '../tenancy/index.js';
|
|
6
|
+
// ─── Workspaces ─────────────────────────────────────────────────────────────
|
|
7
|
+
export const TEAM_WORKSPACE_LIMIT = 50;
|
|
8
|
+
export const workspaceCreateSchema = z.object({
|
|
9
|
+
name: z.string().trim().min(1).max(80),
|
|
10
|
+
avatarAssetId: z.string().min(1).nullable().optional(),
|
|
11
|
+
});
|
|
12
|
+
export const workspaceUpdateSchema = z.object({
|
|
13
|
+
name: z.string().trim().min(1).max(80).optional(),
|
|
14
|
+
avatarAssetId: z.string().min(1).nullable().optional(),
|
|
15
|
+
defaultTemplateId: z.string().min(1).nullable().optional(),
|
|
16
|
+
defaultLanguage: z.string().min(2).max(35).optional(),
|
|
17
|
+
aiEnabled: z.boolean().optional(),
|
|
18
|
+
allowPublicLinks: z.boolean().optional(),
|
|
19
|
+
});
|
|
20
|
+
export const workspaceDeleteSchema = z.object({ confirmName: z.string() });
|
|
21
|
+
export const workspaceTransferSchema = z.object({
|
|
22
|
+
toUserId: z.string().min(1),
|
|
23
|
+
});
|
|
24
|
+
/** `GET /workspaces/:wid/audit.csv` query: an ISO-8601 range of at most one year. */
|
|
25
|
+
export const workspaceAuditQuerySchema = z.object({
|
|
26
|
+
from: z.iso.datetime({ offset: true }).optional(),
|
|
27
|
+
to: z.iso.datetime({ offset: true }).optional(),
|
|
28
|
+
});
|
|
29
|
+
export const AUDIT_CSV_COLUMNS = [
|
|
30
|
+
'created_at',
|
|
31
|
+
'actor',
|
|
32
|
+
'action',
|
|
33
|
+
'target_type',
|
|
34
|
+
'target_id',
|
|
35
|
+
'ip',
|
|
36
|
+
];
|
|
37
|
+
export const AUDIT_MAX_RANGE_DAYS = 366;
|
|
38
|
+
export const memberUpdateSchema = z.object({ role: roleSchema });
|
|
39
|
+
// ─── Invitations ────────────────────────────────────────────────────────────
|
|
40
|
+
export const INVITATION_TARGETS = ['workspace', 'project', 'document'];
|
|
41
|
+
export const INVITATION_TTL_DAYS = 14;
|
|
42
|
+
export const INVITATION_STATUSES = [
|
|
43
|
+
'pending',
|
|
44
|
+
'accepted',
|
|
45
|
+
'declined',
|
|
46
|
+
'cancelled',
|
|
47
|
+
'expired',
|
|
48
|
+
];
|
|
49
|
+
/** `role` for a workspace invitation is `admin|writer|commenter|viewer` (never `owner`); for a project or document it is a grant role. */
|
|
50
|
+
export const invitationCreateSchema = z.object({
|
|
51
|
+
email: z.email().max(254),
|
|
52
|
+
role: roleSchema,
|
|
53
|
+
});
|
|
54
|
+
export const invitationAcceptSchema = z.object({ token: z.string().min(1) });
|
|
55
|
+
export const grantUpdateSchema = z.object({ role: roleSchema });
|
|
56
|
+
// ─── Share links ────────────────────────────────────────────────────────────
|
|
57
|
+
export const SHARE_LINK_ACCESS = ['view', 'comment'];
|
|
58
|
+
/** `anyone`: the URL alone opens it at `access`. `restricted`: only people on the access list (grants/membership). */
|
|
59
|
+
export const SHARE_LINK_GENERAL_ACCESS = ['anyone', 'restricted'];
|
|
60
|
+
export const SHARE_LINK_TARGETS = ['project', 'document', 'snapshot'];
|
|
61
|
+
export const SHARE_LINK_TOKEN_PREFIX = 'fws_';
|
|
62
|
+
/** Link session (`fls_...`) lifetime, seconds (spec 05 §5.4). */
|
|
63
|
+
export const LINK_SESSION_TTL_S = 900;
|
|
64
|
+
export const LINK_SESSION_PREFIX = 'fls_';
|
|
65
|
+
export const shareLinkCreateSchema = z.object({
|
|
66
|
+
access: z.enum(SHARE_LINK_ACCESS),
|
|
67
|
+
generalAccess: z.enum(SHARE_LINK_GENERAL_ACCESS).default('anyone'),
|
|
68
|
+
expiresAt: z.iso.datetime({ offset: true }).nullable().optional(),
|
|
69
|
+
password: z.string().min(1).max(200).optional(),
|
|
70
|
+
allowDownload: z.boolean().default(true),
|
|
71
|
+
watermarkText: z.string().max(200).nullable().optional(),
|
|
72
|
+
});
|
|
73
|
+
export const shareLinkUpdateSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
access: z.enum(SHARE_LINK_ACCESS).optional(),
|
|
76
|
+
generalAccess: z.enum(SHARE_LINK_GENERAL_ACCESS).optional(),
|
|
77
|
+
expiresAt: z.iso.datetime({ offset: true }).nullable().optional(),
|
|
78
|
+
password: z.string().min(1).max(200).optional(),
|
|
79
|
+
clearPassword: z.boolean().optional(),
|
|
80
|
+
allowDownload: z.boolean().optional(),
|
|
81
|
+
watermarkText: z.string().max(200).nullable().optional(),
|
|
82
|
+
})
|
|
83
|
+
.refine((v) => !(v.password !== undefined && v.clearPassword), {
|
|
84
|
+
message: 'password and clearPassword are exclusive',
|
|
85
|
+
});
|
|
86
|
+
export const shareUnlockSchema = z.object({
|
|
87
|
+
password: z.string().max(200).optional(),
|
|
88
|
+
});
|
|
89
|
+
// ─── Document lock ──────────────────────────────────────────────────────────
|
|
90
|
+
export const UNLOCK_SESSION_TTL_S = 1800;
|
|
91
|
+
export const REAUTH_MAX_AGE_S = 300;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type SyncFrame } from './index.js';
|
|
2
|
+
export declare class SyncCodecError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function encodeFrame(frame: SyncFrame): Uint8Array;
|
|
6
|
+
/** Throws `SyncCodecError` on any malformed or schema-invalid frame. */
|
|
7
|
+
export declare function decodeFrame(bytes: Uint8Array): SyncFrame;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary wire codec for sync frames (spec 03 §2.2). Shared by the API and the frontend client.
|
|
3
|
+
* Platform-free: only `lib0` (pure JS), no Node built-ins, no DOM.
|
|
4
|
+
*
|
|
5
|
+
* frame := channelId:varuint messageType:varuint payload
|
|
6
|
+
*
|
|
7
|
+
* Payload by type:
|
|
8
|
+
* - JSON UTF-8 (the JSON text fills the rest of the frame): auth, authOk, authError, reauth, ping/pong, subscribe, subscribed, subscribeError,
|
|
9
|
+
* unsubscribe, ack, reject, epochChanged, roleChanged, documentDeleted, normalized,
|
|
10
|
+
* serverShutdown.
|
|
11
|
+
* - syncStep1 / syncStep2 / awareness: `varuint8array` (the y-protocols payload, which is
|
|
12
|
+
* exactly what follows the message-type tag in y-protocols/sync and /awareness).
|
|
13
|
+
* - update: `epoch:varuint clientSeq:varuint update:varuint8array`.
|
|
14
|
+
*
|
|
15
|
+
* ping and pong share code 4. `decodeFrame` returns type `ping` for it; a heartbeat frame is a
|
|
16
|
+
* liveness signal only and must never be answered (otherwise the two sides would echo forever).
|
|
17
|
+
*/
|
|
18
|
+
import * as decoding from 'lib0/decoding';
|
|
19
|
+
import * as encoding from 'lib0/encoding';
|
|
20
|
+
import * as string from 'lib0/string';
|
|
21
|
+
import { SYNC_MESSAGE_TYPES, syncFrameSchema, } from './index.js';
|
|
22
|
+
export class SyncCodecError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = 'SyncCodecError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const BINARY_TYPES = new Set(['syncStep1', 'syncStep2', 'awareness']);
|
|
29
|
+
const NAME_BY_CODE = new Map();
|
|
30
|
+
for (const [name, code] of Object.entries(SYNC_MESSAGE_TYPES)) {
|
|
31
|
+
// ping and pong share a code: the first registration (ping) wins on decode.
|
|
32
|
+
if (!NAME_BY_CODE.has(code))
|
|
33
|
+
NAME_BY_CODE.set(code, name);
|
|
34
|
+
}
|
|
35
|
+
export function encodeFrame(frame) {
|
|
36
|
+
const enc = encoding.createEncoder();
|
|
37
|
+
encoding.writeVarUint(enc, frame.channelId);
|
|
38
|
+
encoding.writeVarUint(enc, SYNC_MESSAGE_TYPES[frame.type]);
|
|
39
|
+
if (BINARY_TYPES.has(frame.type)) {
|
|
40
|
+
encoding.writeVarUint8Array(enc, frame.payload);
|
|
41
|
+
}
|
|
42
|
+
else if (frame.type === 'update') {
|
|
43
|
+
encoding.writeVarUint(enc, frame.payload.epoch);
|
|
44
|
+
encoding.writeVarUint(enc, frame.payload.clientSeq);
|
|
45
|
+
encoding.writeVarUint8Array(enc, frame.payload.update);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
encoding.writeUint8Array(enc, string.encodeUtf8(JSON.stringify(frame.payload)));
|
|
49
|
+
}
|
|
50
|
+
return encoding.toUint8Array(enc);
|
|
51
|
+
}
|
|
52
|
+
/** Throws `SyncCodecError` on any malformed or schema-invalid frame. */
|
|
53
|
+
export function decodeFrame(bytes) {
|
|
54
|
+
let candidate;
|
|
55
|
+
try {
|
|
56
|
+
const dec = decoding.createDecoder(bytes);
|
|
57
|
+
const channelId = decoding.readVarUint(dec);
|
|
58
|
+
const code = decoding.readVarUint(dec);
|
|
59
|
+
const type = NAME_BY_CODE.get(code);
|
|
60
|
+
if (!type)
|
|
61
|
+
throw new SyncCodecError(`unknown message type ${code}`);
|
|
62
|
+
let payload;
|
|
63
|
+
if (BINARY_TYPES.has(type)) {
|
|
64
|
+
payload = decoding.readVarUint8Array(dec);
|
|
65
|
+
if (decoding.hasContent(dec))
|
|
66
|
+
throw new SyncCodecError('trailing bytes');
|
|
67
|
+
}
|
|
68
|
+
else if (type === 'update') {
|
|
69
|
+
const epoch = decoding.readVarUint(dec);
|
|
70
|
+
const clientSeq = decoding.readVarUint(dec);
|
|
71
|
+
const update = decoding.readVarUint8Array(dec);
|
|
72
|
+
if (decoding.hasContent(dec))
|
|
73
|
+
throw new SyncCodecError('trailing bytes');
|
|
74
|
+
payload = { epoch, clientSeq, update };
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const rest = decoding.readTailAsUint8Array(dec);
|
|
78
|
+
payload = JSON.parse(string.decodeUtf8(rest));
|
|
79
|
+
}
|
|
80
|
+
candidate = { channelId, type, payload };
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
if (e instanceof SyncCodecError)
|
|
84
|
+
throw e;
|
|
85
|
+
throw new SyncCodecError(`malformed frame: ${e instanceof Error ? e.message : String(e)}`);
|
|
86
|
+
}
|
|
87
|
+
const parsed = syncFrameSchema.safeParse(candidate);
|
|
88
|
+
if (!parsed.success)
|
|
89
|
+
throw new SyncCodecError(`invalid frame: ${parsed.error.message}`);
|
|
90
|
+
return parsed.data;
|
|
91
|
+
}
|