@shipload/item-renderer 0.2.2 → 1.0.0-beta1
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 +47 -49
- package/src/assets/stardust-base64.ts +1 -1
- package/src/errors.ts +14 -14
- package/src/fonts/index.ts +23 -24
- package/src/fonts/load-bun.ts +11 -11
- package/src/index.ts +28 -28
- package/src/links.ts +11 -11
- package/src/meta.ts +25 -25
- package/src/payload/base64url.ts +21 -21
- package/src/payload/codec.ts +17 -17
- package/src/primitives/category-icon.ts +90 -67
- package/src/primitives/compact-row.ts +32 -32
- package/src/primitives/divider.ts +14 -14
- package/src/primitives/icon-hex.ts +36 -34
- package/src/primitives/module-slot.ts +131 -131
- package/src/primitives/panel.ts +18 -18
- package/src/primitives/quantity-badge.ts +32 -32
- package/src/primitives/span-paragraph.ts +55 -57
- package/src/primitives/stat-bar.ts +71 -71
- package/src/primitives/svg.ts +15 -15
- package/src/primitives/text.ts +33 -33
- package/src/primitives/wrap.ts +19 -19
- package/src/render.ts +21 -25
- package/src/templates/_shared.ts +5 -6
- package/src/templates/component.ts +123 -121
- package/src/templates/index.ts +23 -23
- package/src/templates/item-cell.ts +84 -81
- package/src/templates/module.ts +177 -174
- package/src/templates/packed-entity.ts +22 -24
- package/src/templates/resource.ts +134 -134
- package/src/templates/ship-panel.ts +120 -121
- package/src/templates/social-card.ts +28 -26
- package/src/tokens/colors.ts +38 -38
- package/src/tokens/index.ts +5 -5
- package/src/tokens/spacing.ts +8 -8
- package/src/tokens/typography.ts +17 -17
- package/.github/workflows/ci.yml +0 -14
- package/.gitignore +0 -6
- package/Makefile +0 -50
- package/biome.json +0 -18
- package/bun.lock +0 -123
- package/scripts/check-bundle-size.ts +0 -37
- package/scripts/copy-fonts.ts +0 -41
- package/scripts/preview.ts +0 -43
- package/test/__image_snapshots__/.gitkeep +0 -0
- package/test/__image_snapshots__/component-hull-plates.diff.png +0 -0
- package/test/__image_snapshots__/component-hull-plates.png +0 -0
- package/test/__image_snapshots__/module-engine-t1.diff.png +0 -0
- package/test/__image_snapshots__/module-engine-t1.png +0 -0
- package/test/__image_snapshots__/module-storage-t1.diff.png +0 -0
- package/test/__image_snapshots__/module-storage-t1.png +0 -0
- package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.diff.png +0 -0
- package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.png +0 -0
- package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.diff.png +0 -0
- package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.png +0 -0
- package/test/__image_snapshots__/resource-ore-t1.diff.png +0 -0
- package/test/__image_snapshots__/resource-ore-t1.png +0 -0
- package/test/__snapshots__/templates-component.test.ts.snap +0 -5
- package/test/__snapshots__/templates-item-cell.test.ts.snap +0 -9
- package/test/__snapshots__/templates-module.test.ts.snap +0 -17
- package/test/__snapshots__/templates-packed-entity.test.ts.snap +0 -5
- package/test/__snapshots__/templates-resource.test.ts.snap +0 -7
- package/test/base64url.test.ts +0 -33
- package/test/codec.test.ts +0 -43
- package/test/errors.test.ts +0 -24
- package/test/fixtures/cargo-items.ts +0 -122
- package/test/fonts.test.ts +0 -28
- package/test/links-meta.test.ts +0 -43
- package/test/pixel.test.ts +0 -66
- package/test/primitives-category-icon.test.ts +0 -79
- package/test/primitives-compact-row.test.ts +0 -44
- package/test/primitives-domain.test.ts +0 -72
- package/test/primitives-layout.test.ts +0 -56
- package/test/primitives-module-slot.test.ts +0 -88
- package/test/render.test.ts +0 -40
- package/test/sanity.test.ts +0 -6
- package/test/sdk-link.test.ts +0 -19
- package/test/snapshots/.gitkeep +0 -0
- package/test/svg.test.ts +0 -28
- package/test/templates-component.test.ts +0 -36
- package/test/templates-dispatch.test.ts +0 -35
- package/test/templates-item-cell.test.ts +0 -94
- package/test/templates-module.test.ts +0 -63
- package/test/templates-packed-entity.test.ts +0 -47
- package/test/templates-resource.test.ts +0 -71
- package/test/templates-ship-panel.test.ts +0 -87
- package/test/tokens.test.ts +0 -32
- package/tsconfig.json +0 -20
package/package.json
CHANGED
|
@@ -1,55 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"name": "@shipload/item-renderer",
|
|
3
|
+
"version": "1.0.0-beta1",
|
|
4
|
+
"description": "Deterministic SVG rendering for Shipload items",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"default": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"./fonts": {
|
|
14
|
+
"types": "./src/fonts/index.ts",
|
|
15
|
+
"default": "./src/fonts/index.ts"
|
|
16
|
+
},
|
|
17
|
+
"./fonts/load-bun": {
|
|
18
|
+
"types": "./src/fonts/load-bun.ts",
|
|
19
|
+
"default": "./src/fonts/load-bun.ts"
|
|
20
|
+
},
|
|
21
|
+
"./fonts/*.woff2": "./src/fonts/*.woff2",
|
|
22
|
+
"./test/fixtures": {
|
|
23
|
+
"types": "./test/fixtures/cargo-items.ts",
|
|
24
|
+
"default": "./test/fixtures/cargo-items.ts"
|
|
25
|
+
}
|
|
12
26
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"!src/**/*.test.ts"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "bun test",
|
|
33
|
+
"test:pixel": "bun test test/pixel.test.ts",
|
|
34
|
+
"dev:preview": "bun run scripts/preview.ts",
|
|
35
|
+
"check": "tsc --noEmit",
|
|
36
|
+
"build": "echo '@shipload/item-renderer publishes raw TS source — no build step'",
|
|
37
|
+
"fonts:copy": "bun run scripts/copy-fonts.ts"
|
|
16
38
|
},
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@shipload/sdk": "workspace:^",
|
|
41
|
+
"@wharfkit/antelope": "1.2.0"
|
|
20
42
|
},
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@fontsource/inter": "^5.2.8",
|
|
45
|
+
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
46
|
+
"@fontsource/orbitron": "^5.2.8",
|
|
47
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
48
|
+
"@resvg/resvg-wasm": "^2.6.2",
|
|
49
|
+
"@types/bun": "latest",
|
|
50
|
+
"pixelmatch": "^6.0.0",
|
|
51
|
+
"pngjs": "^7.0.0"
|
|
25
52
|
}
|
|
26
|
-
},
|
|
27
|
-
"files": [
|
|
28
|
-
"src",
|
|
29
|
-
"!src/**/*.test.ts"
|
|
30
|
-
],
|
|
31
|
-
"scripts": {
|
|
32
|
-
"test": "bun test",
|
|
33
|
-
"test:pixel": "bun test test/pixel.test.ts",
|
|
34
|
-
"dev:preview": "bun run scripts/preview.ts",
|
|
35
|
-
"typecheck": "tsc --noEmit",
|
|
36
|
-
"lint": "biome check src test scripts",
|
|
37
|
-
"fonts:copy": "bun run scripts/copy-fonts.ts"
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"@shipload/sdk": "2.0.0-rc21",
|
|
41
|
-
"@wharfkit/antelope": "^1.0.0"
|
|
42
|
-
},
|
|
43
|
-
"devDependencies": {
|
|
44
|
-
"@biomejs/biome": "^1.8.0",
|
|
45
|
-
"@fontsource/inter": "^5.2.8",
|
|
46
|
-
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
47
|
-
"@fontsource/orbitron": "^5.2.8",
|
|
48
|
-
"@resvg/resvg-js": "^2.6.2",
|
|
49
|
-
"@types/bun": "latest",
|
|
50
|
-
"pixelmatch": "^6.0.0",
|
|
51
|
-
"pngjs": "^7.0.0",
|
|
52
|
-
"typescript": "^5.4.0"
|
|
53
|
-
},
|
|
54
|
-
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
55
53
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const STARDUST_BASE64 =
|
|
2
|
-
'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'
|
|
2
|
+
'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'
|
package/src/errors.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
export class InvalidPayloadError extends Error {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
override readonly name = 'InvalidPayloadError'
|
|
3
|
+
constructor(message: string) {
|
|
4
|
+
super(message)
|
|
5
|
+
}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export class UnknownItemError extends Error {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
override readonly name = 'UnknownItemError'
|
|
10
|
+
readonly itemId: number
|
|
11
|
+
constructor(itemId: number) {
|
|
12
|
+
super(`unknown item id: ${itemId}`)
|
|
13
|
+
this.itemId = itemId
|
|
14
|
+
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export class RenderError extends Error {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
override readonly name = 'RenderError'
|
|
19
|
+
constructor(message: string, options?: {cause?: unknown}) {
|
|
20
|
+
super(message, options)
|
|
21
|
+
}
|
|
22
22
|
}
|
package/src/fonts/index.ts
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
export type FontKey = 'orbitron-700' | 'inter-400' | 'inter-600' | 'jetbrains-500'
|
|
2
2
|
|
|
3
3
|
export interface FontMeta {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
family: string
|
|
5
|
+
weight: number
|
|
6
|
+
fileName: string
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export const FONT_MANIFEST: Record<FontKey, FontMeta> = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
'orbitron-700': {family: 'Orbitron', weight: 700, fileName: 'orbitron-700.woff2'},
|
|
11
|
+
'inter-400': {family: 'Inter', weight: 400, fileName: 'inter-400.woff2'},
|
|
12
|
+
'inter-600': {family: 'Inter', weight: 600, fileName: 'inter-600.woff2'},
|
|
13
|
+
'jetbrains-500': {family: 'JetBrains Mono', weight: 500, fileName: 'jetbrains-500.woff2'},
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function bytesToBase64(bytes: Uint8Array): string {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
let binary = ''
|
|
18
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!)
|
|
19
|
+
return btoa(binary)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function embedFontsInSvg(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return svg.replace('>', `>${style}`)
|
|
22
|
+
export function embedFontsInSvg(svg: string, fontData: Record<FontKey, Uint8Array>): string {
|
|
23
|
+
const faceBlocks = (Object.keys(fontData) as FontKey[])
|
|
24
|
+
.map((key) => {
|
|
25
|
+
const meta = FONT_MANIFEST[key]
|
|
26
|
+
const b64 = bytesToBase64(fontData[key])
|
|
27
|
+
return (
|
|
28
|
+
`@font-face { font-family: "${meta.family}"; font-weight: ${meta.weight}; ` +
|
|
29
|
+
`font-style: normal; src: url(data:font/woff2;base64,${b64}) format("woff2"); }`
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
.join('\n')
|
|
33
|
+
const style = `<defs><style type="text/css"><![CDATA[\n${faceBlocks}\n]]></style></defs>`
|
|
34
|
+
return svg.replace('>', `>${style}`)
|
|
36
35
|
}
|
package/src/fonts/load-bun.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import {readFile} from 'node:fs/promises'
|
|
2
|
+
import {dirname, join} from 'node:path'
|
|
3
|
+
import {fileURLToPath} from 'node:url'
|
|
4
|
+
import {FONT_MANIFEST, type FontKey} from './index.ts'
|
|
5
5
|
|
|
6
6
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
7
7
|
|
|
8
8
|
export async function loadFontData(): Promise<Record<FontKey, Uint8Array>> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
const entries = await Promise.all(
|
|
10
|
+
(Object.keys(FONT_MANIFEST) as FontKey[]).map(async (key) => {
|
|
11
|
+
const buf = await readFile(join(HERE, FONT_MANIFEST[key].fileName))
|
|
12
|
+
return [key, new Uint8Array(buf)] as const
|
|
13
|
+
})
|
|
14
|
+
)
|
|
15
|
+
return Object.fromEntries(entries) as Record<FontKey, Uint8Array>
|
|
16
16
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,53 +2,53 @@
|
|
|
2
2
|
export const VERSION = '0.1.0'
|
|
3
3
|
|
|
4
4
|
// Errors
|
|
5
|
-
export {
|
|
5
|
+
export {InvalidPayloadError, UnknownItemError, RenderError} from './errors.ts'
|
|
6
6
|
|
|
7
7
|
// Payload
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
8
|
+
export {encodePayload, decodePayload} from './payload/codec.ts'
|
|
9
|
+
export type {CargoItem, CargoItemLike} from './payload/codec.ts'
|
|
10
10
|
|
|
11
11
|
// Rendering
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
12
|
+
export {renderItem, renderFromPayload, type RenderOptions} from './render.ts'
|
|
13
|
+
export {renderByType, type RenderByTypeOpts} from './templates/index.ts'
|
|
14
14
|
|
|
15
15
|
// Links + meta
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export type {
|
|
16
|
+
export {linkToItemPage, linkToItemImage, linkToItemSocial} from './links.ts'
|
|
17
|
+
export {itemPageMeta, svgDimensions} from './meta.ts'
|
|
18
|
+
export type {ItemPageMeta, ItemPageMetaOptions} from './meta.ts'
|
|
19
19
|
|
|
20
20
|
// Tokens (consumed by testmap tailwind.config)
|
|
21
|
-
export {
|
|
22
|
-
export type {
|
|
23
|
-
export type {
|
|
21
|
+
export {tokens} from './tokens/index.ts'
|
|
22
|
+
export type {Tokens} from './tokens/index.ts'
|
|
23
|
+
export type {CategoryColorKey, TierColorKey} from './tokens/colors.ts'
|
|
24
24
|
|
|
25
25
|
// Category icon primitive
|
|
26
|
-
export {
|
|
27
|
-
export type {
|
|
26
|
+
export {categoryIconSvg, categoryIconPath} from './primitives/category-icon.ts'
|
|
27
|
+
export type {CategoryIconPathOpts, CategoryIconSvgOpts} from './primitives/category-icon.ts'
|
|
28
28
|
|
|
29
29
|
// Item cell templates
|
|
30
|
-
export {
|
|
31
|
-
export type {
|
|
30
|
+
export {renderItemCell, itemCellGroup} from './templates/item-cell.ts'
|
|
31
|
+
export type {ItemCellProps, ItemCellGroupProps} from './templates/item-cell.ts'
|
|
32
32
|
|
|
33
33
|
// Social card template (1200x630 OG image)
|
|
34
34
|
export {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
socialCardSvg,
|
|
36
|
+
SOCIAL_CARD_WIDTH,
|
|
37
|
+
SOCIAL_CARD_HEIGHT,
|
|
38
38
|
} from './templates/social-card.ts'
|
|
39
39
|
|
|
40
40
|
// Ship panel template
|
|
41
|
-
export {
|
|
42
|
-
export type {
|
|
41
|
+
export {renderShipPanel} from './templates/ship-panel.ts'
|
|
42
|
+
export type {ShipPanelProps, ShipPanelSlot} from './templates/ship-panel.ts'
|
|
43
43
|
|
|
44
44
|
// Re-exports from sdkv2 so consumers only need one import boundary
|
|
45
45
|
export {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
resolveItem,
|
|
47
|
+
ServerContract,
|
|
48
|
+
type ResolvedItem,
|
|
49
|
+
type ResolvedItemStat,
|
|
50
|
+
type ResolvedItemType,
|
|
51
|
+
type ResolvedModuleSlot,
|
|
52
|
+
type ResolvedAttributeGroup,
|
|
53
53
|
} from '@shipload/sdk'
|
|
54
|
-
export type {
|
|
54
|
+
export type {CategoryIconShape} from '@shipload/sdk'
|
package/src/links.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type {CargoItem} from './payload/codec.ts'
|
|
2
|
+
import {encodePayload} from './payload/codec.ts'
|
|
3
3
|
|
|
4
4
|
const DEFAULT_WEBSITE_BASE = 'https://shiploadgame.com'
|
|
5
5
|
const DEFAULT_IMAGE_BASE = 'https://item.shiploadgame.com'
|
|
6
6
|
|
|
7
7
|
export function linkToItemPage(item: CargoItem, baseUrl = DEFAULT_WEBSITE_BASE): string {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const payload = encodePayload(item)
|
|
9
|
+
return `${baseUrl}/guide/item/${payload}`
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function linkToItemImage(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
item: CargoItem,
|
|
14
|
+
ext: 'png' | 'svg',
|
|
15
|
+
baseUrl = DEFAULT_IMAGE_BASE
|
|
16
16
|
): string {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const payload = encodePayload(item)
|
|
18
|
+
return `${baseUrl}/item/${payload}.${ext}`
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function linkToItemSocial(item: CargoItem, baseUrl = DEFAULT_IMAGE_BASE): string {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const payload = encodePayload(item)
|
|
23
|
+
return `${baseUrl}/social/${payload}.png`
|
|
24
24
|
}
|
package/src/meta.ts
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import type {ResolvedItem} from '@shipload/sdk'
|
|
2
|
+
import {describeItem, displayName} from '@shipload/sdk'
|
|
3
|
+
import type {CargoItem} from './payload/codec.ts'
|
|
4
|
+
import {linkToItemSocial} from './links.ts'
|
|
5
|
+
import {SOCIAL_CARD_WIDTH, SOCIAL_CARD_HEIGHT} from './templates/social-card.ts'
|
|
6
6
|
|
|
7
7
|
const DIMS_RE = /<svg[^>]*?\bwidth="(\d+)"[^>]*?\bheight="(\d+)"/
|
|
8
8
|
|
|
9
|
-
export function svgDimensions(svg: string): {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
export function svgDimensions(svg: string): {width: number; height: number} {
|
|
10
|
+
const m = DIMS_RE.exec(svg)
|
|
11
|
+
if (!m) throw new Error('svgDimensions: could not locate width/height on root <svg>')
|
|
12
|
+
return {width: Number(m[1]), height: Number(m[2])}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface ItemPageMeta {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
title: string
|
|
17
|
+
description: string
|
|
18
|
+
ogImage: string
|
|
19
|
+
ogImageWidth: number
|
|
20
|
+
ogImageHeight: number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface ItemPageMetaOptions {
|
|
24
|
-
|
|
24
|
+
imageBaseUrl?: string
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function itemPageMeta(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
item: CargoItem,
|
|
29
|
+
resolved: ResolvedItem,
|
|
30
|
+
opts?: ItemPageMetaOptions
|
|
31
31
|
): ItemPageMeta {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
return {
|
|
33
|
+
title: `${displayName(resolved)} · Shipload Guide`,
|
|
34
|
+
description: describeItem(resolved),
|
|
35
|
+
ogImage: linkToItemSocial(item, opts?.imageBaseUrl),
|
|
36
|
+
ogImageWidth: SOCIAL_CARD_WIDTH,
|
|
37
|
+
ogImageHeight: SOCIAL_CARD_HEIGHT,
|
|
38
|
+
}
|
|
39
39
|
}
|
package/src/payload/base64url.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {InvalidPayloadError} from '../errors.ts'
|
|
2
2
|
|
|
3
3
|
export function bytesToBase64Url(bytes: Uint8Array): string {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
let binary = ''
|
|
5
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!)
|
|
6
|
+
const b64 = btoa(binary)
|
|
7
|
+
return b64.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '')
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function base64UrlToBytes(input: string): Uint8Array {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
if (!/^[A-Za-z0-9_-]*$/.test(input)) {
|
|
12
|
+
throw new InvalidPayloadError('payload contains non-base64url characters')
|
|
13
|
+
}
|
|
14
|
+
const padded = input
|
|
15
|
+
.replaceAll('-', '+')
|
|
16
|
+
.replaceAll('_', '/')
|
|
17
|
+
.padEnd(Math.ceil(input.length / 4) * 4, '=')
|
|
18
|
+
let binary: string
|
|
19
|
+
try {
|
|
20
|
+
binary = atob(padded)
|
|
21
|
+
} catch (e) {
|
|
22
|
+
throw new InvalidPayloadError(`base64 decode failed: ${(e as Error).message}`)
|
|
23
|
+
}
|
|
24
|
+
const bytes = new Uint8Array(binary.length)
|
|
25
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
|
|
26
|
+
return bytes
|
|
27
27
|
}
|
package/src/payload/codec.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import {Serializer} from '@wharfkit/antelope'
|
|
2
|
+
import {ServerContract} from '@shipload/sdk'
|
|
3
|
+
import {InvalidPayloadError} from '../errors.ts'
|
|
4
|
+
import {base64UrlToBytes, bytesToBase64Url} from './base64url.ts'
|
|
5
5
|
|
|
6
6
|
export type CargoItem = InstanceType<typeof ServerContract.Types.cargo_item>
|
|
7
7
|
export type CargoItemLike = Parameters<typeof ServerContract.Types.cargo_item.from>[0]
|
|
8
8
|
|
|
9
9
|
export function encodePayload(input: CargoItemLike): string {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const item = ServerContract.Types.cargo_item.from(input)
|
|
11
|
+
const bytes = Serializer.encode({object: item}).array
|
|
12
|
+
return bytesToBase64Url(bytes)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function decodePayload(input: string): CargoItem {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
if (input.length === 0) throw new InvalidPayloadError('empty payload')
|
|
17
|
+
const bytes = base64UrlToBytes(input)
|
|
18
|
+
try {
|
|
19
|
+
return Serializer.decode({
|
|
20
|
+
data: bytes,
|
|
21
|
+
type: ServerContract.Types.cargo_item,
|
|
22
|
+
}) as CargoItem
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw new InvalidPayloadError(`cargo_item decode failed: ${(e as Error).message}`)
|
|
25
|
+
}
|
|
26
26
|
}
|