Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const eslint = require("@eslint/js");
const tseslint = require("typescript-eslint");
const angular = require("angular-eslint");
const rdlabo = require("@rdlabo/eslint-plugin-rules");

module.exports = tseslint.config(
{
Expand All @@ -13,6 +14,15 @@ module.exports = tseslint.config(
...angular.configs.tsRecommended,
],
processor: angular.processInlineTemplates,
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
},
plugins: {
"@rdlabo/rules": rdlabo,
},
rules: {
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "off",
Expand All @@ -25,6 +35,16 @@ module.exports = tseslint.config(
"@angular-eslint/no-empty-lifecycle-method": "off",
"@angular-eslint/directive-selector": "off",
"@angular-eslint/component-selector": "off",
"@rdlabo/rules/restrict-try-block": [
"error",
{
allowPromise: false,
allowPromiseResolve: false,
allowRxjs: false,
allowInSignal: false,
maxLines: 3,
},
],
},
},
{
Expand Down
32 changes: 32 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"@ionic/storage-angular": "^4.0.0",
"@playwright/test": "^1.57.0",
"@rdlabo/capacitor-brotherprint": "^8.1.1",
"@rdlabo/eslint-plugin-rules": "^21.2.6",
"angular-eslint": "21.4.0",
"child_process": "^1.0.2",
"dom-to-image-more": "^3.10.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ describe('provideKitAppUpdate', () => {
});

function setup(result: boolean | Error | Promise<boolean>, isEnabled = true, isControlled = true) {
const checkForUpdate = vi.fn(() =>
result instanceof Promise ? result : result instanceof Error ? Promise.reject(result) : Promise.resolve(result),
);
const checkForUpdate = vi.fn(async () => {
if (result instanceof Error) throw result;
return result;
});
const reload = vi.fn();
TestBed.configureTestingModule({
providers: [
Expand Down
18 changes: 7 additions & 11 deletions projects/kit/app-update/src/lib/kit-app-update.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ export class KitAppUpdateService {
if (!this.#updates.isEnabled || !this.#document.defaultView?.navigator.serviceWorker?.controller) {
return;
}
try {
const available = await withTimeout(this.#updates.checkForUpdate(), UPDATE_CHECK_TIMEOUT_MS);
if (available) {
this.#document.location?.reload();
}
} catch (error) {
console.error('Angular service-worker update check failed', error);
}
const checkForUpdate = async (): Promise<boolean | undefined> => withTimeout(this.#updates.checkForUpdate(), UPDATE_CHECK_TIMEOUT_MS);
await checkForUpdate()
.then((available) => {
if (available) this.#document.location?.reload();
})
.catch((error: unknown) => console.error('Angular service-worker update check failed', error));
}
}

Expand All @@ -42,9 +40,7 @@ export class KitAppUpdateService {
* rolling out because code already running in older application versions cannot gain this behavior retroactively.
*/
export function provideKitAppUpdate(): EnvironmentProviders {
return makeEnvironmentProviders([
provideAppInitializer(() => inject(KitAppUpdateService).initialize()),
]);
return makeEnvironmentProviders([provideAppInitializer(() => inject(KitAppUpdateService).initialize())]);
}

function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | undefined> {
Expand Down
79 changes: 38 additions & 41 deletions projects/kit/auth-firebase/social/src/kit-social.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ const classifyOAuthError = (e: unknown): KitOAuthErrorCategory => {
return 'other';
};

type Settled<T> = { status: 'fulfilled'; value: T } | { status: 'rejected'; reason: unknown };

/** Convert a possibly synchronously-throwing Promise producer into an explicit result. */
const settle = <T>(operation: () => Promise<T>): Promise<Settled<T>> => {
const execute = async (): Promise<T> => operation();
return execute().then(
(value) => ({ status: 'fulfilled', value }),
(reason: unknown) => ({ status: 'rejected', reason }),
);
};

/**
* The shared 3-mode credential state machine (internal).
*
Expand All @@ -109,7 +120,7 @@ const applyOAuthCredential = async (
error: (category: KitOAuthErrorCategory, error: unknown) => void | Promise<unknown>;
},
): Promise<boolean> => {
try {
const result = await settle(async () => {
if (mode.mode === 'new') {
await signInWithCredential(auth, credential);
} else {
Expand All @@ -124,8 +135,9 @@ const applyOAuthCredential = async (
await linkWithCredential(user, EmailAuthProvider.credential(mode.emailLogin.email, mode.emailLogin.password));
}
}
} catch (e) {
await effects.error(classifyOAuthError(e), e);
});
if (result.status === 'rejected') {
await effects.error(classifyOAuthError(result.reason), result.reason);
return false;
}
await effects.success();
Expand Down Expand Up @@ -164,27 +176,17 @@ const isFacebookCancellation = (error: unknown): boolean => {
* hooks).
*/
export const kitFacebookLogin = async (auth: Auth, options: KitFacebookLoginOptions): Promise<{ status: boolean }> => {
try {
const execute = async (): Promise<{ status: boolean }> => {
await options.before?.();
const nonce = generateNonce();
let pluginFailed = false;
let pluginError: unknown;
const event = await FacebookLogin.login({ permissions: options.permissions, nonce }).catch((error: unknown) => {
pluginFailed = true;
pluginError = error;
return undefined;
});
const login = await settle(() => FacebookLogin.login({ permissions: options.permissions, nonce }));
await nextFrame();
if (pluginFailed) {
if (isFacebookCancellation(pluginError)) {
return { status: false };
}
await options.error?.('other', pluginError);
return { status: false };
}
if (!event || !event.accessToken?.token) {
if (login.status === 'rejected') {
if (!isFacebookCancellation(login.reason)) await options.error?.('other', login.reason);
return { status: false };
}
const event = login.value;
if (!event?.accessToken?.token) return { status: false };
const accessToken = event.accessToken.token;
const credential: AuthCredential =
Capacitor.isNativePlatform() && Capacitor.getPlatform() === 'ios'
Expand All @@ -196,9 +198,8 @@ export const kitFacebookLogin = async (auth: Auth, options: KitFacebookLoginOpti
error: (category, error) => options.error?.(category, error),
});
return { status };
} finally {
await options.finally?.();
}
};
return execute().finally(() => options.finally?.());
};

/**
Expand Down Expand Up @@ -231,13 +232,11 @@ export const kitFacebookLogout = async (): Promise<void> => {
* Every failure path (including popup errors) is routed through `onError`.
*/
export const kitAppleLogin = async (auth: Auth, options: KitAppleLoginOptions): Promise<{ status: boolean }> => {
try {
const execute = async (): Promise<{ status: boolean }> => {
await options.before?.();
if (Capacitor.isNativePlatform()) {
const authorize = await SignInWithApple.authorize().catch(() => undefined);
if (!authorize) {
return { status: false };
}
if (!authorize) return { status: false };
const r = authorize.response;
const response: KitAppleResponse = {
user: r.user ?? null,
Expand All @@ -261,28 +260,27 @@ export const kitAppleLogin = async (auth: Auth, options: KitAppleLoginOptions):
provider.addScope('name');

if (options.mode === 'credential') {
const user = auth.currentUser;
try {
if (!user) {
throw new Error('kit social: no signed-in user to re-authenticate');
}
const operation = await settle(async () => {
const user = requireUser(auth);
await reauthenticateWithPopup(user, provider);
await linkWithCredential(user, EmailAuthProvider.credential(options.emailLogin.email, options.emailLogin.password));
} catch (e) {
await options.error?.(classifyOAuthError(e), e);
});
if (operation.status === 'rejected') {
await options.error?.(classifyOAuthError(operation.reason), operation.reason);
return { status: false };
}
await options.success?.({ response: emptyAppleResponse(), mode: 'credential' });
return { status: true };
}

let result;
try {
result = options.mode === 'new' ? await signInWithPopup(auth, provider) : await linkWithPopup(requireUser(auth), provider);
} catch (e) {
await options.error?.(classifyOAuthError(e), e);
const popup = await settle(() =>
options.mode === 'new' ? signInWithPopup(auth, provider) : linkWithPopup(requireUser(auth), provider),
);
if (popup.status === 'rejected') {
await options.error?.(classifyOAuthError(popup.reason), popup.reason);
return { status: false };
}
const result = popup.value;
const credential = OAuthProvider.credentialFromResult(result);
const response: KitAppleResponse = {
...emptyAppleResponse(),
Expand All @@ -292,9 +290,8 @@ export const kitAppleLogin = async (auth: Auth, options: KitAppleLoginOptions):
};
await options.success?.({ response, mode: options.mode });
return { status: true };
} finally {
await options.finally?.();
}
};
return execute().finally(() => options.finally?.());
};

const emptyAppleResponse = (): KitAppleResponse => ({
Expand Down
Loading