@dtechph/wayfinding-core 0.1.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/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # `@dtechph/wayfinding-core`
2
+
3
+ Core API client for the Duon Wayfinding SDK. Fetches CMS-configured malls via `GET /v1/sdk/malls` using `x-api-key`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @dtechph/wayfinding-core
9
+ ```
10
+
11
+ Requires GitHub Packages auth — see the [root README](../../README.md).
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { DuonWayfinding } from "@dtechph/wayfinding-core";
17
+
18
+ DuonWayfinding.initialize({
19
+ apiBaseUrl: "https://your-backend.example.com",
20
+ apiKey: "duon_sk_live_...",
21
+ });
22
+
23
+ const malls = await DuonWayfinding.fetchMalls();
24
+ const url = DuonWayfinding.getViewerUrl(malls[0]);
25
+ ```
26
+
27
+ For React Native / Expo UI components, use [`@dtechph/wayfinding-react-native`](../react-native).
@@ -0,0 +1,6 @@
1
+ import type { DuonMall, DuonWayfindingConfig } from "./types";
2
+ export declare const initialize: (next: DuonWayfindingConfig) => void;
3
+ export declare const getConfig: () => DuonWayfindingConfig;
4
+ export type FetchFn = typeof fetch;
5
+ export declare const fetchMalls: (fetchImpl?: FetchFn) => Promise<DuonMall[]>;
6
+ export declare const resetForTests: () => void;
package/dist/client.js ADDED
@@ -0,0 +1,63 @@
1
+ import { DuonAuthError, DuonConfigError, DuonForbiddenError, DuonNetworkError, } from "./errors";
2
+ import { mapApiMall } from "./mall";
3
+ let config = null;
4
+ const normalizeBaseUrl = (url) => url.replace(/\/$/, "");
5
+ export const initialize = (next) => {
6
+ if (!next.apiBaseUrl?.trim()) {
7
+ throw new DuonConfigError("apiBaseUrl is required");
8
+ }
9
+ if (!next.apiKey?.trim()) {
10
+ throw new DuonConfigError("apiKey is required");
11
+ }
12
+ config = {
13
+ apiBaseUrl: normalizeBaseUrl(next.apiBaseUrl.trim()),
14
+ apiKey: next.apiKey.trim(),
15
+ };
16
+ };
17
+ export const getConfig = () => {
18
+ if (!config) {
19
+ throw new DuonConfigError("DuonWayfinding.initialize() must be called first");
20
+ }
21
+ return config;
22
+ };
23
+ export const fetchMalls = async (fetchImpl = fetch) => {
24
+ const { apiBaseUrl, apiKey } = getConfig();
25
+ const url = `${apiBaseUrl}/v1/sdk/malls`;
26
+ let response;
27
+ try {
28
+ response = await fetchImpl(url, {
29
+ method: "GET",
30
+ headers: {
31
+ Accept: "application/json",
32
+ "x-api-key": apiKey,
33
+ },
34
+ });
35
+ }
36
+ catch (err) {
37
+ throw new DuonNetworkError(err instanceof Error ? err.message : "Network request failed");
38
+ }
39
+ if (response.status === 401) {
40
+ throw new DuonAuthError();
41
+ }
42
+ if (response.status === 403) {
43
+ throw new DuonForbiddenError();
44
+ }
45
+ if (!response.ok) {
46
+ throw new DuonNetworkError(`Request failed with status ${response.status}`, response.status);
47
+ }
48
+ const body = (await response.json());
49
+ if (!body?.message || !Array.isArray(body.message)) {
50
+ throw new DuonNetworkError("Invalid malls response shape");
51
+ }
52
+ return body.message.map((item) => {
53
+ try {
54
+ return mapApiMall(item);
55
+ }
56
+ catch (err) {
57
+ throw new DuonConfigError(err instanceof Error ? err.message : "Invalid mall entry");
58
+ }
59
+ });
60
+ };
61
+ export const resetForTests = () => {
62
+ config = null;
63
+ };
@@ -0,0 +1,16 @@
1
+ export declare class DuonError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class DuonNetworkError extends DuonError {
5
+ readonly status?: number | undefined;
6
+ constructor(message: string, status?: number | undefined);
7
+ }
8
+ export declare class DuonAuthError extends DuonError {
9
+ constructor(message?: string);
10
+ }
11
+ export declare class DuonForbiddenError extends DuonError {
12
+ constructor(message?: string);
13
+ }
14
+ export declare class DuonConfigError extends DuonError {
15
+ constructor(message: string);
16
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,31 @@
1
+ export class DuonError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "DuonError";
5
+ }
6
+ }
7
+ export class DuonNetworkError extends DuonError {
8
+ constructor(message, status) {
9
+ super(message);
10
+ this.status = status;
11
+ this.name = "DuonNetworkError";
12
+ }
13
+ }
14
+ export class DuonAuthError extends DuonError {
15
+ constructor(message = "Invalid or missing API key") {
16
+ super(message);
17
+ this.name = "DuonAuthError";
18
+ }
19
+ }
20
+ export class DuonForbiddenError extends DuonError {
21
+ constructor(message = "Insufficient scope") {
22
+ super(message);
23
+ this.name = "DuonForbiddenError";
24
+ }
25
+ }
26
+ export class DuonConfigError extends DuonError {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = "DuonConfigError";
30
+ }
31
+ }
@@ -0,0 +1,10 @@
1
+ import { fetchMalls, getConfig, initialize, resetForTests } from "./client";
2
+ import { getViewerUrl } from "./mall";
3
+ export { initialize, fetchMalls, getConfig, resetForTests, getViewerUrl };
4
+ export type { DuonMall, DuonWayfindingConfig, MallMapType } from "./types";
5
+ export { DuonError, DuonAuthError, DuonConfigError, DuonForbiddenError, DuonNetworkError, } from "./errors";
6
+ export declare const DuonWayfinding: {
7
+ initialize: (next: import("./types").DuonWayfindingConfig) => void;
8
+ fetchMalls: (fetchImpl?: import("./client").FetchFn) => Promise<import("./types").DuonMall[]>;
9
+ getViewerUrl: (mall: import("./types").DuonMall) => string;
10
+ };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { fetchMalls, getConfig, initialize, resetForTests } from "./client";
2
+ import { getViewerUrl } from "./mall";
3
+ export { initialize, fetchMalls, getConfig, resetForTests, getViewerUrl };
4
+ export { DuonError, DuonAuthError, DuonConfigError, DuonForbiddenError, DuonNetworkError, } from "./errors";
5
+ export const DuonWayfinding = {
6
+ initialize,
7
+ fetchMalls,
8
+ getViewerUrl,
9
+ };
package/dist/mall.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { DuonMall, MallMapType, MallsApiResponse } from "./types";
2
+ export declare const parseMapType: (value: string | undefined) => MallMapType;
3
+ export declare const mapApiMall: (raw: MallsApiResponse["message"][number]) => DuonMall;
4
+ export declare const getViewerUrl: (mall: DuonMall) => string;
package/dist/mall.js ADDED
@@ -0,0 +1,17 @@
1
+ export const parseMapType = (value) => value === "kiosk" ? "kiosk" : "situm";
2
+ export const mapApiMall = (raw) => {
3
+ const viewerUrl = raw.viewer_url ?? raw.map_embed_url ?? "";
4
+ if (!viewerUrl) {
5
+ throw new Error(`Mall "${raw.name}" is missing viewer_url`);
6
+ }
7
+ return {
8
+ buildingId: String(raw.building_id),
9
+ name: raw.name,
10
+ mapType: parseMapType(raw.map_type),
11
+ viewerUrl,
12
+ mapEmbedUrl: raw.map_embed_url,
13
+ latitude: raw.latitude,
14
+ longitude: raw.longitude,
15
+ };
16
+ };
17
+ export const getViewerUrl = (mall) => mall.viewerUrl;
@@ -0,0 +1,26 @@
1
+ export type MallMapType = "situm" | "kiosk";
2
+ export interface DuonMall {
3
+ buildingId: string;
4
+ name: string;
5
+ mapType: MallMapType;
6
+ viewerUrl: string;
7
+ mapEmbedUrl?: string;
8
+ latitude?: number;
9
+ longitude?: number;
10
+ }
11
+ export interface DuonWayfindingConfig {
12
+ apiBaseUrl: string;
13
+ apiKey: string;
14
+ }
15
+ export interface MallsApiResponse {
16
+ status: number;
17
+ message: Array<{
18
+ building_id: string;
19
+ name: string;
20
+ map_type: string;
21
+ viewer_url?: string;
22
+ map_embed_url?: string;
23
+ latitude?: number;
24
+ longitude?: number;
25
+ }>;
26
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@dtechph/wayfinding-core",
3
+ "version": "0.1.0",
4
+ "description": "Duon Wayfinding SDK — core API client for CMS-driven mall maps",
5
+ "license": "UNLICENSED",
6
+ "author": "dtechph",
7
+ "homepage": "https://github.com/dtechph/DuonSDK/tree/main/packages/core#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/dtechph/DuonSDK/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/dtechph/DuonSDK.git",
14
+ "directory": "packages/core"
15
+ },
16
+ "keywords": [
17
+ "duon",
18
+ "wayfinding",
19
+ "indoor-maps",
20
+ "situm",
21
+ "sdk"
22
+ ],
23
+ "sideEffects": false,
24
+ "main": "dist/index.js",
25
+ "module": "dist/index.js",
26
+ "types": "dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "prepublishOnly": "npm run build",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest"
43
+ },
44
+ "publishConfig": {
45
+ "registry": "https://registry.npmjs.org",
46
+ "access": "public"
47
+ },
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "devDependencies": {
52
+ "typescript": "~5.9.2",
53
+ "vitest": "^3.2.4"
54
+ }
55
+ }