@omerlo/omerlo-webkit 0.0.4 → 0.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/README.md +24 -24
- package/dist/components/OmerloWebkit.svelte +2 -3
- package/dist/components/OmerloWebkit.svelte.d.ts +1 -1
- package/dist/omerlo/index.d.ts +32 -4
- package/dist/omerlo/reader/endpoints/categories.d.ts +18 -0
- package/dist/omerlo/reader/endpoints/categories.js +39 -0
- package/dist/omerlo/reader/endpoints/content-templates.d.ts +23 -0
- package/dist/omerlo/reader/endpoints/content-templates.js +34 -0
- package/dist/omerlo/reader/endpoints/contents.d.ts +122 -0
- package/dist/omerlo/reader/endpoints/contents.js +187 -0
- package/dist/omerlo/reader/endpoints/device.js +1 -1
- package/dist/omerlo/reader/endpoints/events.d.ts +32 -0
- package/dist/omerlo/reader/endpoints/events.js +45 -0
- package/dist/omerlo/reader/endpoints/magazines.d.ts +104 -0
- package/dist/omerlo/reader/endpoints/magazines.js +135 -0
- package/dist/omerlo/reader/endpoints/media.d.ts +65 -0
- package/dist/omerlo/reader/endpoints/media.js +133 -0
- package/dist/omerlo/reader/endpoints/menu.d.ts +31 -0
- package/dist/omerlo/reader/endpoints/menu.js +49 -0
- package/dist/omerlo/reader/endpoints/notification.d.ts +5 -5
- package/dist/omerlo/reader/endpoints/notification.js +6 -8
- package/dist/omerlo/reader/endpoints/oauth.js +1 -1
- package/dist/omerlo/reader/endpoints/organizations.d.ts +40 -0
- package/dist/omerlo/reader/endpoints/organizations.js +72 -0
- package/dist/omerlo/reader/endpoints/person.d.ts +30 -0
- package/dist/omerlo/reader/endpoints/person.js +39 -0
- package/dist/omerlo/reader/endpoints/profileType.d.ts +27 -0
- package/dist/omerlo/reader/endpoints/profileType.js +28 -0
- package/dist/omerlo/reader/endpoints/profiles.d.ts +70 -0
- package/dist/omerlo/reader/endpoints/profiles.js +97 -0
- package/dist/omerlo/reader/endpoints/projects.d.ts +28 -0
- package/dist/omerlo/reader/endpoints/projects.js +37 -0
- package/dist/omerlo/reader/endpoints/visuals.d.ts +34 -0
- package/dist/omerlo/reader/endpoints/visuals.js +44 -0
- package/dist/omerlo/reader/endpoints/webpage.d.ts +20 -0
- package/dist/omerlo/reader/endpoints/webpage.js +21 -0
- package/dist/omerlo/reader/fetchers.d.ts +30 -2
- package/dist/omerlo/reader/fetchers.js +12 -0
- package/dist/omerlo/reader/index.d.ts +18 -1
- package/dist/omerlo/reader/index.js +18 -0
- package/dist/omerlo/reader/server/email.d.ts +12 -0
- package/dist/omerlo/reader/server/email.js +30 -0
- package/dist/omerlo/reader/server/hooks.d.ts +1 -1
- package/dist/omerlo/reader/server/hooks.js +39 -20
- package/dist/omerlo/reader/server/index.d.ts +1 -0
- package/dist/omerlo/reader/server/index.js +1 -0
- package/dist/omerlo/reader/server/token.d.ts +6 -6
- package/dist/omerlo/reader/server/token.js +7 -7
- package/dist/omerlo/reader/server/utils.d.ts +4 -4
- package/dist/omerlo/reader/server/utils.js +13 -7
- package/dist/omerlo/reader/stores/user_session.d.ts +2 -2
- package/dist/omerlo/reader/stores/user_session.js +11 -9
- package/dist/omerlo/reader/utils/api.d.ts +1 -1
- package/dist/omerlo/reader/utils/api.js +4 -3
- package/dist/omerlo/reader/utils/assocs.d.ts +5 -4
- package/dist/omerlo/reader/utils/assocs.js +23 -0
- package/dist/omerlo/reader/utils/parseHelpers.d.ts +7 -0
- package/dist/omerlo/reader/utils/parseHelpers.js +22 -0
- package/dist/omerlo/reader/utils/request.d.ts +10 -8
- package/dist/omerlo/reader/utils/request.js +39 -11
- package/dist/omerlo/reader/utils/response.d.ts +1 -1
- package/dist/omerlo/reader/utils/response.js +1 -1
- package/eslint.config.js +11 -2
- package/package.json +3 -2
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
export * from './stores/user_session';
|
|
2
|
-
export type * from './endpoints/oauth';
|
|
3
2
|
export type * from './endpoints/accounts';
|
|
3
|
+
export type * from './endpoints/categories';
|
|
4
|
+
export type * from './endpoints/content-templates';
|
|
5
|
+
export type * from './endpoints/contents';
|
|
6
|
+
export type * from './endpoints/device';
|
|
7
|
+
export type * from './endpoints/events';
|
|
8
|
+
export type * from './endpoints/magazines';
|
|
9
|
+
export type * from './endpoints/media';
|
|
10
|
+
export type * from './endpoints/menu';
|
|
11
|
+
export type * from './endpoints/notification';
|
|
12
|
+
export type * from './endpoints/oauth';
|
|
13
|
+
export type * from './endpoints/organizations';
|
|
14
|
+
export type * from './endpoints/person';
|
|
15
|
+
export type * from './endpoints/profileType';
|
|
16
|
+
export type * from './endpoints/profiles';
|
|
17
|
+
export type * from './endpoints/projects';
|
|
18
|
+
export type * from './endpoints/visuals';
|
|
19
|
+
export type * from './endpoints/webpage';
|
|
20
|
+
export type * from './utils/response';
|
|
@@ -1 +1,19 @@
|
|
|
1
|
+
import { parseCategory } from './endpoints/categories';
|
|
2
|
+
import { parseProfileBlock, parseProfileSummary } from './endpoints/profiles';
|
|
3
|
+
import { parseContentBlockTemplate, parseContentTemplate } from './endpoints/content-templates';
|
|
4
|
+
import { parseProfileTypeSummary } from './endpoints/profileType';
|
|
5
|
+
import { registerAssocParser } from './utils/assocs';
|
|
6
|
+
import { parseContentSummary } from './endpoints/contents';
|
|
7
|
+
import { parseIssueBlockConfiguration, parseIssueType } from './endpoints/magazines';
|
|
1
8
|
export * from './stores/user_session';
|
|
9
|
+
registerAssocParser('categories', parseCategory);
|
|
10
|
+
registerAssocParser('profiles', parseProfileSummary);
|
|
11
|
+
registerAssocParser('content_templates', parseContentTemplate);
|
|
12
|
+
registerAssocParser('profile_types', parseProfileTypeSummary);
|
|
13
|
+
registerAssocParser('profile_block_types', parseProfileBlock);
|
|
14
|
+
registerAssocParser('content_block_templates', parseContentBlockTemplate);
|
|
15
|
+
registerAssocParser('contents', parseContentSummary);
|
|
16
|
+
registerAssocParser('issue_types', parseIssueType);
|
|
17
|
+
registerAssocParser('issue_block_configurations', parseIssueBlockConfiguration);
|
|
18
|
+
// NOTE: This one is for retro compatibility with publisher public api v2
|
|
19
|
+
registerAssocParser('templates', parseContentTemplate);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface EmailParams {
|
|
2
|
+
context: 'platform' | 'media';
|
|
3
|
+
to: string;
|
|
4
|
+
subject: string;
|
|
5
|
+
body: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Send an email.
|
|
9
|
+
*
|
|
10
|
+
* Return `true` if the email has been sent. It could take a couple of minutes to be received.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sendEmail(params: EmailParams): Promise<boolean>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { env } from '$env/dynamic/private';
|
|
2
|
+
import { ApiError } from '../utils/api';
|
|
3
|
+
import { getApplicationToken } from './utils';
|
|
4
|
+
/**
|
|
5
|
+
* Send an email.
|
|
6
|
+
*
|
|
7
|
+
* Return `true` if the email has been sent. It could take a couple of minutes to be received.
|
|
8
|
+
*/
|
|
9
|
+
export async function sendEmail(params) {
|
|
10
|
+
const url = getOmerloEndpoint();
|
|
11
|
+
const accessToken = await getApplicationToken();
|
|
12
|
+
const headers = new Headers({
|
|
13
|
+
'x-omerlo-media-id': env.PRIVATE_OMERLO_MEDIA_ID,
|
|
14
|
+
Authorization: `Bearer ${accessToken}`,
|
|
15
|
+
'Content-Type': 'text/html'
|
|
16
|
+
});
|
|
17
|
+
url.pathname = '/api/media/v1/mail/send';
|
|
18
|
+
url.searchParams.append('context', params.context);
|
|
19
|
+
url.searchParams.append('to', params.to);
|
|
20
|
+
url.searchParams.append('subject', params.subject);
|
|
21
|
+
const resp = await fetch(url, { method: 'POST', headers, body: params.body });
|
|
22
|
+
if (resp.ok) {
|
|
23
|
+
return Promise.resolve(true);
|
|
24
|
+
}
|
|
25
|
+
const payload = await resp.json();
|
|
26
|
+
throw new ApiError(resp.status, payload.error, resp.statusText);
|
|
27
|
+
}
|
|
28
|
+
function getOmerloEndpoint() {
|
|
29
|
+
return new URL(`${env.PRIVATE_OMERLO_PROTOCOL}://${env.PRIVATE_OMERLO_HOST}`);
|
|
30
|
+
}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import { error } from
|
|
1
|
+
import { error } from '@sveltejs/kit';
|
|
2
2
|
import { env } from '$env/dynamic/private';
|
|
3
|
-
import { ApiError } from
|
|
4
|
-
import { clearAuthorizationCookies, clearAuthorizationUsingHeader, getAccessTokenFromCookie, getApplicationToken, getRefreshTokenFromCookie, setAuthorizationCookies } from
|
|
5
|
-
import { refresh } from
|
|
3
|
+
import { ApiError } from '../utils/api';
|
|
4
|
+
import { clearAuthorizationCookies, clearAuthorizationUsingHeader, getAccessTokenFromCookie, getApplicationToken, getRefreshTokenFromCookie, setAuthorizationCookies } from './utils';
|
|
5
|
+
import { refresh } from './token';
|
|
6
6
|
// NOTE: inspired by https://sami.website/blog/sveltekit-api-reverse-proxy
|
|
7
7
|
const handleApiProxy = async ({ event, ...tail }) => {
|
|
8
|
+
// NOTE: It's important to reset the port BEFORE settings the host because the host could contains the port
|
|
9
|
+
// Example: localhost:4000
|
|
10
|
+
event.url.port = '';
|
|
8
11
|
event.url.host = env.PRIVATE_OMERLO_HOST;
|
|
9
12
|
event.url.protocol = env.PRIVATE_OMERLO_PROTOCOL;
|
|
10
|
-
event.
|
|
11
|
-
let accessToken = event.locals.accessToken;
|
|
12
|
-
if (!accessToken) {
|
|
13
|
-
accessToken = await getApplicationToken();
|
|
14
|
-
}
|
|
13
|
+
const accessToken = event.locals.accessToken ?? (await getApplicationToken());
|
|
15
14
|
const body = event.request.body;
|
|
16
15
|
const method = event.request.method;
|
|
17
|
-
const headers = new Headers(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})
|
|
16
|
+
const headers = new Headers({
|
|
17
|
+
'x-omerlo-media-id': env.PRIVATE_OMERLO_MEDIA_ID,
|
|
18
|
+
Authorization: `Bearer ${accessToken}`
|
|
19
|
+
});
|
|
20
|
+
const contentType = event.request.headers.get('content-type');
|
|
21
|
+
if (contentType)
|
|
22
|
+
headers.set('Content-Type', contentType);
|
|
23
|
+
return await fetch(event.url.toString(), { body, headers, method, duplex: 'half' })
|
|
25
24
|
.then(async (resp) => {
|
|
26
25
|
const headers = new Headers();
|
|
27
26
|
if (resp.status === 401 && event.locals.accessToken) {
|
|
@@ -32,12 +31,12 @@ const handleApiProxy = async ({ event, ...tail }) => {
|
|
|
32
31
|
const responseOpts = {
|
|
33
32
|
headers: headers,
|
|
34
33
|
status: resp.status,
|
|
35
|
-
statusText: resp.statusText
|
|
34
|
+
statusText: resp.statusText
|
|
36
35
|
};
|
|
37
36
|
return new Response(resp.body, responseOpts);
|
|
38
37
|
})
|
|
39
38
|
.catch((err) => {
|
|
40
|
-
console.log(
|
|
39
|
+
console.log('Could not proxy API request: ', err);
|
|
41
40
|
error(500, 'Something went wrong');
|
|
42
41
|
});
|
|
43
42
|
};
|
|
@@ -45,13 +44,33 @@ export const proxyHook = async ({ event, resolve }) => {
|
|
|
45
44
|
if (event.url.pathname.startsWith('/api/media/v1')) {
|
|
46
45
|
return await handleApiProxy({ event, resolve });
|
|
47
46
|
}
|
|
47
|
+
// TODO remove once every API will be done in Reader
|
|
48
|
+
if (event.url.pathname.startsWith('/api/publisher/')) {
|
|
49
|
+
const pathAfterPublisher = event.url.pathname.slice('/api/publisher/'.length);
|
|
50
|
+
const resourceConfigs = {
|
|
51
|
+
'media/': { idKey: env['PRIVATE_OMERLO_MEDIA_ID'], resourcePath: 'medias' },
|
|
52
|
+
'organization/': {
|
|
53
|
+
idKey: env['PRIVATE_OMERLO_ORGANIZATION_ID'],
|
|
54
|
+
resourcePath: 'organizations'
|
|
55
|
+
},
|
|
56
|
+
'issue/': { idKey: env['PRIVATE_OMERLO_ISSUE_ID'], resourcePath: 'issues' }
|
|
57
|
+
};
|
|
58
|
+
for (const [prefix, config] of Object.entries(resourceConfigs)) {
|
|
59
|
+
if (pathAfterPublisher.startsWith(prefix)) {
|
|
60
|
+
event.url.pathname = event.url.pathname.replace(`/api/publisher/${prefix}`, `/api/public/publisher/v2/${config.resourcePath}/${config.idKey}/`);
|
|
61
|
+
return handleApiProxy({ event, resolve });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
event.url.pathname = event.url.pathname.replace('/api/publisher/', `/api/public/publisher/v2/`);
|
|
65
|
+
return handleApiProxy({ event, resolve });
|
|
66
|
+
}
|
|
48
67
|
return resolve(event);
|
|
49
68
|
};
|
|
50
69
|
export const handleUserToken = async ({ event, resolve }) => {
|
|
51
70
|
const accessToken = getAccessTokenFromCookie(event.cookies);
|
|
52
71
|
const refreshToken = getRefreshTokenFromCookie(event.cookies);
|
|
53
72
|
const opts = {
|
|
54
|
-
filterSerializedResponseHeaders: (name) => name == 'x-logout'
|
|
73
|
+
filterSerializedResponseHeaders: (name) => name == 'x-logout'
|
|
55
74
|
};
|
|
56
75
|
if (accessToken) {
|
|
57
76
|
event.locals.accessToken = accessToken;
|
|
@@ -4,16 +4,16 @@ export interface exchangeAuthorizationCodeParams {
|
|
|
4
4
|
oauthProviderId: string;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
* Exchange an authorization code for an omerlo's token.
|
|
8
|
+
*/
|
|
9
9
|
export declare function exchangeAuthorizationCode(params: exchangeAuthorizationCodeParams): Promise<OmerloToken>;
|
|
10
10
|
/**
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
* Refresh the user's token.
|
|
12
|
+
*/
|
|
13
13
|
export declare function refresh(refreshToken: string): Promise<OmerloToken>;
|
|
14
14
|
/**
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
* Authenticate anonymously a user to get an anonymous token.
|
|
16
|
+
*/
|
|
17
17
|
export declare function getAnonymousToken(scope: string): Promise<OmerloToken>;
|
|
18
18
|
export interface OmerloToken {
|
|
19
19
|
accessToken: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { env } from '$env/dynamic/private';
|
|
2
2
|
import { ApiError } from '../utils/api';
|
|
3
3
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
* Exchange an authorization code for an omerlo's token.
|
|
5
|
+
*/
|
|
6
6
|
export async function exchangeAuthorizationCode(params) {
|
|
7
7
|
const url = getTokenEndpoint();
|
|
8
8
|
url.pathname = '/api/media/v1/oauth/token';
|
|
@@ -19,8 +19,8 @@ export async function exchangeAuthorizationCode(params) {
|
|
|
19
19
|
throw new ApiError(resp.status, payload.error, resp.statusText);
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
* Refresh the user's token.
|
|
23
|
+
*/
|
|
24
24
|
export async function refresh(refreshToken) {
|
|
25
25
|
const url = getTokenEndpoint();
|
|
26
26
|
url.pathname = '/api/media/v1/oauth/token';
|
|
@@ -34,8 +34,8 @@ export async function refresh(refreshToken) {
|
|
|
34
34
|
throw new ApiError(resp.status, payload.error, resp.statusText);
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
* Authenticate anonymously a user to get an anonymous token.
|
|
38
|
+
*/
|
|
39
39
|
export async function getAnonymousToken(scope) {
|
|
40
40
|
const url = getTokenEndpoint();
|
|
41
41
|
url.pathname = '/api/media/v1/oauth/token';
|
|
@@ -58,6 +58,6 @@ function parseTokenResponse(data) {
|
|
|
58
58
|
accessToken: data.access_token,
|
|
59
59
|
refreshToken: data.refresh_token,
|
|
60
60
|
expiresIn: data.expires_in,
|
|
61
|
-
tokenType: data.token_type
|
|
61
|
+
tokenType: data.token_type
|
|
62
62
|
};
|
|
63
63
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { UserSession } from
|
|
1
|
+
import type { UserSession } from '../stores/user_session';
|
|
2
2
|
import type { Cookies } from '@sveltejs/kit';
|
|
3
|
-
import { type OmerloToken } from
|
|
3
|
+
import { type OmerloToken } from './token';
|
|
4
4
|
export declare function loadUserSession(f: typeof fetch, cookies: Cookies): Promise<UserSession>;
|
|
5
5
|
export declare function isAuthenticated(cookies: Cookies): boolean;
|
|
6
6
|
export declare function setAuthorizationCookies(cookies: Cookies, token: OmerloToken): void;
|
|
@@ -9,6 +9,6 @@ export declare function clearAuthorizationUsingHeader(headers: Headers): void;
|
|
|
9
9
|
export declare function getAccessTokenFromCookie(cookies: Cookies): string | null;
|
|
10
10
|
export declare function getRefreshTokenFromCookie(cookies: Cookies): string | null;
|
|
11
11
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
* Get the token used by the application.
|
|
13
|
+
*/
|
|
14
14
|
export declare function getApplicationToken(): Promise<string>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { useReader } from
|
|
2
|
-
import { getAnonymousToken, refresh } from
|
|
1
|
+
import { useReader } from '../..';
|
|
2
|
+
import { getAnonymousToken, refresh } from './token';
|
|
3
3
|
export async function loadUserSession(f, cookies) {
|
|
4
4
|
const userSession = { verified: false, authenticated: false, user: null };
|
|
5
5
|
if (isAuthenticated(cookies)) {
|
|
6
6
|
userSession.authenticated = true;
|
|
7
7
|
try {
|
|
8
|
-
const userInfo = await useReader(f)
|
|
8
|
+
const userInfo = await useReader(f)
|
|
9
|
+
.userInfo()
|
|
10
|
+
.then((resp) => resp.data);
|
|
9
11
|
userSession.verified = true;
|
|
10
12
|
userSession.user = userInfo;
|
|
11
13
|
}
|
|
@@ -22,7 +24,11 @@ const accessTokenCookieName = 'access_token';
|
|
|
22
24
|
const refreshTokenCookieName = 'refresh_token';
|
|
23
25
|
export function setAuthorizationCookies(cookies, token) {
|
|
24
26
|
cookies.set('logged_in', 'true', { path: '/', httpOnly: false });
|
|
25
|
-
cookies.set(accessTokenCookieName, token.accessToken, {
|
|
27
|
+
cookies.set(accessTokenCookieName, token.accessToken, {
|
|
28
|
+
httpOnly: true,
|
|
29
|
+
path: '/',
|
|
30
|
+
maxAge: token.expiresIn - 60
|
|
31
|
+
});
|
|
26
32
|
cookies.set(refreshTokenCookieName, token.refreshToken, { httpOnly: true, path: '/' });
|
|
27
33
|
}
|
|
28
34
|
export function clearAuthorizationCookies(cookies) {
|
|
@@ -46,11 +52,11 @@ const applicationToken = {
|
|
|
46
52
|
accessToken: '',
|
|
47
53
|
refreshToken: '',
|
|
48
54
|
expiredAt: 0,
|
|
49
|
-
init: false
|
|
55
|
+
init: false
|
|
50
56
|
};
|
|
51
57
|
/**
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
* Get the token used by the application.
|
|
59
|
+
*/
|
|
54
60
|
export async function getApplicationToken() {
|
|
55
61
|
if (!applicationToken.init) {
|
|
56
62
|
await newApplicationToken();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { UserInfo } from
|
|
2
|
-
import { type Readable } from
|
|
1
|
+
import type { UserInfo } from '../endpoints/accounts';
|
|
2
|
+
import { type Readable } from 'svelte/store';
|
|
3
3
|
export interface UserSession {
|
|
4
4
|
user: UserInfo | null;
|
|
5
5
|
verified: boolean;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { getContext, setContext } from
|
|
2
|
-
import { writable } from
|
|
3
|
-
import { browser } from
|
|
4
|
-
import { useReader } from
|
|
5
|
-
import { invalidate } from
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { writable } from 'svelte/store';
|
|
3
|
+
import { browser } from '$app/environment';
|
|
4
|
+
import { useReader } from '../..';
|
|
5
|
+
import { invalidate } from '$app/navigation';
|
|
6
6
|
const anonymousUserSession = {
|
|
7
7
|
user: null,
|
|
8
8
|
verified: false,
|
|
9
|
-
authenticated: false
|
|
9
|
+
authenticated: false
|
|
10
10
|
};
|
|
11
11
|
export function initUserSession(session) {
|
|
12
12
|
const { subscribe, update, set } = writable(session);
|
|
@@ -28,16 +28,18 @@ export function initUserSession(session) {
|
|
|
28
28
|
subscribe,
|
|
29
29
|
handleLogout: () => {
|
|
30
30
|
if (!browser) {
|
|
31
|
-
throw new Error(
|
|
31
|
+
throw new Error('MUST NOT call refresh on user session from server side.');
|
|
32
32
|
}
|
|
33
33
|
invalidate('omerlo:user_session');
|
|
34
34
|
update(updateUserInfo(null, false));
|
|
35
35
|
},
|
|
36
36
|
refresh: async () => {
|
|
37
37
|
if (!browser) {
|
|
38
|
-
throw new Error(
|
|
38
|
+
throw new Error('MUST NOT call refresh on user session from server side.');
|
|
39
39
|
}
|
|
40
|
-
const userInfo = await useReader(fetch)
|
|
40
|
+
const userInfo = await useReader(fetch)
|
|
41
|
+
.userInfo()
|
|
42
|
+
.then((resp) => resp.data);
|
|
41
43
|
update(updateUserInfo(userInfo, true));
|
|
42
44
|
}
|
|
43
45
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Assoc } from './assocs';
|
|
2
|
-
export type ApiAssocs = Record<string,
|
|
2
|
+
export type ApiAssocs = Record<string, Assoc>;
|
|
3
3
|
export declare function parseApiResponse<T>(response: Response, parser: (data: ApiData, assocs: ApiAssocs) => T): Promise<ApiResponse<T>>;
|
|
4
4
|
export declare function parseMany<T>(parser: (data: ApiData, assocs: ApiAssocs) => T): (response: ApiData[], assocs: ApiAssocs) => T[];
|
|
5
5
|
export interface ApiResponse<T> {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { parseAssocs } from './assocs';
|
|
1
|
+
import { initAssocs, parseAssocs } from './assocs';
|
|
2
2
|
export async function parseApiResponse(response, parser) {
|
|
3
3
|
const payload = await response.json();
|
|
4
4
|
let data = null, meta = null;
|
|
5
5
|
if (response.ok) {
|
|
6
|
-
|
|
6
|
+
const assocs = initAssocs(payload.assocs);
|
|
7
|
+
parseAssocs(assocs);
|
|
7
8
|
meta = payload.meta;
|
|
8
|
-
data = parser(payload.data,
|
|
9
|
+
data = parser(payload.data, assocs);
|
|
9
10
|
}
|
|
10
11
|
const errors = payload.errors || [];
|
|
11
12
|
return { ok: response.ok, status: response.status, parser, meta, data, errors };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ApiAssocs } from './api';
|
|
2
|
-
export declare function registerAssocParser(assocName: string, parser: (
|
|
1
|
+
import type { ApiAssocs, ApiData } from './api';
|
|
2
|
+
export declare function registerAssocParser(assocName: string, parser: (apiData: unknown, assocs: ApiAssocs) => unknown): void;
|
|
3
3
|
/**
|
|
4
4
|
* Return the assoc corresponding the given `id`
|
|
5
5
|
*/
|
|
@@ -8,8 +8,9 @@ export declare function getAssoc<T>(assocs: ApiAssocs, name: string, id: string)
|
|
|
8
8
|
* Return assocs corresponding given `ids`
|
|
9
9
|
*/
|
|
10
10
|
export declare function getAssocs<T>(assocs: ApiAssocs, name: string, ids: string[]): T[];
|
|
11
|
+
export declare function initAssocs(apiAssocs: ApiAssocs): Record<string, Record<string, Assoc>>;
|
|
11
12
|
/**
|
|
12
13
|
* Parse all assocs using an ordering system to prevent any clone.
|
|
13
14
|
*/
|
|
14
|
-
export declare function parseAssocs(apiAssocs:
|
|
15
|
-
export type Assoc =
|
|
15
|
+
export declare function parseAssocs(apiAssocs: Record<string, Record<string, Assoc>>): void;
|
|
16
|
+
export type Assoc = ApiData;
|
|
@@ -18,6 +18,25 @@ export function getAssoc(assocs, name, id) {
|
|
|
18
18
|
export function getAssocs(assocs, name, ids) {
|
|
19
19
|
return ids.map((id) => getAssoc(assocs, name, id));
|
|
20
20
|
}
|
|
21
|
+
export function initAssocs(apiAssocs) {
|
|
22
|
+
const result = {};
|
|
23
|
+
for (const assocName in apiAssocs) {
|
|
24
|
+
const assocs = apiAssocs[assocName];
|
|
25
|
+
result[assocName] = {};
|
|
26
|
+
// NOTE this is a workarround because we use publisher's api
|
|
27
|
+
// Once we remove publisher's api from reader, we should remove this condition.
|
|
28
|
+
if (!Array.isArray(assocs)) {
|
|
29
|
+
result[assocName] = assocs;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
for (const assoc of assocs) {
|
|
33
|
+
// FIXME we should implement a assoc_id handler because event's return
|
|
34
|
+
// the occurrence id but the assoc's id is the event_id.
|
|
35
|
+
result[assocName][assoc.id] = assoc;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
21
40
|
/**
|
|
22
41
|
* Parse all assocs using an ordering system to prevent any clone.
|
|
23
42
|
*/
|
|
@@ -26,6 +45,10 @@ export function parseAssocs(apiAssocs) {
|
|
|
26
45
|
const assocs = apiAssocs[assocName];
|
|
27
46
|
for (const assocId in assocs) {
|
|
28
47
|
const assoc = assocs[assocId];
|
|
48
|
+
if (!assocsParsers[assocName]) {
|
|
49
|
+
console.error(`No assoc parser found for ${assocName}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
29
52
|
assocs[assocId] = assocsParsers[assocName](assoc, apiAssocs);
|
|
30
53
|
}
|
|
31
54
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function buildMeta(locale) {
|
|
2
|
+
if (!locale) {
|
|
3
|
+
return {
|
|
4
|
+
locales: {
|
|
5
|
+
available: ['en'],
|
|
6
|
+
current: 'en'
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
locales: {
|
|
12
|
+
available: [locale],
|
|
13
|
+
current: locale
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function parseDate(input) {
|
|
18
|
+
if (!input)
|
|
19
|
+
return null;
|
|
20
|
+
const date = new Date(input);
|
|
21
|
+
return isNaN(date.getTime()) ? null : date;
|
|
22
|
+
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import type { ApiAssocs, ApiData, ApiResponse } from './api';
|
|
2
|
-
type FetchOptions<T> = {
|
|
3
|
-
parser
|
|
4
|
-
queryParams?: ApiData;
|
|
5
|
-
method?: 'get' | 'post' | 'put' | 'delete';
|
|
6
|
-
body?: ApiData;
|
|
7
|
-
headers?: Headers;
|
|
2
|
+
type FetchOptions<T> = Required<DirtyFetchOptions> & {
|
|
3
|
+
parser: (data: ApiData, assocs: ApiAssocs) => T;
|
|
8
4
|
};
|
|
9
|
-
export declare function request<T>(f: typeof fetch, path: string, opts: FetchOptions<T
|
|
5
|
+
export declare function request<T>(f: typeof fetch, path: string, opts: Partial<FetchOptions<T>>): Promise<ApiResponse<T>>;
|
|
10
6
|
type DirtyFetchOptions = {
|
|
11
7
|
queryParams?: ApiData;
|
|
12
|
-
method?: 'get' | 'post' | 'put' | 'delete';
|
|
8
|
+
method?: 'get' | 'post' | 'patch' | 'put' | 'delete';
|
|
13
9
|
body?: ApiData;
|
|
14
10
|
headers?: Headers;
|
|
15
11
|
};
|
|
16
12
|
export declare function dirtyRequest(f: typeof fetch, path: string, opts: DirtyFetchOptions): Promise<Response>;
|
|
13
|
+
/**
|
|
14
|
+
* NOTE: This is for OLD api and will be removed once every API will
|
|
15
|
+
* be developped in Reader.
|
|
16
|
+
*/
|
|
17
|
+
export declare function requestPublisher<T>(f: typeof fetch, path: string, opts: Partial<FetchOptions<T>>): Promise<ApiResponse<T>>;
|
|
18
|
+
export declare function dirtyRequestPublisher(f: typeof fetch, path: string, opts: DirtyFetchOptions): Promise<Response>;
|
|
17
19
|
export {};
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { parseApiResponse } from './api';
|
|
2
2
|
import { BROWSER } from 'esm-env';
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
opts.headers = new Headers();
|
|
3
|
+
function parseRequestOpts(params) {
|
|
4
|
+
const headers = params.headers ?? new Headers();
|
|
5
|
+
const method = params.method ?? 'get';
|
|
6
|
+
if (!headers.has('Accept')) {
|
|
7
|
+
headers.set('Accept', 'application/json');
|
|
8
|
+
}
|
|
9
|
+
if (!headers.has('Content-Type') && ['post', 'put', 'patch'].includes(method)) {
|
|
10
|
+
headers.set('Content-Type', 'application/json');
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
return {
|
|
13
|
+
queryParams: params.queryParams,
|
|
14
|
+
body: JSON.stringify(params.body),
|
|
15
|
+
method: params.method ?? 'get',
|
|
16
|
+
headers: headers,
|
|
17
|
+
parser: params.parser || ((data) => data)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export async function request(f, path, opts) {
|
|
21
|
+
const { body, headers, method, queryParams, parser } = parseRequestOpts(opts);
|
|
15
22
|
return dirtyRequest(f, path, { body, headers, method, queryParams }).then(async (resp) => {
|
|
16
23
|
return parseApiResponse(resp, parser);
|
|
17
24
|
});
|
|
@@ -34,3 +41,24 @@ export async function dirtyRequest(f, path, opts) {
|
|
|
34
41
|
}
|
|
35
42
|
return resp;
|
|
36
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* NOTE: This is for OLD api and will be removed once every API will
|
|
46
|
+
* be developped in Reader.
|
|
47
|
+
*/
|
|
48
|
+
export async function requestPublisher(f, path, opts) {
|
|
49
|
+
const { body, headers, method, queryParams, parser } = parseRequestOpts(opts);
|
|
50
|
+
return dirtyRequestPublisher(f, path, { body, headers, method, queryParams }).then(async (resp) => {
|
|
51
|
+
return parseApiResponse(resp, parser);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export async function dirtyRequestPublisher(f, path, opts) {
|
|
55
|
+
const queryParams = new URLSearchParams();
|
|
56
|
+
path = `/api/publisher/${path}`;
|
|
57
|
+
if (opts?.queryParams) {
|
|
58
|
+
Object.entries(opts.queryParams).forEach(([key, value]) => {
|
|
59
|
+
queryParams.append(key, String(value));
|
|
60
|
+
});
|
|
61
|
+
path = `${path}?${queryParams}`;
|
|
62
|
+
}
|
|
63
|
+
return f(path.toString(), opts);
|
|
64
|
+
}
|
package/eslint.config.js
CHANGED
|
@@ -16,7 +16,7 @@ export default ts.config(
|
|
|
16
16
|
languageOptions: {
|
|
17
17
|
globals: {
|
|
18
18
|
...globals.browser,
|
|
19
|
-
...globals.node
|
|
19
|
+
...globals.node
|
|
20
20
|
},
|
|
21
21
|
parserOptions: {
|
|
22
22
|
parser: ts.parser
|
|
@@ -28,7 +28,16 @@ export default ts.config(
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
rules: {
|
|
31
|
-
'@typescript-eslint/no-unused-vars': [
|
|
31
|
+
'@typescript-eslint/no-unused-vars': [
|
|
32
|
+
'warn',
|
|
33
|
+
{
|
|
34
|
+
argsIgnorePattern: '^_',
|
|
35
|
+
caughtErrorsIgnorePattern: '^_'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
32
38
|
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
ignores: ['.svelte-kit/**', 'build/**', 'dist/**', 'example/.svelte-kit']
|
|
33
42
|
}
|
|
34
43
|
);
|