founder-schema 0.4.0 → 0.5.0

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.
Files changed (44) hide show
  1. package/README.md +26 -1
  2. package/dist/core/errors.d.ts +13 -1
  3. package/dist/core/errors.d.ts.map +1 -1
  4. package/dist/core/errors.js +27 -3
  5. package/dist/core/errors.js.map +1 -1
  6. package/dist/core/limits.d.ts +27 -4
  7. package/dist/core/limits.d.ts.map +1 -1
  8. package/dist/core/limits.js +27 -4
  9. package/dist/core/limits.js.map +1 -1
  10. package/dist/core/responses.d.ts +12 -0
  11. package/dist/core/responses.d.ts.map +1 -1
  12. package/dist/core/validation.d.ts +2 -0
  13. package/dist/core/validation.d.ts.map +1 -1
  14. package/dist/core/validation.js +2 -0
  15. package/dist/core/validation.js.map +1 -1
  16. package/dist/domains/account/account.d.ts +2 -0
  17. package/dist/domains/account/account.d.ts.map +1 -1
  18. package/dist/domains/account/account.js +2 -0
  19. package/dist/domains/account/account.js.map +1 -1
  20. package/dist/domains/feed/feed.d.ts +62 -25
  21. package/dist/domains/feed/feed.d.ts.map +1 -1
  22. package/dist/domains/feed/feed.js +23 -20
  23. package/dist/domains/feed/feed.js.map +1 -1
  24. package/dist/domains/media/media.d.ts +134 -0
  25. package/dist/domains/media/media.d.ts.map +1 -0
  26. package/dist/domains/media/media.js +110 -0
  27. package/dist/domains/media/media.js.map +1 -0
  28. package/dist/domains/onboarding/onboarding.d.ts +11 -11
  29. package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
  30. package/dist/domains/onboarding/onboarding.js +15 -15
  31. package/dist/domains/onboarding/onboarding.js.map +1 -1
  32. package/dist/domains/profile/founder-profile.d.ts +24 -0
  33. package/dist/domains/profile/founder-profile.d.ts.map +1 -1
  34. package/dist/domains/profile/founder-profile.js +23 -1
  35. package/dist/domains/profile/founder-profile.js.map +1 -1
  36. package/dist/domains/uploads/uploads.d.ts +52 -64
  37. package/dist/domains/uploads/uploads.d.ts.map +1 -1
  38. package/dist/domains/uploads/uploads.js +43 -60
  39. package/dist/domains/uploads/uploads.js.map +1 -1
  40. package/dist/index.d.ts +4 -3
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +6 -4
  43. package/dist/index.js.map +1 -1
  44. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * founder-schema — Media: what an uploaded image becomes
3
+ *
4
+ * THE PIPELINE, end to end (0.5.0):
5
+ *
6
+ * 1. The client shrinks the photo (long edge at most the largest size in
7
+ * `MEDIA_SIZES`, WebP or JPEG) and asks for an upload slot:
8
+ * `POST /api/v1/uploads/presign` with a purpose, a content type and the
9
+ * exact byte count (`uploadPresignBodySchema`).
10
+ * 2. It PUTs the bytes straight to storage, with the headers it was given.
11
+ * Nothing passes through the API server.
12
+ * 3. It calls `POST /api/v1/uploads/:mediaId/complete`. The server checks the
13
+ * file, decodes it fully, applies the photo's rotation, REMOVES EVERY
14
+ * PIECE OF METADATA (GPS position, camera, timestamps), re-encodes it as
15
+ * WebP at each size in `MEDIA_SIZES`, deletes the raw upload, and answers
16
+ * with `uploadedMediaSchema`.
17
+ * 4. A post (`attachedMediaSchema`) or the avatar (`setAvatarBodySchema`,
18
+ * onboarding's `avatarMediaId`) takes the media id. One upload is used
19
+ * once, by its owner, for the purpose it was uploaded for.
20
+ *
21
+ * What nobody sees: the raw upload. It sits in a folder the CDN cannot reach
22
+ * and is deleted when processing finishes, or by storage itself after a day.
23
+ * Only the re-encoded sizes are ever served.
24
+ *
25
+ * An image never attached is deleted after `MEDIA_UNATTACHED_TTL_HOURS`; a
26
+ * deleted post's images, a replaced avatar, and an erased account's images are
27
+ * deleted from storage by the server, so a copied link stops working.
28
+ *
29
+ * @module founder-schema/domains/media
30
+ */
31
+ import { z } from 'zod';
32
+ /** What an upload is for. It decides the sizes made and where it may be attached. */
33
+ export declare const MEDIA_PURPOSE_VALUES: readonly ["post", "avatar"];
34
+ export declare const mediaPurposeSchema: z.ZodEnum<{
35
+ post: "post";
36
+ avatar: "avatar";
37
+ }>;
38
+ export type MediaPurpose = z.infer<typeof mediaPurposeSchema>;
39
+ /**
40
+ * The content types a member may upload. Every one is decoded and re-encoded
41
+ * to WebP before anyone sees it, so this list is about what the server can
42
+ * DECODE safely, not what it serves.
43
+ *
44
+ * Not HEIC: an iPhone's photo picker converts HEIC to JPEG for the web, and a
45
+ * client that shrinks the photo first re-encodes it anyway. SVG never: it is a
46
+ * document that can carry script, not an image.
47
+ */
48
+ export declare const MEDIA_UPLOAD_CONTENT_TYPES: readonly ["image/jpeg", "image/png", "image/webp", "image/gif"];
49
+ export declare const mediaUploadContentTypeSchema: z.ZodEnum<{
50
+ "image/jpeg": "image/jpeg";
51
+ "image/png": "image/png";
52
+ "image/webp": "image/webp";
53
+ "image/gif": "image/gif";
54
+ }>;
55
+ export type MediaUploadContentType = z.infer<typeof mediaUploadContentTypeSchema>;
56
+ /**
57
+ * The sizes each purpose is stored at, smallest first.
58
+ *
59
+ * - post: the LONG edge, aspect kept. 540 for a feed card on a plain screen,
60
+ * 1080 for the same card on a phone's dense screen, 2048 for the full-screen
61
+ * view. An image smaller than a size is never enlarged, so a small image
62
+ * has fewer sizes.
63
+ * - avatar: a SQUARE, cropped around the most interesting region (usually a
64
+ * face). 192 for every small circle (bylines, lists, chat) up to 64 CSS
65
+ * pixels on a 3x screen, 512 for a profile header.
66
+ *
67
+ * A client shrinks a photo to the largest size here before uploading: anything
68
+ * bigger is thrown away by the server anyway, and costs the member data.
69
+ */
70
+ export declare const MEDIA_SIZES: {
71
+ readonly post: readonly [540, 1080, 2048];
72
+ readonly avatar: readonly [192, 512];
73
+ };
74
+ /** One stored size of an image. */
75
+ export declare const mediaVariantSchema: z.ZodObject<{
76
+ width: z.ZodNumber;
77
+ height: z.ZodNumber;
78
+ url: z.ZodURL;
79
+ }, z.core.$strip>;
80
+ export type MediaVariant = z.infer<typeof mediaVariantSchema>;
81
+ /**
82
+ * A processed image, as every read shows it.
83
+ *
84
+ * `variants` is a ready-made `srcset`: smallest first, each with its own pixel
85
+ * width. `url`, `width` and `height` repeat the largest one, so a client that
86
+ * ignores `variants` still renders correctly and can reserve the image's box
87
+ * from the aspect ratio before it loads (no layout shift).
88
+ *
89
+ * `placeholder` is a ThumbHash (base64): a ~30 character blurred preview a
90
+ * client can paint instantly while the image loads.
91
+ *
92
+ * URLs point at our media CDN. They come from our own pipeline, never from a
93
+ * member, so a plain `z.url()`.
94
+ */
95
+ export declare const mediaImageSchema: z.ZodObject<{
96
+ id: z.ZodString;
97
+ url: z.ZodURL;
98
+ width: z.ZodNumber;
99
+ height: z.ZodNumber;
100
+ variants: z.ZodArray<z.ZodObject<{
101
+ width: z.ZodNumber;
102
+ height: z.ZodNumber;
103
+ url: z.ZodURL;
104
+ }, z.core.$strip>>;
105
+ placeholder: z.ZodOptional<z.ZodString>;
106
+ }, z.core.$strip>;
107
+ export type MediaImage = z.infer<typeof mediaImageSchema>;
108
+ /**
109
+ * `POST /api/v1/uploads/:mediaId/complete` answers with this: the image as it
110
+ * will appear, not yet attached to anything.
111
+ *
112
+ * `expiresAt` is when the server deletes it if it is still unattached
113
+ * (`MEDIA_UNATTACHED_TTL_HOURS` after the slot was issued). A composer left
114
+ * open longer than that must upload again.
115
+ */
116
+ export declare const uploadedMediaSchema: z.ZodObject<{
117
+ id: z.ZodString;
118
+ url: z.ZodURL;
119
+ width: z.ZodNumber;
120
+ height: z.ZodNumber;
121
+ variants: z.ZodArray<z.ZodObject<{
122
+ width: z.ZodNumber;
123
+ height: z.ZodNumber;
124
+ url: z.ZodURL;
125
+ }, z.core.$strip>>;
126
+ placeholder: z.ZodOptional<z.ZodString>;
127
+ purpose: z.ZodEnum<{
128
+ post: "post";
129
+ avatar: "avatar";
130
+ }>;
131
+ expiresAt: z.ZodISODateTime;
132
+ }, z.core.$strip>;
133
+ export type UploadedMedia = z.infer<typeof uploadedMediaSchema>;
134
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/domains/media/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,qFAAqF;AACrF,eAAO,MAAM,oBAAoB,6BAA8B,CAAC;AAChE,eAAO,MAAM,kBAAkB;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,iEAK7B,CAAC;AACX,eAAO,MAAM,4BAA4B;;;;;EAAqC,CAAC;AAC/E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW;;;CAGoC,CAAC;AAE7D,mCAAmC;AACnC,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;iBAO3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;iBAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * founder-schema — Media: what an uploaded image becomes
3
+ *
4
+ * THE PIPELINE, end to end (0.5.0):
5
+ *
6
+ * 1. The client shrinks the photo (long edge at most the largest size in
7
+ * `MEDIA_SIZES`, WebP or JPEG) and asks for an upload slot:
8
+ * `POST /api/v1/uploads/presign` with a purpose, a content type and the
9
+ * exact byte count (`uploadPresignBodySchema`).
10
+ * 2. It PUTs the bytes straight to storage, with the headers it was given.
11
+ * Nothing passes through the API server.
12
+ * 3. It calls `POST /api/v1/uploads/:mediaId/complete`. The server checks the
13
+ * file, decodes it fully, applies the photo's rotation, REMOVES EVERY
14
+ * PIECE OF METADATA (GPS position, camera, timestamps), re-encodes it as
15
+ * WebP at each size in `MEDIA_SIZES`, deletes the raw upload, and answers
16
+ * with `uploadedMediaSchema`.
17
+ * 4. A post (`attachedMediaSchema`) or the avatar (`setAvatarBodySchema`,
18
+ * onboarding's `avatarMediaId`) takes the media id. One upload is used
19
+ * once, by its owner, for the purpose it was uploaded for.
20
+ *
21
+ * What nobody sees: the raw upload. It sits in a folder the CDN cannot reach
22
+ * and is deleted when processing finishes, or by storage itself after a day.
23
+ * Only the re-encoded sizes are ever served.
24
+ *
25
+ * An image never attached is deleted after `MEDIA_UNATTACHED_TTL_HOURS`; a
26
+ * deleted post's images, a replaced avatar, and an erased account's images are
27
+ * deleted from storage by the server, so a copied link stops working.
28
+ *
29
+ * @module founder-schema/domains/media
30
+ */
31
+ import { z } from 'zod';
32
+ import { MEDIA_PLACEHOLDER_MAX } from '../../core/limits.js';
33
+ /** What an upload is for. It decides the sizes made and where it may be attached. */
34
+ export const MEDIA_PURPOSE_VALUES = ['post', 'avatar'];
35
+ export const mediaPurposeSchema = z.enum(MEDIA_PURPOSE_VALUES);
36
+ /**
37
+ * The content types a member may upload. Every one is decoded and re-encoded
38
+ * to WebP before anyone sees it, so this list is about what the server can
39
+ * DECODE safely, not what it serves.
40
+ *
41
+ * Not HEIC: an iPhone's photo picker converts HEIC to JPEG for the web, and a
42
+ * client that shrinks the photo first re-encodes it anyway. SVG never: it is a
43
+ * document that can carry script, not an image.
44
+ */
45
+ export const MEDIA_UPLOAD_CONTENT_TYPES = [
46
+ 'image/jpeg',
47
+ 'image/png',
48
+ 'image/webp',
49
+ 'image/gif',
50
+ ];
51
+ export const mediaUploadContentTypeSchema = z.enum(MEDIA_UPLOAD_CONTENT_TYPES);
52
+ /**
53
+ * The sizes each purpose is stored at, smallest first.
54
+ *
55
+ * - post: the LONG edge, aspect kept. 540 for a feed card on a plain screen,
56
+ * 1080 for the same card on a phone's dense screen, 2048 for the full-screen
57
+ * view. An image smaller than a size is never enlarged, so a small image
58
+ * has fewer sizes.
59
+ * - avatar: a SQUARE, cropped around the most interesting region (usually a
60
+ * face). 192 for every small circle (bylines, lists, chat) up to 64 CSS
61
+ * pixels on a 3x screen, 512 for a profile header.
62
+ *
63
+ * A client shrinks a photo to the largest size here before uploading: anything
64
+ * bigger is thrown away by the server anyway, and costs the member data.
65
+ */
66
+ export const MEDIA_SIZES = {
67
+ post: [540, 1080, 2048],
68
+ avatar: [192, 512],
69
+ };
70
+ /** One stored size of an image. */
71
+ export const mediaVariantSchema = z.object({
72
+ width: z.number().int().positive(),
73
+ height: z.number().int().positive(),
74
+ url: z.url(),
75
+ });
76
+ /**
77
+ * A processed image, as every read shows it.
78
+ *
79
+ * `variants` is a ready-made `srcset`: smallest first, each with its own pixel
80
+ * width. `url`, `width` and `height` repeat the largest one, so a client that
81
+ * ignores `variants` still renders correctly and can reserve the image's box
82
+ * from the aspect ratio before it loads (no layout shift).
83
+ *
84
+ * `placeholder` is a ThumbHash (base64): a ~30 character blurred preview a
85
+ * client can paint instantly while the image loads.
86
+ *
87
+ * URLs point at our media CDN. They come from our own pipeline, never from a
88
+ * member, so a plain `z.url()`.
89
+ */
90
+ export const mediaImageSchema = z.object({
91
+ id: z.string(),
92
+ url: z.url(),
93
+ width: z.number().int().positive(),
94
+ height: z.number().int().positive(),
95
+ variants: z.array(mediaVariantSchema).min(1).max(MEDIA_SIZES.post.length),
96
+ placeholder: z.string().max(MEDIA_PLACEHOLDER_MAX).optional(),
97
+ });
98
+ /**
99
+ * `POST /api/v1/uploads/:mediaId/complete` answers with this: the image as it
100
+ * will appear, not yet attached to anything.
101
+ *
102
+ * `expiresAt` is when the server deletes it if it is still unattached
103
+ * (`MEDIA_UNATTACHED_TTL_HOURS` after the slot was issued). A composer left
104
+ * open longer than that must upload again.
105
+ */
106
+ export const uploadedMediaSchema = mediaImageSchema.extend({
107
+ purpose: mediaPurposeSchema,
108
+ expiresAt: z.iso.datetime(),
109
+ });
110
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/domains/media/media.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,qFAAqF;AACrF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAU,CAAC;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,WAAW;CACH,CAAC;AACX,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAG/E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC;IACvB,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;CACwC,CAAC;AAE7D,mCAAmC;AACnC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACnC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE;CACb,CAAC,CAAC;AAGH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE;IACZ,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACnC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;IACzE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;CAC9D,CAAC,CAAC;AAGH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC,MAAM,CAAC;IACzD,OAAO,EAAE,kBAAkB;IAC3B,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC","sourcesContent":["/**\n * founder-schema — Media: what an uploaded image becomes\n *\n * THE PIPELINE, end to end (0.5.0):\n *\n * 1. The client shrinks the photo (long edge at most the largest size in\n * `MEDIA_SIZES`, WebP or JPEG) and asks for an upload slot:\n * `POST /api/v1/uploads/presign` with a purpose, a content type and the\n * exact byte count (`uploadPresignBodySchema`).\n * 2. It PUTs the bytes straight to storage, with the headers it was given.\n * Nothing passes through the API server.\n * 3. It calls `POST /api/v1/uploads/:mediaId/complete`. The server checks the\n * file, decodes it fully, applies the photo's rotation, REMOVES EVERY\n * PIECE OF METADATA (GPS position, camera, timestamps), re-encodes it as\n * WebP at each size in `MEDIA_SIZES`, deletes the raw upload, and answers\n * with `uploadedMediaSchema`.\n * 4. A post (`attachedMediaSchema`) or the avatar (`setAvatarBodySchema`,\n * onboarding's `avatarMediaId`) takes the media id. One upload is used\n * once, by its owner, for the purpose it was uploaded for.\n *\n * What nobody sees: the raw upload. It sits in a folder the CDN cannot reach\n * and is deleted when processing finishes, or by storage itself after a day.\n * Only the re-encoded sizes are ever served.\n *\n * An image never attached is deleted after `MEDIA_UNATTACHED_TTL_HOURS`; a\n * deleted post's images, a replaced avatar, and an erased account's images are\n * deleted from storage by the server, so a copied link stops working.\n *\n * @module founder-schema/domains/media\n */\n\nimport { z } from 'zod';\nimport { MEDIA_PLACEHOLDER_MAX } from '../../core/limits.js';\n\n/** What an upload is for. It decides the sizes made and where it may be attached. */\nexport const MEDIA_PURPOSE_VALUES = ['post', 'avatar'] as const;\nexport const mediaPurposeSchema = z.enum(MEDIA_PURPOSE_VALUES);\nexport type MediaPurpose = z.infer<typeof mediaPurposeSchema>;\n\n/**\n * The content types a member may upload. Every one is decoded and re-encoded\n * to WebP before anyone sees it, so this list is about what the server can\n * DECODE safely, not what it serves.\n *\n * Not HEIC: an iPhone's photo picker converts HEIC to JPEG for the web, and a\n * client that shrinks the photo first re-encodes it anyway. SVG never: it is a\n * document that can carry script, not an image.\n */\nexport const MEDIA_UPLOAD_CONTENT_TYPES = [\n 'image/jpeg',\n 'image/png',\n 'image/webp',\n 'image/gif',\n] as const;\nexport const mediaUploadContentTypeSchema = z.enum(MEDIA_UPLOAD_CONTENT_TYPES);\nexport type MediaUploadContentType = z.infer<typeof mediaUploadContentTypeSchema>;\n\n/**\n * The sizes each purpose is stored at, smallest first.\n *\n * - post: the LONG edge, aspect kept. 540 for a feed card on a plain screen,\n * 1080 for the same card on a phone's dense screen, 2048 for the full-screen\n * view. An image smaller than a size is never enlarged, so a small image\n * has fewer sizes.\n * - avatar: a SQUARE, cropped around the most interesting region (usually a\n * face). 192 for every small circle (bylines, lists, chat) up to 64 CSS\n * pixels on a 3x screen, 512 for a profile header.\n *\n * A client shrinks a photo to the largest size here before uploading: anything\n * bigger is thrown away by the server anyway, and costs the member data.\n */\nexport const MEDIA_SIZES = {\n post: [540, 1080, 2048],\n avatar: [192, 512],\n} as const satisfies Record<MediaPurpose, readonly number[]>;\n\n/** One stored size of an image. */\nexport const mediaVariantSchema = z.object({\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n url: z.url(),\n});\nexport type MediaVariant = z.infer<typeof mediaVariantSchema>;\n\n/**\n * A processed image, as every read shows it.\n *\n * `variants` is a ready-made `srcset`: smallest first, each with its own pixel\n * width. `url`, `width` and `height` repeat the largest one, so a client that\n * ignores `variants` still renders correctly and can reserve the image's box\n * from the aspect ratio before it loads (no layout shift).\n *\n * `placeholder` is a ThumbHash (base64): a ~30 character blurred preview a\n * client can paint instantly while the image loads.\n *\n * URLs point at our media CDN. They come from our own pipeline, never from a\n * member, so a plain `z.url()`.\n */\nexport const mediaImageSchema = z.object({\n id: z.string(),\n url: z.url(),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n variants: z.array(mediaVariantSchema).min(1).max(MEDIA_SIZES.post.length),\n placeholder: z.string().max(MEDIA_PLACEHOLDER_MAX).optional(),\n});\nexport type MediaImage = z.infer<typeof mediaImageSchema>;\n\n/**\n * `POST /api/v1/uploads/:mediaId/complete` answers with this: the image as it\n * will appear, not yet attached to anything.\n *\n * `expiresAt` is when the server deletes it if it is still unattached\n * (`MEDIA_UNATTACHED_TTL_HOURS` after the slot was issued). A composer left\n * open longer than that must upload again.\n */\nexport const uploadedMediaSchema = mediaImageSchema.extend({\n purpose: mediaPurposeSchema,\n expiresAt: z.iso.datetime(),\n});\nexport type UploadedMedia = z.infer<typeof uploadedMediaSchema>;\n"]}
@@ -274,7 +274,7 @@ export declare const onboardingFormSchema: z.ZodObject<{
274
274
  dateOfBirth: z.ZodString;
275
275
  headline: z.ZodString;
276
276
  about: z.ZodString;
277
- avatarUploadKey: z.ZodNullable<z.ZodString>;
277
+ avatarMediaId: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
278
278
  skills: z.ZodArray<z.ZodEnum<{
279
279
  engineering: "engineering";
280
280
  product: "product";
@@ -442,8 +442,8 @@ export declare const EMPTY_ONBOARDING_VENTURE: OnboardingForm['venture'];
442
442
  * Every field is checked against its own vocabulary and cut to its own limit;
443
443
  * one that does not fit becomes "not answered" (`''`, `null`, `[]`) and the
444
444
  * rest of the draft survives. Never coerced into a different answer: a value
445
- * outside a vocabulary is dropped, not mapped to the nearest one. A photo key
446
- * is never restored (see `avatarUploadKey`), and agreement only counts for the
445
+ * outside a vocabulary is dropped, not mapped to the nearest one. A photo
446
+ * is never restored (see `avatarMediaId`), and agreement only counts for the
447
447
  * CURRENT guidelines.
448
448
  *
449
449
  * It fills in no answer the member did not give: a blank country stays blank.
@@ -478,7 +478,7 @@ export declare const completeOnboardingBodySchema: z.ZodObject<{
478
478
  dateOfBirth: z.ZodString;
479
479
  headline: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
480
480
  about: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
481
- avatarUploadKey: z.ZodOptional<z.ZodString>;
481
+ avatarMediaId: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
482
482
  phone: z.ZodString;
483
483
  links: z.ZodOptional<z.ZodObject<{
484
484
  linkedin: z.ZodOptional<z.ZodURL>;
@@ -684,7 +684,7 @@ export declare const ONBOARDING_STEP_SCHEMAS: {
684
684
  prefer_not_to_say: "prefer_not_to_say";
685
685
  }>;
686
686
  dateOfBirth: z.ZodString;
687
- avatarUploadKey: z.ZodOptional<z.ZodString>;
687
+ avatarMediaId: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
688
688
  }, z.core.$strip>;
689
689
  readonly contact: z.ZodObject<{
690
690
  links: z.ZodOptional<z.ZodObject<{
@@ -898,12 +898,12 @@ export declare const onboardingDraftKeySchema: z.ZodEnum<{
898
898
  interests: "interests";
899
899
  gender: "gender";
900
900
  dateOfBirth: "dateOfBirth";
901
- avatarUploadKey: "avatarUploadKey";
901
+ avatarMediaId: "avatarMediaId";
902
902
  phone: "phone";
903
903
  businessType: "businessType";
904
904
  guidelinesVersion: "guidelinesVersion";
905
905
  }>;
906
- export declare const ONBOARDING_DRAFT_KEYS: ("location" | "about" | "displayName" | "headline" | "venture" | "skills" | "goals" | "links" | "interests" | "gender" | "dateOfBirth" | "avatarUploadKey" | "phone" | "businessType" | "guidelinesVersion")[];
906
+ export declare const ONBOARDING_DRAFT_KEYS: ("location" | "about" | "displayName" | "headline" | "venture" | "skills" | "goals" | "links" | "interests" | "gender" | "dateOfBirth" | "avatarMediaId" | "phone" | "businessType" | "guidelinesVersion")[];
907
907
  export type OnboardingDraftKey = z.infer<typeof onboardingDraftKeySchema>;
908
908
  /**
909
909
  * Where the member got to, kept in its own collection, separate from the
@@ -932,7 +932,7 @@ export declare const onboardingDraftSchema: z.ZodRecord<z.ZodEnum<{
932
932
  interests: "interests";
933
933
  gender: "gender";
934
934
  dateOfBirth: "dateOfBirth";
935
- avatarUploadKey: "avatarUploadKey";
935
+ avatarMediaId: "avatarMediaId";
936
936
  phone: "phone";
937
937
  businessType: "businessType";
938
938
  guidelinesVersion: "guidelinesVersion";
@@ -952,7 +952,7 @@ export declare const onboardingProgressSchema: z.ZodObject<{
952
952
  interests: "interests";
953
953
  gender: "gender";
954
954
  dateOfBirth: "dateOfBirth";
955
- avatarUploadKey: "avatarUploadKey";
955
+ avatarMediaId: "avatarMediaId";
956
956
  phone: "phone";
957
957
  businessType: "businessType";
958
958
  guidelinesVersion: "guidelinesVersion";
@@ -978,7 +978,7 @@ export declare const onboardingProgressUpdateSchema: z.ZodObject<{
978
978
  interests: "interests";
979
979
  gender: "gender";
980
980
  dateOfBirth: "dateOfBirth";
981
- avatarUploadKey: "avatarUploadKey";
981
+ avatarMediaId: "avatarMediaId";
982
982
  phone: "phone";
983
983
  businessType: "businessType";
984
984
  guidelinesVersion: "guidelinesVersion";
@@ -1008,7 +1008,7 @@ export declare const onboardingStatusSchema: z.ZodObject<{
1008
1008
  interests: "interests";
1009
1009
  gender: "gender";
1010
1010
  dateOfBirth: "dateOfBirth";
1011
- avatarUploadKey: "avatarUploadKey";
1011
+ avatarMediaId: "avatarMediaId";
1012
1012
  phone: "phone";
1013
1013
  businessType: "businessType";
1014
1014
  guidelinesVersion: "guidelinesVersion";
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmCxB;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,qEAAqE;AACrE,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAgB1E;AAED,4DAA4D;AAC5D,eAAO,MAAM,aAAa,aAGqC,CAAC;AAChE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAYpD;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,aAG2D,CAAC;AAC1F,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,eAAO,MAAM,oBAAoB,2CAA4C,CAAC;AAC9E,eAAO,MAAM,kBAAkB;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,YAAY,iiCA6Ef,CAAC;AACX,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAuB,CAAC;AAChD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,yEAOtB,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,gBAAgB,CAAC;IACtB,oFAAoF;IACpF,QAAQ,EAAE,mBAAmB,gBAAgB,EAAE,CAAC;IAChD,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CAC1B;AAQD,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAmFpD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,aAAa,KAAK,CAAC;AAIhC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,8FAQvB,CAAC;AACX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAItE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAG,YAAqB,CAAC;AACxD,eAAO,MAAM,uBAAuB,4BAElC,CAAC;AAIH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,QAA+B,CAAC;AAIjE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsD/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAqFlE,mDAAmD;AACnD,eAAO,MAAM,wBAAwB,EAAE,cAAc,CAAC,SAAS,CAM9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAGlE;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyDvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,8DAA8D;AAC9D,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAIvF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmBuB,CAAC;AAE5D,wFAAwF;AACxF,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,IAAI,CAAC,CAAC,KAAK,CACnE,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC,CAAC,CACpC,CAAC;AAEF,gCAAgC;AAChC,MAAM,MAAM,eAAe,GAAG,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,sFAAsF;IACtF,GAAG,EAAE,iBAAiB,CAAC;IACvB,sEAAsE;IACtE,QAAQ,EAAE,oBAAoB,iBAAiB,QAAQ,CAAC;IACxD,OAAO,EAAE,oBAAoB,iBAAiB,OAAO,CAAC;IACtD,4DAA4D;IAC5D,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,wBAAwB,EAAE,SAAS,oBAAoB,EAWnE,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAkC,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,EAAE,SAAS,CAAC,SAAS,eAAe,EAAE,CAAC,EAC3B,CAAC;AAEhD,kFAAkF;AAClF,eAAO,MAAM,gCAAgC;;;iBAG3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAI1F;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;EAA+B,CAAC;AACrE,eAAO,MAAM,qBAAqB,8MAAmC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;mCAAyD,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;iBAGnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;iBAGzC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;iBAIjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -24,11 +24,10 @@
24
24
  * @module founder-schema/domains/onboarding
25
25
  */
26
26
  import { z } from 'zod';
27
- import { GENDER_VALUES, GOAL_VALUES, genderSchema, goalSchema, INDUSTRY_VALUES, industrySchema, locationSchema, optionalTrimmedString, phoneSchema, SKILL_VALUES, STARTUP_STAGE_VALUES, skillSchema, startupStageSchema, } from '../../core/common.js';
27
+ import { GENDER_VALUES, GOAL_VALUES, genderSchema, goalSchema, INDUSTRY_VALUES, industrySchema, locationSchema, objectIdSchema, optionalTrimmedString, phoneSchema, SKILL_VALUES, STARTUP_STAGE_VALUES, skillSchema, startupStageSchema, } from '../../core/common.js';
28
28
  import { ABOUT_MAX, CITY_MAX, COUNTRY_MAX, DISPLAY_NAME_MAX, GOALS_MIN, HEADLINE_MAX, LINK_URL_MAX, SKILLS_MAX, STATE_MAX, TEAM_SIZE_MAX, VENTURE_NAME_MAX, } from '../../core/limits.js';
29
29
  import { VALIDATION_MESSAGE } from '../../core/validation.js';
30
30
  import { founderLinksSchema, ventureSchema } from '../profile/founder-profile.js';
31
- import { uploadKeySchema } from '../uploads/uploads.js';
32
31
  // ─── Age ─────────────────────────────────────────────────────────────────────
33
32
  /**
34
33
  * 18, ruled by the user 2026-09-21. Under-18 would pull in parental-consent
@@ -399,12 +398,13 @@ export const onboardingFormSchema = z.object({
399
398
  headline: z.string().max(HEADLINE_MAX),
400
399
  about: z.string().max(ABOUT_MAX),
401
400
  /**
402
- * Allowed as a key, and NEVER restored by `readOnboardingDraft`: an upload
403
- * not attached within a day is deleted by the storage lifecycle rule, so a
404
- * key from an older draft would fail at Finish, on the last screen, about a
405
- * photo picked days ago. Clients hold it for the visit and do not save it.
401
+ * The completed upload's id (`media`, purpose `avatar`). Allowed in the form,
402
+ * and NEVER restored by `readOnboardingDraft`: an upload not attached within
403
+ * `MEDIA_UNATTACHED_TTL_HOURS` is deleted, so an id from an older draft would
404
+ * fail at Finish, on the last screen, about a photo picked days ago. Clients
405
+ * hold it for the visit and do not save it.
406
406
  */
407
- avatarUploadKey: uploadKeySchema.nullable(),
407
+ avatarMediaId: objectIdSchema.nullable(),
408
408
  skills: z.array(skillSchema).max(SKILLS_MAX),
409
409
  /** As typed; the finish body takes `phoneSchema`'s ten digits. */
410
410
  phone: z.string(),
@@ -476,7 +476,7 @@ const draftReaderSchema = z.object({
476
476
  dateOfBirth: draftText(DATE_INPUT_MAX),
477
477
  headline: draftText(HEADLINE_MAX),
478
478
  about: draftText(ABOUT_MAX),
479
- avatarUploadKey: z
479
+ avatarMediaId: z
480
480
  .unknown()
481
481
  .optional()
482
482
  .transform(() => null),
@@ -525,8 +525,8 @@ export const EMPTY_ONBOARDING_VENTURE = {
525
525
  * Every field is checked against its own vocabulary and cut to its own limit;
526
526
  * one that does not fit becomes "not answered" (`''`, `null`, `[]`) and the
527
527
  * rest of the draft survives. Never coerced into a different answer: a value
528
- * outside a vocabulary is dropped, not mapped to the nearest one. A photo key
529
- * is never restored (see `avatarUploadKey`), and agreement only counts for the
528
+ * outside a vocabulary is dropped, not mapped to the nearest one. A photo
529
+ * is never restored (see `avatarMediaId`), and agreement only counts for the
530
530
  * CURRENT guidelines.
531
531
  *
532
532
  * It fills in no answer the member did not give: a blank country stays blank.
@@ -578,11 +578,11 @@ export const completeOnboardingBodySchema = z.object({
578
578
  headline: optionalTrimmedString(HEADLINE_MAX),
579
579
  about: optionalTrimmedString(ABOUT_MAX),
580
580
  /**
581
- * The key the presign handed out, in the minted format. It was a bare
582
- * string until 0.2.0, so `'../../etc/passwd'` passed this schema and only
583
- * the server's own commit check stood in the way.
581
+ * A completed avatar upload (`media`, purpose `avatar`). The server attaches
582
+ * it inside the same transaction that creates the profile, so a refused
583
+ * Finish leaves the upload unattached (and swept), never half-used.
584
584
  */
585
- avatarUploadKey: uploadKeySchema.optional(),
585
+ avatarMediaId: objectIdSchema.optional(),
586
586
  phone: phoneSchema,
587
587
  links: contactLinksSchema.optional(),
588
588
  /** Absent when the member skipped the step or refused the permission. */
@@ -629,7 +629,7 @@ export const ONBOARDING_STEP_SCHEMAS = {
629
629
  dateOfBirth: true,
630
630
  headline: true,
631
631
  about: true,
632
- avatarUploadKey: true,
632
+ avatarMediaId: true,
633
633
  // Here because ruling D8 folded the design's orphan "title, achievements,
634
634
  // skills" screen into step one. It was on no step until 0.2.0, which made
635
635
  // it the one finish field no step owned.