@12-apps/storage 2.4.0 → 2.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.
- package/package.json +3 -2
- package/src/en-US.ts +30 -0
- package/src/index.ts +2 -0
- package/src/locales.ts +31 -0
- package/src/react/en-US.ts +49 -0
- package/src/react/index.ts +2 -0
- package/src/react/locales.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/storage",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Image uploads that land at YOUR OWN ORIGIN, never at the bucket (12-20). Isomorphic core (format allowlist, upload ceiling, object-key grammar, rendition set, refusal taxonomy, wire schemas), host-mounted backend surface (./server: createApiStorage, the local-disk driver, the image-pipeline port, the orphan reclaim), Hono adapter (./hono), the S3-compatible driver behind its own subpath (./s3) and the bound picker + upload hook (./react). Standardized adoption contract in ADOPTING.md.",
|
|
6
6
|
"exports": {
|
|
@@ -43,11 +43,12 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@12-apps/ui": "^6.
|
|
46
|
+
"@12-apps/ui": "^6.9.0",
|
|
47
47
|
"zod": "^4.3.5"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@12-apps/eslint-config": "^1.21.1",
|
|
51
|
+
"@12-apps/i18n": "^1.0.0",
|
|
51
52
|
"@12-apps/typescript-config": "^1.20.1",
|
|
52
53
|
"@12-apps/wiring": "^1.10.0",
|
|
53
54
|
"@aws-sdk/client-s3": "^3.703.0",
|
package/src/en-US.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { StorageMessageContext, StorageMessages } from './problems';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack — a NAMED factory a host passes by hand
|
|
5
|
+
* (`messages: EN_US_STORAGE_MESSAGES`), never a default. The filename is what
|
|
6
|
+
* exempts this file from the copy-portability gate, as `pt-BR.ts` beside it is
|
|
7
|
+
* exempt.
|
|
8
|
+
*
|
|
9
|
+
* `limit` is interpolated rather than hard-coded, for the reason the pt-BR pack
|
|
10
|
+
* gives: a host that raises `maxBytes` must not end up with a message naming
|
|
11
|
+
* the old number, which is the single commonest way this kind of copy goes
|
|
12
|
+
* stale. A translation that inlined the number would reintroduce exactly that.
|
|
13
|
+
*/
|
|
14
|
+
export function EN_US_STORAGE_MESSAGES(context: StorageMessageContext): StorageMessages {
|
|
15
|
+
return {
|
|
16
|
+
invalid_base64: 'That image is not correctly base64-encoded.',
|
|
17
|
+
empty_file: 'That image is empty.',
|
|
18
|
+
file_too_large: `That image is larger than the ${context.limit} limit.`,
|
|
19
|
+
content_mismatch:
|
|
20
|
+
'The image contents do not match the format you declared. Send a PNG, JPEG, WebP or GIF.',
|
|
21
|
+
unsupported_content_type: 'Unsupported format. Send a PNG, JPG, WebP or GIF.',
|
|
22
|
+
image_unreadable: 'That image could not be read — the file appears to be corrupted.',
|
|
23
|
+
image_dimensions_too_large: 'That image is too large in dimensions to process.',
|
|
24
|
+
storage_not_configured: 'Image storage has not been set up for this store yet.',
|
|
25
|
+
storage_unavailable: 'That image could not be saved right now. Try again in a moment.',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The 401 the mounted router answers when the host resolves no actor. */
|
|
30
|
+
export const EN_US_STORAGE_UNAUTHENTICATED = 'Not authenticated.';
|
package/src/index.ts
CHANGED
|
@@ -76,6 +76,8 @@ export {
|
|
|
76
76
|
type StorageProblem,
|
|
77
77
|
} from './problems';
|
|
78
78
|
export { PT_BR_STORAGE_MESSAGES, PT_BR_STORAGE_UNAUTHENTICATED } from './pt-BR';
|
|
79
|
+
export { EN_US_STORAGE_MESSAGES, EN_US_STORAGE_UNAUTHENTICATED } from './en-US';
|
|
80
|
+
export { STORAGE_MESSAGES, STORAGE_UNAUTHENTICATED } from './locales';
|
|
79
81
|
|
|
80
82
|
export {
|
|
81
83
|
imageSources,
|
package/src/locales.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EN_US_STORAGE_MESSAGES, EN_US_STORAGE_UNAUTHENTICATED } from './en-US';
|
|
2
|
+
import type { StorageMessageContext, StorageMessages } from './problems';
|
|
3
|
+
import { PT_BR_STORAGE_MESSAGES, PT_BR_STORAGE_UNAUTHENTICATED } from './pt-BR';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Both languages, keyed by tag — what a host hands to `@12-apps/i18n` when the
|
|
7
|
+
* reader's language is a property of the request rather than of the deployment.
|
|
8
|
+
*
|
|
9
|
+
* The pack holds FACTORIES rather than tables, because that is what this
|
|
10
|
+
* package's copy already is: `limit` is interpolated so a host that raises
|
|
11
|
+
* `maxBytes` cannot end up with a message naming the old number. `localeCopy`
|
|
12
|
+
* therefore hands back a factory, and the host calls it with its context:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const messages = localeCopy(STORAGE_MESSAGES)({ locale })({ limit });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* `LocalePack` is mirrored here rather than imported so the package stays
|
|
19
|
+
* liftable into a repo that has never heard of `@12-apps/i18n`.
|
|
20
|
+
*/
|
|
21
|
+
type LocalePack<T> = { readonly 'pt-BR': T; readonly 'en-US': T };
|
|
22
|
+
|
|
23
|
+
export const STORAGE_MESSAGES = {
|
|
24
|
+
'pt-BR': PT_BR_STORAGE_MESSAGES,
|
|
25
|
+
'en-US': EN_US_STORAGE_MESSAGES,
|
|
26
|
+
} as const satisfies LocalePack<(context: StorageMessageContext) => StorageMessages>;
|
|
27
|
+
|
|
28
|
+
export const STORAGE_UNAUTHENTICATED = {
|
|
29
|
+
'pt-BR': PT_BR_STORAGE_UNAUTHENTICATED,
|
|
30
|
+
'en-US': EN_US_STORAGE_UNAUTHENTICATED,
|
|
31
|
+
} as const satisfies LocalePack<string>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { WebStorageMessageContext, WebStorageMessages } from './failures';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The en-US pack — a NAMED factory a host passes by hand, never a default.
|
|
5
|
+
* `limit` is interpolated for the same reason the server pack does it: a host
|
|
6
|
+
* that raises the ceiling must not end up with copy naming the old number.
|
|
7
|
+
*/
|
|
8
|
+
export function EN_US_WEB_STORAGE_MESSAGES(
|
|
9
|
+
context: WebStorageMessageContext,
|
|
10
|
+
): WebStorageMessages {
|
|
11
|
+
return {
|
|
12
|
+
forbidden: 'Your account may not upload images for this store.',
|
|
13
|
+
unsupported_content_type: 'Unsupported format. Send a PNG, JPG, WebP or GIF.',
|
|
14
|
+
file_too_large: `That image is too large. The limit is ${context.limit}.`,
|
|
15
|
+
invalid_key: 'That image could not be found.',
|
|
16
|
+
not_found: 'That image is no longer available.',
|
|
17
|
+
session_expired: 'Your session has expired. Sign in again and repeat the upload.',
|
|
18
|
+
transport: 'Could not reach the server. Check your connection and try again.',
|
|
19
|
+
empty_file: 'Empty file (0 bytes). Pick the image again.',
|
|
20
|
+
file_too_large_upfront: ({ size, limit }) =>
|
|
21
|
+
`That image is too large: ${size}. The limit is ${limit} — shrink it and try again.`,
|
|
22
|
+
unknown_content_type: 'Could not identify the file type. Send a PNG, JPG or WebP.',
|
|
23
|
+
unsupported_content_type_upfront: ({ contentType }) =>
|
|
24
|
+
`Unsupported format (${contentType}). Send a PNG, JPG, WebP or GIF.`,
|
|
25
|
+
missing_key: 'The server accepted the image but returned no key. Try again.',
|
|
26
|
+
upload_failed_http: ({ status, detail }) =>
|
|
27
|
+
`Could not upload that image (HTTP ${status}${detail}). Try again in a moment.`,
|
|
28
|
+
upload_failed: 'Uploading that image failed. Try again in a moment.',
|
|
29
|
+
|
|
30
|
+
pageTitle: 'Store images',
|
|
31
|
+
pageIntro: `Send a PNG, JPG, WebP or GIF of up to ${context.limit}.`,
|
|
32
|
+
fieldLabel: 'Product photo',
|
|
33
|
+
fieldHelper: 'The image is resized in the browser before it is sent.',
|
|
34
|
+
fieldRemove: 'Remove image',
|
|
35
|
+
field: {
|
|
36
|
+
// `buttonLabel` is never read here — `ImageField` overrides it with
|
|
37
|
+
// `fieldLabel`. It is still answered, because the type is the whole of
|
|
38
|
+
// what `UploadButton` may render and a partial answer is not a pack.
|
|
39
|
+
buttonLabel: 'Product photo',
|
|
40
|
+
dropzoneHint: 'Drop the image here, or click to choose one',
|
|
41
|
+
dropzoneRole: (label) => `Image upload area. ${label}`,
|
|
42
|
+
uploading: 'Uploading…',
|
|
43
|
+
dropReady: 'Image ready to drop',
|
|
44
|
+
percentUploaded: (percent) => `${percent}% uploaded`,
|
|
45
|
+
uploadInProgress: (percent) => `Upload in progress: ${percent}%`,
|
|
46
|
+
errorAnnouncement: (message) => `Error: ${message}`,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
package/src/react/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EN_US_WEB_STORAGE_MESSAGES } from './en-US';
|
|
2
|
+
import type { WebStorageMessageContext, WebStorageMessages } from './failures';
|
|
3
|
+
import { PT_BR_WEB_STORAGE_MESSAGES } from './pt-BR';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Both languages of the browser half, keyed by tag. Factories, for the reason
|
|
7
|
+
* the server pack's docstring gives: the ceiling is interpolated so copy cannot
|
|
8
|
+
* name a limit the host has since changed.
|
|
9
|
+
*
|
|
10
|
+
* `LocalePack` is mirrored here rather than imported so the package stays
|
|
11
|
+
* liftable into a repo that has never heard of `@12-apps/i18n`.
|
|
12
|
+
*/
|
|
13
|
+
type LocalePack<T> = { readonly 'pt-BR': T; readonly 'en-US': T };
|
|
14
|
+
|
|
15
|
+
export const WEB_STORAGE_MESSAGES = {
|
|
16
|
+
'pt-BR': PT_BR_WEB_STORAGE_MESSAGES,
|
|
17
|
+
'en-US': EN_US_WEB_STORAGE_MESSAGES,
|
|
18
|
+
} as const satisfies LocalePack<(context: WebStorageMessageContext) => WebStorageMessages>;
|