@shipload/item-renderer 0.2.3 → 1.0.0-next.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 (89) hide show
  1. package/package.json +47 -49
  2. package/src/assets/stardust-base64.ts +1 -1
  3. package/src/errors.ts +14 -14
  4. package/src/fonts/index.ts +24 -24
  5. package/src/fonts/load-bun.ts +12 -12
  6. package/src/index.ts +31 -31
  7. package/src/links.ts +13 -13
  8. package/src/meta.ts +26 -26
  9. package/src/payload/base64url.ts +21 -21
  10. package/src/payload/codec.ts +19 -19
  11. package/src/primitives/category-icon.ts +88 -86
  12. package/src/primitives/compact-row.ts +32 -32
  13. package/src/primitives/divider.ts +14 -14
  14. package/src/primitives/icon-hex.ts +36 -36
  15. package/src/primitives/module-slot.ts +131 -131
  16. package/src/primitives/panel.ts +18 -18
  17. package/src/primitives/quantity-badge.ts +32 -32
  18. package/src/primitives/span-paragraph.ts +56 -56
  19. package/src/primitives/stat-bar.ts +72 -72
  20. package/src/primitives/svg.ts +16 -16
  21. package/src/primitives/text.ts +33 -33
  22. package/src/primitives/wrap.ts +19 -19
  23. package/src/render.ts +20 -20
  24. package/src/templates/_shared.ts +5 -5
  25. package/src/templates/component.ts +124 -124
  26. package/src/templates/index.ts +23 -23
  27. package/src/templates/item-cell.ts +84 -84
  28. package/src/templates/module.ts +182 -181
  29. package/src/templates/packed-entity.ts +22 -22
  30. package/src/templates/resource.ts +136 -134
  31. package/src/templates/ship-panel.ts +122 -118
  32. package/src/templates/social-card.ts +36 -36
  33. package/src/tokens/colors.ts +42 -42
  34. package/src/tokens/index.ts +6 -6
  35. package/src/tokens/spacing.ts +9 -9
  36. package/src/tokens/typography.ts +18 -18
  37. package/.claude/settings.local.json +0 -6
  38. package/.github/workflows/ci.yml +0 -14
  39. package/.gitignore +0 -6
  40. package/Makefile +0 -50
  41. package/biome.json +0 -18
  42. package/bun.lock +0 -123
  43. package/scripts/check-bundle-size.ts +0 -37
  44. package/scripts/copy-fonts.ts +0 -41
  45. package/scripts/preview.ts +0 -41
  46. package/test/__image_snapshots__/.gitkeep +0 -0
  47. package/test/__image_snapshots__/component-hull-plates.diff.png +0 -0
  48. package/test/__image_snapshots__/component-hull-plates.png +0 -0
  49. package/test/__image_snapshots__/module-engine-t1.diff.png +0 -0
  50. package/test/__image_snapshots__/module-engine-t1.png +0 -0
  51. package/test/__image_snapshots__/module-storage-t1.diff.png +0 -0
  52. package/test/__image_snapshots__/module-storage-t1.png +0 -0
  53. package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.diff.png +0 -0
  54. package/test/__image_snapshots__/packed-entity-ship-t1-only-engine.png +0 -0
  55. package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.diff.png +0 -0
  56. package/test/__image_snapshots__/packed-entity-ship-t1-two-modules.png +0 -0
  57. package/test/__image_snapshots__/resource-ore-t1.diff.png +0 -0
  58. package/test/__image_snapshots__/resource-ore-t1.png +0 -0
  59. package/test/__snapshots__/templates-component.test.ts.snap +0 -5
  60. package/test/__snapshots__/templates-item-cell.test.ts.snap +0 -9
  61. package/test/__snapshots__/templates-module.test.ts.snap +0 -17
  62. package/test/__snapshots__/templates-packed-entity.test.ts.snap +0 -5
  63. package/test/__snapshots__/templates-resource.test.ts.snap +0 -7
  64. package/test/base64url.test.ts +0 -33
  65. package/test/codec.test.ts +0 -34
  66. package/test/errors.test.ts +0 -24
  67. package/test/fixtures/cargo-items.ts +0 -122
  68. package/test/fonts.test.ts +0 -28
  69. package/test/links-meta.test.ts +0 -43
  70. package/test/pixel.test.ts +0 -69
  71. package/test/primitives-category-icon.test.ts +0 -86
  72. package/test/primitives-compact-row.test.ts +0 -44
  73. package/test/primitives-domain.test.ts +0 -83
  74. package/test/primitives-layout.test.ts +0 -56
  75. package/test/primitives-module-slot.test.ts +0 -88
  76. package/test/render.test.ts +0 -43
  77. package/test/sanity.test.ts +0 -6
  78. package/test/sdk-link.test.ts +0 -19
  79. package/test/snapshots/.gitkeep +0 -0
  80. package/test/svg.test.ts +0 -30
  81. package/test/templates-component.test.ts +0 -36
  82. package/test/templates-dispatch.test.ts +0 -35
  83. package/test/templates-item-cell.test.ts +0 -94
  84. package/test/templates-module.test.ts +0 -63
  85. package/test/templates-packed-entity.test.ts +0 -47
  86. package/test/templates-resource.test.ts +0 -71
  87. package/test/templates-ship-panel.test.ts +0 -91
  88. package/test/tokens.test.ts +0 -34
  89. package/tsconfig.json +0 -20
package/package.json CHANGED
@@ -1,55 +1,53 @@
1
1
  {
2
- "name": "@shipload/item-renderer",
3
- "version": "0.2.3",
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"
2
+ "name": "@shipload/item-renderer",
3
+ "version": "1.0.0-next.0",
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
- "./fonts": {
14
- "types": "./src/fonts/index.ts",
15
- "default": "./src/fonts/index.ts"
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
- "./fonts/load-bun": {
18
- "types": "./src/fonts/load-bun.ts",
19
- "default": "./src/fonts/load-bun.ts"
39
+ "dependencies": {
40
+ "@shipload/sdk": "workspace:^",
41
+ "@wharfkit/antelope": "1.2.0"
20
42
  },
21
- "./fonts/*.woff2": "./src/fonts/*.woff2",
22
- "./test/fixtures": {
23
- "types": "./test/fixtures/cargo-items.ts",
24
- "default": "./test/fixtures/cargo-items.ts"
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-rc25",
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
- override readonly name = "InvalidPayloadError";
3
- constructor(message: string) {
4
- super(message);
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
- override readonly name = "UnknownItemError";
10
- readonly itemId: number;
11
- constructor(itemId: number) {
12
- super(`unknown item id: ${itemId}`);
13
- this.itemId = itemId;
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
- override readonly name = "RenderError";
19
- constructor(message: string, options?: { cause?: unknown }) {
20
- super(message, options);
21
- }
18
+ override readonly name = 'RenderError'
19
+ constructor(message: string, options?: {cause?: unknown}) {
20
+ super(message, options)
21
+ }
22
22
  }
@@ -1,35 +1,35 @@
1
- export type FontKey = "orbitron-700" | "inter-400" | "inter-600" | "jetbrains-500";
1
+ export type FontKey = 'orbitron-700' | 'inter-400' | 'inter-600' | 'jetbrains-500'
2
2
 
3
3
  export interface FontMeta {
4
- family: string;
5
- weight: number;
6
- fileName: string;
4
+ family: string
5
+ weight: number
6
+ fileName: string
7
7
  }
8
8
 
9
9
  export const FONT_MANIFEST: Record<FontKey, FontMeta> = {
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
- };
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
+ }
15
15
 
16
16
  function bytesToBase64(bytes: Uint8Array): string {
17
- let binary = "";
18
- for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!);
19
- return btoa(binary);
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
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}`);
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}`)
35
35
  }
@@ -1,16 +1,16 @@
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";
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
- const HERE = dirname(fileURLToPath(import.meta.url));
6
+ const HERE = dirname(fileURLToPath(import.meta.url))
7
7
 
8
8
  export async function loadFontData(): Promise<Record<FontKey, Uint8Array>> {
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>;
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
@@ -1,54 +1,54 @@
1
1
  // Version
2
- export const VERSION = "0.1.0";
2
+ export const VERSION = '0.1.0'
3
3
 
4
4
  // Errors
5
- export { InvalidPayloadError, UnknownItemError, RenderError } from "./errors.ts";
5
+ export {InvalidPayloadError, UnknownItemError, RenderError} from './errors.ts'
6
6
 
7
7
  // Payload
8
- export { encodePayload, decodePayload } from "./payload/codec.ts";
9
- export type { CargoItem, CargoItemLike } from "./payload/codec.ts";
8
+ export {encodePayload, decodePayload} from './payload/codec.ts'
9
+ export type {CargoItem, CargoItemLike} from './payload/codec.ts'
10
10
 
11
11
  // Rendering
12
- export { renderItem, renderFromPayload, type RenderOptions } from "./render.ts";
13
- export { renderByType, type RenderByTypeOpts } from "./templates/index.ts";
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 { linkToItemPage, linkToItemImage, linkToItemSocial } from "./links.ts";
17
- export { itemPageMeta, svgDimensions } from "./meta.ts";
18
- export type { ItemPageMeta, ItemPageMetaOptions } from "./meta.ts";
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 { tokens } from "./tokens/index.ts";
22
- export type { Tokens } from "./tokens/index.ts";
23
- export type { CategoryColorKey, TierColorKey } from "./tokens/colors.ts";
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 { categoryIconSvg, categoryIconPath } from "./primitives/category-icon.ts";
27
- export type { CategoryIconPathOpts, CategoryIconSvgOpts } from "./primitives/category-icon.ts";
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 { renderItemCell, itemCellGroup } from "./templates/item-cell.ts";
31
- export type { ItemCellProps, ItemCellGroupProps } from "./templates/item-cell.ts";
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
- socialCardSvg,
36
- SOCIAL_CARD_WIDTH,
37
- SOCIAL_CARD_HEIGHT,
38
- } from "./templates/social-card.ts";
35
+ socialCardSvg,
36
+ SOCIAL_CARD_WIDTH,
37
+ SOCIAL_CARD_HEIGHT,
38
+ } from './templates/social-card.ts'
39
39
 
40
40
  // Ship panel template
41
- export { renderShipPanel } from "./templates/ship-panel.ts";
42
- export type { ShipPanelProps, ShipPanelSlot } from "./templates/ship-panel.ts";
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
- resolveItem,
47
- ServerContract,
48
- type ResolvedItem,
49
- type ResolvedItemStat,
50
- type ResolvedItemType,
51
- type ResolvedModuleSlot,
52
- type ResolvedAttributeGroup,
53
- } from "@shipload/sdk";
54
- export type { CategoryIconShape } from "@shipload/sdk";
46
+ resolveItem,
47
+ ServerContract,
48
+ type ResolvedItem,
49
+ type ResolvedItemStat,
50
+ type ResolvedItemType,
51
+ type ResolvedModuleSlot,
52
+ type ResolvedAttributeGroup,
53
+ } from '@shipload/sdk'
54
+ export type {CategoryIconShape} from '@shipload/sdk'
package/src/links.ts CHANGED
@@ -1,24 +1,24 @@
1
- import type { CargoItem } from "./payload/codec.ts";
2
- import { encodePayload } from "./payload/codec.ts";
1
+ import type {CargoItem} from './payload/codec.ts'
2
+ import {encodePayload} from './payload/codec.ts'
3
3
 
4
- const DEFAULT_WEBSITE_BASE = "https://shiploadgame.com";
5
- const DEFAULT_IMAGE_BASE = "https://item.shiploadgame.com";
4
+ const DEFAULT_WEBSITE_BASE = 'https://shiploadgame.com'
5
+ const DEFAULT_IMAGE_BASE = 'https://item.shiploadgame.com'
6
6
 
7
7
  export function linkToItemPage(item: CargoItem, baseUrl = DEFAULT_WEBSITE_BASE): string {
8
- const payload = encodePayload(item);
9
- return `${baseUrl}/guide/item/${payload}`;
8
+ const payload = encodePayload(item)
9
+ return `${baseUrl}/guide/item/${payload}`
10
10
  }
11
11
 
12
12
  export function linkToItemImage(
13
- item: CargoItem,
14
- ext: "png" | "svg",
15
- baseUrl = DEFAULT_IMAGE_BASE,
13
+ item: CargoItem,
14
+ ext: 'png' | 'svg',
15
+ baseUrl = DEFAULT_IMAGE_BASE
16
16
  ): string {
17
- const payload = encodePayload(item);
18
- return `${baseUrl}/item/${payload}.${ext}`;
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
- const payload = encodePayload(item);
23
- return `${baseUrl}/social/${payload}.png`;
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 { 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";
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
- const DIMS_RE = /<svg[^>]*?\bwidth="(\d+)"[^>]*?\bheight="(\d+)"/;
7
+ const DIMS_RE = /<svg[^>]*?\bwidth="(\d+)"[^>]*?\bheight="(\d+)"/
8
8
 
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]) };
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
- title: string;
17
- description: string;
18
- ogImage: string;
19
- ogImageWidth: number;
20
- ogImageHeight: number;
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
- imageBaseUrl?: string;
24
+ imageBaseUrl?: string
25
25
  }
26
26
 
27
27
  export function itemPageMeta(
28
- item: CargoItem,
29
- resolved: ResolvedItem,
30
- opts?: ItemPageMetaOptions,
28
+ item: CargoItem,
29
+ resolved: ResolvedItem,
30
+ opts?: ItemPageMetaOptions
31
31
  ): ItemPageMeta {
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
- };
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
  }
@@ -1,27 +1,27 @@
1
- import { InvalidPayloadError } from "../errors.ts";
1
+ import {InvalidPayloadError} from '../errors.ts'
2
2
 
3
3
  export function bytesToBase64Url(bytes: Uint8Array): string {
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("=", "");
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
- 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;
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
  }
@@ -1,26 +1,26 @@
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";
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
- export type CargoItem = InstanceType<typeof ServerContract.Types.cargo_item>;
7
- export type CargoItemLike = Parameters<typeof ServerContract.Types.cargo_item.from>[0];
6
+ export type CargoItem = InstanceType<typeof ServerContract.Types.cargo_item>
7
+ export type CargoItemLike = Parameters<typeof ServerContract.Types.cargo_item.from>[0]
8
8
 
9
9
  export function encodePayload(input: CargoItemLike): string {
10
- const item = ServerContract.Types.cargo_item.from(input);
11
- const bytes = Serializer.encode({ object: item }).array;
12
- return bytesToBase64Url(bytes);
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
- 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
- }
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
  }