@furystack/shades 10.0.4 → 10.0.6
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/esm/services/location-service.d.ts +6 -1
- package/esm/services/location-service.d.ts.map +1 -1
- package/esm/services/location-service.js +33 -21
- package/esm/services/location-service.js.map +1 -1
- package/esm/services/location-service.spec.js +22 -2
- package/esm/services/location-service.spec.js.map +1 -1
- package/package.json +3 -3
- package/src/services/location-service.spec.ts +29 -2
- package/src/services/location-service.tsx +44 -22
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Disposable } from '@furystack/utils';
|
|
2
2
|
import { ObservableValue } from '@furystack/utils';
|
|
3
|
+
import { type Injector } from '@furystack/inject';
|
|
4
|
+
import { deserializeQueryString as defaultDeserializeQueryString, serializeToQueryString as defaultSerializeToQueryString } from '@furystack/rest';
|
|
3
5
|
export declare class LocationService implements Disposable {
|
|
6
|
+
private readonly serializeToQueryString;
|
|
7
|
+
readonly deserializeQueryString: (fullQueryString: string) => any;
|
|
8
|
+
constructor(serializeToQueryString?: <T extends object>(queryObject: T) => string, deserializeQueryString?: (fullQueryString: string) => any);
|
|
4
9
|
dispose(): void;
|
|
5
10
|
/**
|
|
6
11
|
* Observable value that will be updated when the location pathname (e.g. /page/1) changes
|
|
@@ -29,6 +34,6 @@ export declare class LocationService implements Disposable {
|
|
|
29
34
|
private replaceStateTracer;
|
|
30
35
|
private popStateListener;
|
|
31
36
|
private hashChangeListener;
|
|
32
|
-
constructor();
|
|
33
37
|
}
|
|
38
|
+
export declare const useCustomSearchStateSerializer: (injector: Injector, serialize: typeof defaultSerializeToQueryString, deserialize: typeof defaultDeserializeQueryString) => void;
|
|
34
39
|
//# sourceMappingURL=location-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location-service.d.ts","sourceRoot":"","sources":["../../src/services/location-service.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAS,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"location-service.d.ts","sourceRoot":"","sources":["../../src/services/location-service.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAS,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EACL,sBAAsB,IAAI,6BAA6B,EACvD,sBAAsB,IAAI,6BAA6B,EACxD,MAAM,iBAAiB,CAAA;AACxB,qBACa,eAAgB,YAAW,UAAU;IAE9C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;aAEvB,sBAAsB;gBAFrB,sBAAsB,+CAAgC,EAEvD,sBAAsB,mCAAgC;IAsBjE,OAAO;IAWd;;OAEG;IACI,qBAAqB,0BAAuD;IAEnF;;OAEG;IACI,qBAAqB,0BAAqC;IAEjE;;OAEG;IACI,uBAAuB,0BAA+C;IAEtE,mCAAmC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAA;IAEzD,4BAA4B,mDAEjC;IAEK,WAAW,aAIL;IAEb,SAAgB,sBAAsB,oCAA0C;IAEhF;;;;;OAKG;IACI,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IA+BrD,OAAO,CAAC,eAAe,CAAY;IACnC,OAAO,CAAC,kBAAkB,CAAY;IAEtC,OAAO,CAAC,gBAAgB,CAEvB;IAED,OAAO,CAAC,kBAAkB,CAEzB;CACF;AAED,eAAO,MAAM,8BAA8B,aAC/B,QAAQ,aACP,oCAAoC,eAClC,oCAAoC,SAQlD,CAAA"}
|
|
@@ -9,8 +9,29 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { ObservableValue, Trace } from '@furystack/utils';
|
|
11
11
|
import { Injectable } from '@furystack/inject';
|
|
12
|
-
import { deserializeQueryString, serializeToQueryString } from '@furystack/rest';
|
|
12
|
+
import { deserializeQueryString as defaultDeserializeQueryString, serializeToQueryString as defaultSerializeToQueryString, } from '@furystack/rest';
|
|
13
13
|
let LocationService = class LocationService {
|
|
14
|
+
serializeToQueryString;
|
|
15
|
+
deserializeQueryString;
|
|
16
|
+
constructor(serializeToQueryString = defaultSerializeToQueryString, deserializeQueryString = defaultDeserializeQueryString) {
|
|
17
|
+
this.serializeToQueryString = serializeToQueryString;
|
|
18
|
+
this.deserializeQueryString = deserializeQueryString;
|
|
19
|
+
window.addEventListener('popstate', this.popStateListener);
|
|
20
|
+
window.addEventListener('hashchange', this.hashChangeListener);
|
|
21
|
+
this.pushStateTracer = Trace.method({
|
|
22
|
+
object: history,
|
|
23
|
+
method: history.pushState,
|
|
24
|
+
isAsync: false,
|
|
25
|
+
onFinished: () => this.updateState(),
|
|
26
|
+
});
|
|
27
|
+
this.replaceStateTracer = Trace.method({
|
|
28
|
+
object: history,
|
|
29
|
+
method: history.replaceState,
|
|
30
|
+
isAsync: false,
|
|
31
|
+
onFinished: () => this.updateState(),
|
|
32
|
+
});
|
|
33
|
+
this.onDeserializedLocationSearchChanged = new ObservableValue(this.deserializeQueryString(location.search));
|
|
34
|
+
}
|
|
14
35
|
dispose() {
|
|
15
36
|
window.removeEventListener('popstate', this.popStateListener);
|
|
16
37
|
window.removeEventListener('hashchange', this.hashChangeListener);
|
|
@@ -33,9 +54,9 @@ let LocationService = class LocationService {
|
|
|
33
54
|
* Observable value that will be updated when the location search (e.g. ?search=1) changes
|
|
34
55
|
*/
|
|
35
56
|
onLocationSearchChanged = new ObservableValue(location.search);
|
|
36
|
-
onDeserializedLocationSearchChanged
|
|
57
|
+
onDeserializedLocationSearchChanged;
|
|
37
58
|
locationDeserializerObserver = this.onLocationSearchChanged.subscribe((search) => {
|
|
38
|
-
this.onDeserializedLocationSearchChanged.setValue(deserializeQueryString(search));
|
|
59
|
+
this.onDeserializedLocationSearchChanged.setValue(this.deserializeQueryString(search));
|
|
39
60
|
});
|
|
40
61
|
updateState = (() => {
|
|
41
62
|
this.onLocationPathChanged.setValue(location.pathname);
|
|
@@ -61,7 +82,7 @@ let LocationService = class LocationService {
|
|
|
61
82
|
newObservable.subscribe((value) => {
|
|
62
83
|
const currentQueryStringObject = this.onDeserializedLocationSearchChanged.getValue();
|
|
63
84
|
if (currentQueryStringObject[key] !== value) {
|
|
64
|
-
const params = serializeToQueryString({ ...currentQueryStringObject, [key]: value });
|
|
85
|
+
const params = this.serializeToQueryString({ ...currentQueryStringObject, [key]: value });
|
|
65
86
|
const newUrl = `${location.pathname}?${params}`;
|
|
66
87
|
history.pushState({}, '', newUrl);
|
|
67
88
|
}
|
|
@@ -82,26 +103,17 @@ let LocationService = class LocationService {
|
|
|
82
103
|
hashChangeListener = (_ev) => {
|
|
83
104
|
this.updateState();
|
|
84
105
|
};
|
|
85
|
-
constructor() {
|
|
86
|
-
window.addEventListener('popstate', this.popStateListener);
|
|
87
|
-
window.addEventListener('hashchange', this.hashChangeListener);
|
|
88
|
-
this.pushStateTracer = Trace.method({
|
|
89
|
-
object: history,
|
|
90
|
-
method: history.pushState,
|
|
91
|
-
isAsync: false,
|
|
92
|
-
onFinished: () => this.updateState(),
|
|
93
|
-
});
|
|
94
|
-
this.replaceStateTracer = Trace.method({
|
|
95
|
-
object: history,
|
|
96
|
-
method: history.replaceState,
|
|
97
|
-
isAsync: false,
|
|
98
|
-
onFinished: () => this.updateState(),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
106
|
};
|
|
102
107
|
LocationService = __decorate([
|
|
103
108
|
Injectable({ lifetime: 'singleton' }),
|
|
104
|
-
__metadata("design:paramtypes", [])
|
|
109
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
105
110
|
], LocationService);
|
|
106
111
|
export { LocationService };
|
|
112
|
+
export const useCustomSearchStateSerializer = (injector, serialize, deserialize) => {
|
|
113
|
+
if (injector.cachedSingletons.has(LocationService)) {
|
|
114
|
+
throw new Error('useCustomSearchStateSerializer must be called before the LocationService is instantiated');
|
|
115
|
+
}
|
|
116
|
+
const locationService = new LocationService(serialize, deserialize);
|
|
117
|
+
injector.setExplicitInstance(locationService, LocationService);
|
|
118
|
+
};
|
|
107
119
|
//# sourceMappingURL=location-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location-service.js","sourceRoot":"","sources":["../../src/services/location-service.tsx"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"location-service.js","sourceRoot":"","sources":["../../src/services/location-service.tsx"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@ global.TextEncoder = TextEncoder;
|
|
|
3
3
|
global.TextDecoder = TextDecoder;
|
|
4
4
|
import { Injector } from '@furystack/inject';
|
|
5
5
|
import { using } from '@furystack/utils';
|
|
6
|
-
import { serializeValue } from '@furystack/rest';
|
|
7
|
-
import { LocationService } from './location-service.js';
|
|
6
|
+
import { deserializeQueryString, serializeToQueryString, serializeValue } from '@furystack/rest';
|
|
7
|
+
import { LocationService, useCustomSearchStateSerializer } from './location-service.js';
|
|
8
8
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
9
|
describe('LocationService', () => {
|
|
10
10
|
beforeEach(() => {
|
|
@@ -85,6 +85,26 @@ describe('LocationService', () => {
|
|
|
85
85
|
expect(location.search).toBe('?test=IjIi');
|
|
86
86
|
});
|
|
87
87
|
});
|
|
88
|
+
it('Should throw an error when trying to use a custom serializer after LocationService has been instantiated', () => {
|
|
89
|
+
using(new Injector(), (i) => {
|
|
90
|
+
const customSerializer = vi.fn((value) => serializeToQueryString(value));
|
|
91
|
+
const customDeserializer = vi.fn((value) => deserializeQueryString(value));
|
|
92
|
+
i.getInstance(LocationService);
|
|
93
|
+
expect(() => useCustomSearchStateSerializer(i, customSerializer, customDeserializer)).toThrowError('useCustomSearchStateSerializer must be called before the LocationService is instantiated');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
it('Should use custom serializer and deserializer', () => {
|
|
97
|
+
using(new Injector(), (i) => {
|
|
98
|
+
const customSerializer = vi.fn((value) => serializeToQueryString(value));
|
|
99
|
+
const customDeserializer = vi.fn((value) => deserializeQueryString(value));
|
|
100
|
+
useCustomSearchStateSerializer(i, customSerializer, customDeserializer);
|
|
101
|
+
const locationService = i.getInstance(LocationService);
|
|
102
|
+
const testSearchParam = locationService.useSearchParam('test', { value: 'foo' });
|
|
103
|
+
testSearchParam.setValue({ value: 'bar' });
|
|
104
|
+
expect(customSerializer).toBeCalledWith({ test: { value: 'bar' } });
|
|
105
|
+
expect(customDeserializer).toBeCalledWith('?test=eyJ2YWx1ZSI6ImJhciJ9');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
88
108
|
});
|
|
89
109
|
});
|
|
90
110
|
//# sourceMappingURL=location-service.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location-service.spec.js","sourceRoot":"","sources":["../../src/services/location-service.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AAE/C,MAAM,CAAC,WAAW,GAAG,WAAW,CAAA;AAChC,MAAM,CAAC,WAAW,GAAG,WAAkB,CAAA;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"location-service.spec.js","sourceRoot":"","sources":["../../src/services/location-service.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/shades",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.6",
|
|
4
4
|
"description": "Google Authentication Provider for FuryStack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -38,14 +38,14 @@
|
|
|
38
38
|
"homepage": "https://github.com/furystack/furystack",
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/jsdom": "^21.1.7",
|
|
41
|
-
"@types/node": "^20.
|
|
41
|
+
"@types/node": "^20.14.2",
|
|
42
42
|
"jsdom": "^24.1.0",
|
|
43
43
|
"typescript": "^5.4.5",
|
|
44
44
|
"vitest": "^1.6.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@furystack/inject": "^11.0.3",
|
|
48
|
-
"@furystack/rest": "^7.0.
|
|
48
|
+
"@furystack/rest": "^7.0.5",
|
|
49
49
|
"@furystack/utils": "^7.0.2",
|
|
50
50
|
"path-to-regexp": "^6.2.2",
|
|
51
51
|
"semaphore-async-await": "^1.5.1"
|
|
@@ -5,8 +5,8 @@ global.TextDecoder = TextDecoder as any
|
|
|
5
5
|
|
|
6
6
|
import { Injector } from '@furystack/inject'
|
|
7
7
|
import { using } from '@furystack/utils'
|
|
8
|
-
import { serializeValue } from '@furystack/rest'
|
|
9
|
-
import { LocationService } from './location-service.js'
|
|
8
|
+
import { deserializeQueryString, serializeToQueryString, serializeValue } from '@furystack/rest'
|
|
9
|
+
import { LocationService, useCustomSearchStateSerializer } from './location-service.js'
|
|
10
10
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
11
11
|
|
|
12
12
|
describe('LocationService', () => {
|
|
@@ -101,5 +101,32 @@ describe('LocationService', () => {
|
|
|
101
101
|
expect(location.search).toBe('?test=IjIi')
|
|
102
102
|
})
|
|
103
103
|
})
|
|
104
|
+
|
|
105
|
+
it('Should throw an error when trying to use a custom serializer after LocationService has been instantiated', () => {
|
|
106
|
+
using(new Injector(), (i) => {
|
|
107
|
+
const customSerializer = vi.fn((value: any) => serializeToQueryString(value))
|
|
108
|
+
const customDeserializer = vi.fn((value: any) => deserializeQueryString(value))
|
|
109
|
+
i.getInstance(LocationService)
|
|
110
|
+
expect(() => useCustomSearchStateSerializer(i, customSerializer, customDeserializer)).toThrowError(
|
|
111
|
+
'useCustomSearchStateSerializer must be called before the LocationService is instantiated',
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('Should use custom serializer and deserializer', () => {
|
|
117
|
+
using(new Injector(), (i) => {
|
|
118
|
+
const customSerializer = vi.fn((value: any) => serializeToQueryString(value))
|
|
119
|
+
const customDeserializer = vi.fn((value: any) => deserializeQueryString(value))
|
|
120
|
+
|
|
121
|
+
useCustomSearchStateSerializer(i, customSerializer, customDeserializer)
|
|
122
|
+
|
|
123
|
+
const locationService = i.getInstance(LocationService)
|
|
124
|
+
const testSearchParam = locationService.useSearchParam('test', { value: 'foo' })
|
|
125
|
+
|
|
126
|
+
testSearchParam.setValue({ value: 'bar' })
|
|
127
|
+
expect(customSerializer).toBeCalledWith({ test: { value: 'bar' } })
|
|
128
|
+
expect(customDeserializer).toBeCalledWith('?test=eyJ2YWx1ZSI6ImJhciJ9')
|
|
129
|
+
})
|
|
130
|
+
})
|
|
104
131
|
})
|
|
105
132
|
})
|
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
import type { Disposable } from '@furystack/utils'
|
|
2
2
|
import { ObservableValue, Trace } from '@furystack/utils'
|
|
3
|
-
import { Injectable } from '@furystack/inject'
|
|
4
|
-
import {
|
|
3
|
+
import { Injectable, type Injector } from '@furystack/inject'
|
|
4
|
+
import {
|
|
5
|
+
deserializeQueryString as defaultDeserializeQueryString,
|
|
6
|
+
serializeToQueryString as defaultSerializeToQueryString,
|
|
7
|
+
} from '@furystack/rest'
|
|
5
8
|
@Injectable({ lifetime: 'singleton' })
|
|
6
9
|
export class LocationService implements Disposable {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly serializeToQueryString = defaultSerializeToQueryString,
|
|
12
|
+
|
|
13
|
+
public readonly deserializeQueryString = defaultDeserializeQueryString,
|
|
14
|
+
) {
|
|
15
|
+
window.addEventListener('popstate', this.popStateListener)
|
|
16
|
+
window.addEventListener('hashchange', this.hashChangeListener)
|
|
17
|
+
|
|
18
|
+
this.pushStateTracer = Trace.method({
|
|
19
|
+
object: history,
|
|
20
|
+
method: history.pushState,
|
|
21
|
+
isAsync: false,
|
|
22
|
+
onFinished: () => this.updateState(),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
this.replaceStateTracer = Trace.method({
|
|
26
|
+
object: history,
|
|
27
|
+
method: history.replaceState,
|
|
28
|
+
isAsync: false,
|
|
29
|
+
onFinished: () => this.updateState(),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
this.onDeserializedLocationSearchChanged = new ObservableValue(this.deserializeQueryString(location.search))
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
public dispose() {
|
|
8
36
|
window.removeEventListener('popstate', this.popStateListener)
|
|
9
37
|
window.removeEventListener('hashchange', this.hashChangeListener)
|
|
@@ -30,10 +58,10 @@ export class LocationService implements Disposable {
|
|
|
30
58
|
*/
|
|
31
59
|
public onLocationSearchChanged = new ObservableValue<string>(location.search)
|
|
32
60
|
|
|
33
|
-
public onDeserializedLocationSearchChanged
|
|
61
|
+
public onDeserializedLocationSearchChanged: ObservableValue<any>
|
|
34
62
|
|
|
35
63
|
public locationDeserializerObserver = this.onLocationSearchChanged.subscribe((search) => {
|
|
36
|
-
this.onDeserializedLocationSearchChanged.setValue(deserializeQueryString(search))
|
|
64
|
+
this.onDeserializedLocationSearchChanged.setValue(this.deserializeQueryString(search))
|
|
37
65
|
})
|
|
38
66
|
|
|
39
67
|
public updateState = (() => {
|
|
@@ -66,7 +94,7 @@ export class LocationService implements Disposable {
|
|
|
66
94
|
newObservable.subscribe((value) => {
|
|
67
95
|
const currentQueryStringObject = this.onDeserializedLocationSearchChanged.getValue()
|
|
68
96
|
if (currentQueryStringObject[key] !== value) {
|
|
69
|
-
const params = serializeToQueryString({ ...currentQueryStringObject, [key]: value })
|
|
97
|
+
const params = this.serializeToQueryString({ ...currentQueryStringObject, [key]: value })
|
|
70
98
|
const newUrl = `${location.pathname}?${params}`
|
|
71
99
|
history.pushState({}, '', newUrl)
|
|
72
100
|
}
|
|
@@ -91,23 +119,17 @@ export class LocationService implements Disposable {
|
|
|
91
119
|
private hashChangeListener = (_ev: HashChangeEvent) => {
|
|
92
120
|
this.updateState()
|
|
93
121
|
}
|
|
122
|
+
}
|
|
94
123
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
isAsync: false,
|
|
103
|
-
onFinished: () => this.updateState(),
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
this.replaceStateTracer = Trace.method({
|
|
107
|
-
object: history,
|
|
108
|
-
method: history.replaceState,
|
|
109
|
-
isAsync: false,
|
|
110
|
-
onFinished: () => this.updateState(),
|
|
111
|
-
})
|
|
124
|
+
export const useCustomSearchStateSerializer = (
|
|
125
|
+
injector: Injector,
|
|
126
|
+
serialize: typeof defaultSerializeToQueryString,
|
|
127
|
+
deserialize: typeof defaultDeserializeQueryString,
|
|
128
|
+
) => {
|
|
129
|
+
if (injector.cachedSingletons.has(LocationService)) {
|
|
130
|
+
throw new Error('useCustomSearchStateSerializer must be called before the LocationService is instantiated')
|
|
112
131
|
}
|
|
132
|
+
|
|
133
|
+
const locationService = new LocationService(serialize, deserialize)
|
|
134
|
+
injector.setExplicitInstance(locationService, LocationService)
|
|
113
135
|
}
|