@offlinejs/network 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.
@@ -0,0 +1,35 @@
1
+ import { NetworkMonitor, NetworkState, SyncTransport, TransportOptions, TransportRequest, TransportResponse } from '@offlinejs/types';
2
+
3
+ interface BrowserNetworkMonitorOptions {
4
+ initialOnline?: boolean;
5
+ }
6
+ declare class BrowserNetworkMonitor implements NetworkMonitor {
7
+ private listeners;
8
+ private state;
9
+ constructor(options?: BrowserNetworkMonitorOptions);
10
+ getState(): NetworkState;
11
+ isOnline(): boolean;
12
+ setOnline(online: boolean): void;
13
+ subscribe(listener: (state: NetworkState) => void): () => void;
14
+ destroy(): void;
15
+ private handleOnline;
16
+ private handleOffline;
17
+ private update;
18
+ }
19
+ type FetchTransportOptions = TransportOptions;
20
+ declare class FetchTransport implements SyncTransport {
21
+ readonly contractVersion = 1;
22
+ private readonly baseURL;
23
+ private readonly fetchImplementation;
24
+ private readonly headers;
25
+ private readonly middlewares;
26
+ private readonly timeoutMs;
27
+ constructor(options: FetchTransportOptions);
28
+ request<TData = unknown, TBody = unknown>(request: TransportRequest<TBody>): Promise<TransportResponse<TData>>;
29
+ private resolveHeaders;
30
+ private applyMiddlewares;
31
+ }
32
+ declare const createNetworkMonitor: (options?: BrowserNetworkMonitorOptions) => BrowserNetworkMonitor;
33
+ declare const createFetchTransport: (options: FetchTransportOptions) => FetchTransport;
34
+
35
+ export { BrowserNetworkMonitor, type BrowserNetworkMonitorOptions, FetchTransport, type FetchTransportOptions, createFetchTransport, createNetworkMonitor };
package/dist/index.js ADDED
@@ -0,0 +1,128 @@
1
+ import { SYNC_TRANSPORT_CONTRACT_VERSION } from '@offlinejs/types';
2
+ import { toQueryString } from '@offlinejs/utils';
3
+
4
+ // src/index.ts
5
+ var BrowserNetworkMonitor = class {
6
+ listeners = /* @__PURE__ */ new Set();
7
+ state;
8
+ constructor(options = {}) {
9
+ const navigatorOnline = typeof globalThis.navigator !== "undefined" ? globalThis.navigator.onLine : true;
10
+ this.state = {
11
+ online: options.initialOnline ?? navigatorOnline,
12
+ since: Date.now()
13
+ };
14
+ globalThis.addEventListener?.("online", this.handleOnline);
15
+ globalThis.addEventListener?.("offline", this.handleOffline);
16
+ }
17
+ getState() {
18
+ return { ...this.state };
19
+ }
20
+ isOnline() {
21
+ return this.state.online;
22
+ }
23
+ setOnline(online) {
24
+ this.update(online);
25
+ }
26
+ subscribe(listener) {
27
+ this.listeners.add(listener);
28
+ listener(this.getState());
29
+ return () => {
30
+ this.listeners.delete(listener);
31
+ };
32
+ }
33
+ destroy() {
34
+ globalThis.removeEventListener?.("online", this.handleOnline);
35
+ globalThis.removeEventListener?.("offline", this.handleOffline);
36
+ this.listeners.clear();
37
+ }
38
+ handleOnline = () => {
39
+ this.update(true);
40
+ };
41
+ handleOffline = () => {
42
+ this.update(false);
43
+ };
44
+ update(online) {
45
+ if (this.state.online === online) {
46
+ return;
47
+ }
48
+ this.state = { online, since: Date.now() };
49
+ for (const listener of this.listeners) {
50
+ listener(this.getState());
51
+ }
52
+ }
53
+ };
54
+ var FetchTransport = class {
55
+ contractVersion = SYNC_TRANSPORT_CONTRACT_VERSION;
56
+ baseURL;
57
+ fetchImplementation;
58
+ headers;
59
+ middlewares;
60
+ timeoutMs;
61
+ constructor(options) {
62
+ this.baseURL = options.baseURL.replace(/\/$/, "");
63
+ this.fetchImplementation = options.fetch ?? globalThis.fetch;
64
+ this.headers = options.headers;
65
+ this.middlewares = options.middlewares ?? [];
66
+ this.timeoutMs = options.timeoutMs;
67
+ if (!this.fetchImplementation) {
68
+ throw new Error("A fetch implementation is required for this runtime");
69
+ }
70
+ }
71
+ async request(request) {
72
+ const preparedRequest = await this.applyMiddlewares(request);
73
+ const headers = {
74
+ "content-type": "application/json",
75
+ ...await this.resolveHeaders(),
76
+ ...preparedRequest.headers
77
+ };
78
+ const timeoutMs = preparedRequest.timeoutMs ?? this.timeoutMs;
79
+ const controller = timeoutMs ? new AbortController() : void 0;
80
+ const timeoutId = timeoutMs ? globalThis.setTimeout(() => controller?.abort(), timeoutMs) : void 0;
81
+ try {
82
+ const response = await this.fetchImplementation(
83
+ `${this.baseURL}${preparedRequest.path}${toQueryString(preparedRequest.query)}`,
84
+ {
85
+ headers,
86
+ method: preparedRequest.method,
87
+ ...controller ? { signal: controller.signal } : {},
88
+ ...preparedRequest.body === void 0 ? {} : { body: JSON.stringify(preparedRequest.body) }
89
+ }
90
+ );
91
+ const text = await response.text();
92
+ const data = text.length > 0 ? JSON.parse(text) : void 0;
93
+ if (!response.ok) {
94
+ const error = new Error(`Request failed with status ${response.status}`);
95
+ Object.assign(error, { data, status: response.status });
96
+ throw error;
97
+ }
98
+ return {
99
+ data,
100
+ status: response.status,
101
+ ...response.headers.get("etag") ? { etag: response.headers.get("etag") } : {}
102
+ };
103
+ } finally {
104
+ if (timeoutId) {
105
+ globalThis.clearTimeout(timeoutId);
106
+ }
107
+ }
108
+ }
109
+ async resolveHeaders() {
110
+ if (!this.headers) {
111
+ return {};
112
+ }
113
+ return typeof this.headers === "function" ? this.headers() : this.headers;
114
+ }
115
+ async applyMiddlewares(request) {
116
+ let nextRequest = request;
117
+ for (const middleware of this.middlewares) {
118
+ nextRequest = await middleware({ request: nextRequest });
119
+ }
120
+ return nextRequest;
121
+ }
122
+ };
123
+ var createNetworkMonitor = (options) => new BrowserNetworkMonitor(options);
124
+ var createFetchTransport = (options) => new FetchTransport(options);
125
+
126
+ export { BrowserNetworkMonitor, FetchTransport, createFetchTransport, createNetworkMonitor };
127
+ //# sourceMappingURL=index.js.map
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n SYNC_TRANSPORT_CONTRACT_VERSION,\n type NetworkMonitor,\n type NetworkState,\n type SyncTransport,\n type TransportOptions,\n type TransportRequest,\n type TransportResponse\n} from \"@offlinejs/types\";\nimport { toQueryString } from \"@offlinejs/utils\";\n\nexport interface BrowserNetworkMonitorOptions {\n initialOnline?: boolean;\n}\n\nexport class BrowserNetworkMonitor implements NetworkMonitor {\n private listeners = new Set<(state: NetworkState) => void>();\n private state: NetworkState;\n\n constructor(options: BrowserNetworkMonitorOptions = {}) {\n const navigatorOnline =\n typeof globalThis.navigator !== \"undefined\" ? globalThis.navigator.onLine : true;\n\n this.state = {\n online: options.initialOnline ?? navigatorOnline,\n since: Date.now()\n };\n\n globalThis.addEventListener?.(\"online\", this.handleOnline);\n globalThis.addEventListener?.(\"offline\", this.handleOffline);\n }\n\n getState(): NetworkState {\n return { ...this.state };\n }\n\n isOnline(): boolean {\n return this.state.online;\n }\n\n setOnline(online: boolean): void {\n this.update(online);\n }\n\n subscribe(listener: (state: NetworkState) => void): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n destroy(): void {\n globalThis.removeEventListener?.(\"online\", this.handleOnline);\n globalThis.removeEventListener?.(\"offline\", this.handleOffline);\n this.listeners.clear();\n }\n\n private handleOnline = (): void => {\n this.update(true);\n };\n\n private handleOffline = (): void => {\n this.update(false);\n };\n\n private update(online: boolean): void {\n if (this.state.online === online) {\n return;\n }\n\n this.state = { online, since: Date.now() };\n\n for (const listener of this.listeners) {\n listener(this.getState());\n }\n }\n}\n\nexport type FetchTransportOptions = TransportOptions;\n\nexport class FetchTransport implements SyncTransport {\n readonly contractVersion = SYNC_TRANSPORT_CONTRACT_VERSION;\n\n private readonly baseURL: string;\n private readonly fetchImplementation: typeof fetch;\n private readonly headers: FetchTransportOptions[\"headers\"] | undefined;\n private readonly middlewares: NonNullable<FetchTransportOptions[\"middlewares\"]>;\n private readonly timeoutMs: number | undefined;\n\n constructor(options: FetchTransportOptions) {\n this.baseURL = options.baseURL.replace(/\\/$/, \"\");\n this.fetchImplementation = options.fetch ?? globalThis.fetch;\n this.headers = options.headers;\n this.middlewares = options.middlewares ?? [];\n this.timeoutMs = options.timeoutMs;\n\n if (!this.fetchImplementation) {\n throw new Error(\"A fetch implementation is required for this runtime\");\n }\n }\n\n async request<TData = unknown, TBody = unknown>(\n request: TransportRequest<TBody>\n ): Promise<TransportResponse<TData>> {\n const preparedRequest = await this.applyMiddlewares(request);\n const headers = {\n \"content-type\": \"application/json\",\n ...(await this.resolveHeaders()),\n ...preparedRequest.headers\n };\n const timeoutMs = preparedRequest.timeoutMs ?? this.timeoutMs;\n const controller = timeoutMs ? new AbortController() : undefined;\n const timeoutId = timeoutMs\n ? globalThis.setTimeout(() => controller?.abort(), timeoutMs)\n : undefined;\n\n try {\n const response = await this.fetchImplementation(\n `${this.baseURL}${preparedRequest.path}${toQueryString(preparedRequest.query)}`,\n {\n headers,\n method: preparedRequest.method,\n ...(controller ? { signal: controller.signal } : {}),\n ...(preparedRequest.body === undefined\n ? {}\n : { body: JSON.stringify(preparedRequest.body) })\n }\n );\n\n const text = await response.text();\n const data = text.length > 0 ? (JSON.parse(text) as TData) : (undefined as TData);\n\n if (!response.ok) {\n const error = new Error(`Request failed with status ${response.status}`);\n Object.assign(error, { data, status: response.status });\n throw error;\n }\n\n return {\n data,\n status: response.status,\n ...(response.headers.get(\"etag\") ? { etag: response.headers.get(\"etag\") as string } : {})\n };\n } finally {\n if (timeoutId) {\n globalThis.clearTimeout(timeoutId);\n }\n }\n }\n\n private async resolveHeaders(): Promise<Record<string, string>> {\n if (!this.headers) {\n return {};\n }\n\n return typeof this.headers === \"function\" ? this.headers() : this.headers;\n }\n\n private async applyMiddlewares<TBody>(\n request: TransportRequest<TBody>\n ): Promise<TransportRequest<TBody>> {\n let nextRequest = request;\n\n for (const middleware of this.middlewares) {\n nextRequest = await middleware({ request: nextRequest });\n }\n\n return nextRequest;\n }\n}\n\nexport const createNetworkMonitor = (\n options?: BrowserNetworkMonitorOptions\n): BrowserNetworkMonitor => new BrowserNetworkMonitor(options);\n\nexport const createFetchTransport = (options: FetchTransportOptions): FetchTransport =>\n new FetchTransport(options);\n"]}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@offlinejs/network",
3
+ "version": "0.1.0",
4
+ "description": "Network detection and fetch transport for OfflineJS.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "@offlinejs/utils": "0.1.0",
18
+ "@offlinejs/types": "0.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "tsup": "latest",
22
+ "typescript": "latest"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "registry": "https://registry.npmjs.org/"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "typecheck": "tsc --noEmit"
31
+ }
32
+ }