@yefengr/pi-reach 0.0.3 → 0.0.5
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/README.md +2 -2
- package/dist/attachments/reservation.fixture.d.ts +1 -0
- package/dist/attachments/reservation.fixture.js +25 -0
- package/dist/attachments/reservation.fixture.js.map +1 -0
- package/dist/attachments/reservations.d.ts +21 -0
- package/dist/attachments/reservations.js +189 -0
- package/dist/attachments/reservations.js.map +1 -0
- package/dist/attachments/safe-files.d.ts +7 -0
- package/dist/attachments/safe-files.js +66 -0
- package/dist/attachments/safe-files.js.map +1 -0
- package/dist/attachments/store.d.ts +55 -0
- package/dist/attachments/store.js +518 -0
- package/dist/attachments/store.js.map +1 -0
- package/dist/attachments/types.d.ts +48 -0
- package/dist/attachments/types.js +20 -0
- package/dist/attachments/types.js.map +1 -0
- package/dist/index.js +42 -35
- package/dist/index.js.map +1 -1
- package/dist/runtime/attachment_binding.d.ts +17 -0
- package/dist/runtime/attachment_binding.js +70 -0
- package/dist/runtime/attachment_binding.js.map +1 -0
- package/dist/runtime/create_owner_binding.d.ts +27 -0
- package/dist/runtime/create_owner_binding.js +22 -0
- package/dist/runtime/create_owner_binding.js.map +1 -0
- package/dist/timeline/attachment_content.d.ts +21 -0
- package/dist/timeline/attachment_content.js +83 -0
- package/dist/timeline/attachment_content.js.map +1 -0
- package/dist/timeline/attachment_delivery.d.ts +65 -0
- package/dist/timeline/attachment_delivery.js +150 -0
- package/dist/timeline/attachment_delivery.js.map +1 -0
- package/dist/timeline/runtime.d.ts +4 -2
- package/dist/timeline/runtime.js +33 -34
- package/dist/timeline/runtime.js.map +1 -1
- package/dist/timeline/user_delivery.d.ts +7 -0
- package/dist/timeline/user_delivery.js +21 -12
- package/dist/timeline/user_delivery.js.map +1 -1
- package/dist/timeline/user_delivery_binding.d.ts +6 -1
- package/dist/timeline/user_delivery_binding.js +17 -1
- package/dist/timeline/user_delivery_binding.js.map +1 -1
- package/dist/timeline/v2_service.d.ts +9 -1
- package/dist/timeline/v2_service.js +26 -8
- package/dist/timeline/v2_service.js.map +1 -1
- package/dist/vendor/protocol/outer/codec.js +3 -0
- package/dist/vendor/protocol/outer/types.d.ts +5 -1
- package/dist/vendor/protocol/session/attachments.d.ts +78 -0
- package/dist/vendor/protocol/session/attachments.js +150 -0
- package/dist/vendor/protocol/session/frames.d.ts +541 -0
- package/dist/vendor/protocol/session/frames.js +39 -4
- package/dist/vendor/protocol/session/index.d.ts +1 -0
- package/dist/vendor/protocol/session/index.js +1 -0
- package/dist/vendor/protocol/session/schema.d.ts +2 -2
- package/dist/vendor/protocol/session/schema.js +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { idSchema, imageMimeSchema, strictObject, textSchema, } from "./schema.js";
|
|
3
|
+
/** Hard per-file upload ceiling; also the maximum decoded original size. */
|
|
4
|
+
export const ATTACHMENT_MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
/** Hard per-message ceiling across every descriptor in one metadata record. */
|
|
6
|
+
export const ATTACHMENT_MAX_MESSAGE_BYTES = 100 * 1024 * 1024;
|
|
7
|
+
export const ATTACHMENT_MAX_COUNT = 10;
|
|
8
|
+
export const ATTACHMENT_CHUNK_BYTES = 64 * 1024;
|
|
9
|
+
export const ATTACHMENT_MAX_IN_FLIGHT = 2;
|
|
10
|
+
export const ATTACHMENT_PREVIEW_MAX_BYTES = 32 * 1024;
|
|
11
|
+
export const ATTACHMENT_PREVIEW_MAX_DIMENSION = 320;
|
|
12
|
+
/** custom_type used by the display-only attachment metadata custom event payload. */
|
|
13
|
+
export const ATTACHMENT_METADATA_TYPE = "pi-reach:attachments-v1";
|
|
14
|
+
/** custom_type used by the branch-anchored metadata/message binding custom event payload. */
|
|
15
|
+
export const ATTACHMENT_MESSAGE_TYPE = "pi-reach:attachment-message-v1";
|
|
16
|
+
const BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
17
|
+
const BINARY_STRING_CHUNK = 8192;
|
|
18
|
+
function toBinaryString(bytes) {
|
|
19
|
+
let binary = "";
|
|
20
|
+
for (let offset = 0; offset < bytes.byteLength; offset += BINARY_STRING_CHUNK) {
|
|
21
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + BINARY_STRING_CHUNK));
|
|
22
|
+
}
|
|
23
|
+
return binary;
|
|
24
|
+
}
|
|
25
|
+
function encodeCanonicalBase64(bytes) {
|
|
26
|
+
return btoa(toBinaryString(bytes));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Decodes a string only when it is *canonical* Base64: valid alphabet/padding and
|
|
30
|
+
* no non-zero trailing bits. Returns null for anything that would re-encode differently.
|
|
31
|
+
*/
|
|
32
|
+
function decodeCanonicalBase64(value) {
|
|
33
|
+
if (!BASE64_PATTERN.test(value))
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
const binary = atob(value);
|
|
37
|
+
const bytes = new Uint8Array(binary.length);
|
|
38
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
39
|
+
bytes[index] = binary.charCodeAt(index);
|
|
40
|
+
}
|
|
41
|
+
return encodeCanonicalBase64(bytes) === value ? bytes : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function canonicalBase64Schema(minBytes, maxBytes) {
|
|
48
|
+
const maxChars = 4 * Math.ceil(maxBytes / 3);
|
|
49
|
+
return z
|
|
50
|
+
.string()
|
|
51
|
+
.min(4)
|
|
52
|
+
.max(maxChars)
|
|
53
|
+
.regex(BASE64_PATTERN)
|
|
54
|
+
.superRefine((value, ctx) => {
|
|
55
|
+
const decoded = decodeCanonicalBase64(value);
|
|
56
|
+
if (decoded === null) {
|
|
57
|
+
ctx.addIssue({ code: "custom", message: "value must be canonical Base64" });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (decoded.byteLength < minBytes) {
|
|
61
|
+
ctx.addIssue({ code: "custom", message: `decoded value must be at least ${minBytes} bytes` });
|
|
62
|
+
}
|
|
63
|
+
if (decoded.byteLength > maxBytes) {
|
|
64
|
+
ctx.addIssue({ code: "custom", message: `decoded value must be at most ${maxBytes} bytes` });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Chunk payload for `attachment_chunk`; canonical Base64 whose decoded size is 1..64 KiB.
|
|
70
|
+
* Declared only as a codec-level constraint; the offset contract is enforced by the store.
|
|
71
|
+
*/
|
|
72
|
+
export const attachmentChunkDataSchema = canonicalBase64Schema(1, ATTACHMENT_CHUNK_BYTES);
|
|
73
|
+
/** Display-only file name; never used to derive a path. Rejects NUL and control characters. */
|
|
74
|
+
export const attachmentFileNameSchema = z
|
|
75
|
+
.string()
|
|
76
|
+
.min(1)
|
|
77
|
+
.max(255)
|
|
78
|
+
.regex(/^[^\p{Cc}]+$/u);
|
|
79
|
+
export const attachmentSha256Schema = z.string().regex(/^[0-9a-f]{64}$/);
|
|
80
|
+
/** 0 is a valid original size (empty files still upload). */
|
|
81
|
+
export const attachmentByteLengthSchema = z
|
|
82
|
+
.number()
|
|
83
|
+
.int()
|
|
84
|
+
.nonnegative()
|
|
85
|
+
.finite()
|
|
86
|
+
.max(ATTACHMENT_MAX_FILE_BYTES);
|
|
87
|
+
export const attachmentPreviewSchema = strictObject({
|
|
88
|
+
mime_type: z.literal("image/jpeg"),
|
|
89
|
+
data: canonicalBase64Schema(1, ATTACHMENT_PREVIEW_MAX_BYTES),
|
|
90
|
+
byte_length: z.number().int().min(1).finite().max(ATTACHMENT_PREVIEW_MAX_BYTES),
|
|
91
|
+
width: z.number().int().min(1).finite().max(ATTACHMENT_PREVIEW_MAX_DIMENSION),
|
|
92
|
+
height: z.number().int().min(1).finite().max(ATTACHMENT_PREVIEW_MAX_DIMENSION),
|
|
93
|
+
}).superRefine((preview, ctx) => {
|
|
94
|
+
const decoded = decodeCanonicalBase64(preview.data);
|
|
95
|
+
if (decoded !== null && decoded.byteLength !== preview.byte_length) {
|
|
96
|
+
ctx.addIssue({
|
|
97
|
+
code: "custom",
|
|
98
|
+
path: ["byte_length"],
|
|
99
|
+
message: "byte_length must equal the decoded preview byte length",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
export const attachmentDescriptorSchema = strictObject({
|
|
104
|
+
attachment_id: idSchema,
|
|
105
|
+
file_name: attachmentFileNameSchema,
|
|
106
|
+
mime_type: imageMimeSchema,
|
|
107
|
+
byte_length: attachmentByteLengthSchema,
|
|
108
|
+
sha256: attachmentSha256Schema,
|
|
109
|
+
preview: attachmentPreviewSchema.optional(),
|
|
110
|
+
});
|
|
111
|
+
export const attachmentIdsSchema = z
|
|
112
|
+
.array(idSchema)
|
|
113
|
+
.min(1)
|
|
114
|
+
.max(ATTACHMENT_MAX_COUNT)
|
|
115
|
+
.superRefine((ids, ctx) => {
|
|
116
|
+
if (new Set(ids).size !== ids.length) {
|
|
117
|
+
ctx.addIssue({ code: "custom", message: "attachment ids must be unique" });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
/** Display metadata carried in a `custom` timeline payload; never enters model input. */
|
|
121
|
+
export const attachmentMetadataSchema = strictObject({
|
|
122
|
+
version: z.literal(1),
|
|
123
|
+
client_request_id: idSchema,
|
|
124
|
+
sender_ref: idSchema,
|
|
125
|
+
text: textSchema,
|
|
126
|
+
attachments: z
|
|
127
|
+
.array(attachmentDescriptorSchema)
|
|
128
|
+
.min(1)
|
|
129
|
+
.max(ATTACHMENT_MAX_COUNT),
|
|
130
|
+
}).superRefine((metadata, ctx) => {
|
|
131
|
+
const ids = metadata.attachments.map((attachment) => attachment.attachment_id);
|
|
132
|
+
if (new Set(ids).size !== ids.length) {
|
|
133
|
+
ctx.addIssue({ code: "custom", path: ["attachments"], message: "attachment ids must be unique" });
|
|
134
|
+
}
|
|
135
|
+
const totalBytes = metadata.attachments.reduce((sum, attachment) => sum + attachment.byte_length, 0);
|
|
136
|
+
if (totalBytes > ATTACHMENT_MAX_MESSAGE_BYTES) {
|
|
137
|
+
ctx.addIssue({
|
|
138
|
+
code: "custom",
|
|
139
|
+
path: ["attachments"],
|
|
140
|
+
message: `attachment total must not exceed ${ATTACHMENT_MAX_MESSAGE_BYTES} bytes`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
/** Binds a metadata record to the committed message id so branches keep their attachment ancestor. */
|
|
145
|
+
export const attachmentMessageBindingSchema = strictObject({
|
|
146
|
+
version: z.literal(1),
|
|
147
|
+
client_request_id: idSchema,
|
|
148
|
+
sender_ref: idSchema,
|
|
149
|
+
message_id: idSchema,
|
|
150
|
+
});
|