local-lite 0.0.12 → 0.0.15
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 +5 -3
- package/dist/access/client.d.ts +9 -0
- package/dist/access/client.js +56 -0
- package/dist/access/engine.d.ts +2 -0
- package/dist/access/engine.js +46 -0
- package/dist/access/errors.d.ts +14 -0
- package/dist/access/errors.js +23 -0
- package/dist/access/guard.d.ts +24 -0
- package/dist/access/guard.js +104 -0
- package/dist/access/index.d.ts +5 -0
- package/dist/access/index.js +21 -0
- package/dist/access/react/index.d.ts +32 -0
- package/dist/access/react/index.js +42 -0
- package/dist/access/types.d.ts +26 -0
- package/dist/access/types.js +2 -0
- package/dist/auth/apple.d.ts +91 -0
- package/dist/auth/apple.js +264 -0
- package/dist/auth/github.d.ts +72 -0
- package/dist/auth/github.js +284 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.js +3 -0
- package/dist/auth/microsoft.d.ts +76 -0
- package/dist/auth/microsoft.js +310 -0
- package/dist/backups/encryption.d.ts +18 -0
- package/dist/backups/encryption.js +122 -0
- package/dist/backups/index.d.ts +2 -0
- package/dist/backups/index.js +11 -1
- package/dist/backups/onedrive.d.ts +22 -0
- package/dist/backups/onedrive.js +114 -0
- package/dist/core/client.d.ts +33 -0
- package/dist/core/client.js +46 -0
- package/dist/core/conflict.d.ts +8 -1
- package/dist/core/conflict.js +75 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/maintenance.d.ts +66 -0
- package/dist/core/maintenance.js +157 -0
- package/dist/core/sync-worker.js +6 -3
- package/dist/core/types.d.ts +34 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/react/auth.d.ts +35 -0
- package/dist/react/auth.js +272 -0
- package/dist/react/index.d.ts +12 -0
- package/dist/react/index.js +51 -0
- package/dist/utils/portability.d.ts +73 -0
- package/dist/utils/portability.js +276 -0
- package/package.json +21 -1
package/README.md
CHANGED
|
@@ -19,9 +19,11 @@ Built around the **Transactional Outbox Pattern**, `local-lite` ensures instant,
|
|
|
19
19
|
* **[Configuration Reference](https://georgechitechi.github.io/local-lite/#/configuration)** - Options for `defineSyncConfig()`, retries, and loggers
|
|
20
20
|
* **[Local SQLite Adapters](https://georgechitechi.github.io/local-lite/#/adapters-local)** - Expo SQLite, OP-SQLite, better-sqlite3, node:sqlite, and LibSQL
|
|
21
21
|
* **[Remote Cloud Adapters](https://georgechitechi.github.io/local-lite/#/adapters-remote)** - Supabase, Convex, Cloud Firestore, and custom adapters
|
|
22
|
-
* **[
|
|
23
|
-
* **[
|
|
24
|
-
* **[Conflict Resolution](https://georgechitechi.github.io/local-lite/#/conflict-resolution)** - LWW, Client-Wins, Server-Wins
|
|
22
|
+
* **[Authentication (Google, Microsoft, GitHub, Apple)](https://georgechitechi.github.io/local-lite/#/auth)** - Direct PKCE & native Apple authentication clients & hooks
|
|
23
|
+
* **[Database Backups & Encryption](https://georgechitechi.github.io/local-lite/#/backups)** - Google Drive, OneDrive, and Zero-Knowledge AES-256-GCM snapshots
|
|
24
|
+
* **[Conflict Resolution & Field Merge](https://georgechitechi.github.io/local-lite/#/conflict-resolution)** - 3-Way field merging, LWW, Client-Wins, and Server-Wins
|
|
25
|
+
* **[Maintenance & Data Portability](https://georgechitechi.github.io/local-lite/#/maintenance-portability)** - VACUUM, WAL checkpoints, outbox pruning, and JSON/CSV export/import
|
|
26
|
+
* **[React & Expo Hooks](https://georgechitechi.github.io/local-lite/#/react-hooks)** - `useLiveQuery()`, `useSyncStatus()`, and auth hooks
|
|
25
27
|
|
|
26
28
|
---
|
|
27
29
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AccessGuard } from './guard.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a LocalFirstClient with an AccessGuard.
|
|
4
|
+
* Intercepts CRUD queries and mutations, asserting permissions before writes and
|
|
5
|
+
* filtering unauthorized rows or restricted fields on reads.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withAccess<T extends {
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}>(client: T, guard: AccessGuard): T;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withAccess = withAccess;
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a LocalFirstClient with an AccessGuard.
|
|
6
|
+
* Intercepts CRUD queries and mutations, asserting permissions before writes and
|
|
7
|
+
* filtering unauthorized rows or restricted fields on reads.
|
|
8
|
+
*/
|
|
9
|
+
function withAccess(client, guard) {
|
|
10
|
+
return new Proxy(client, {
|
|
11
|
+
get(target, prop, receiver) {
|
|
12
|
+
if (prop === 'insert') {
|
|
13
|
+
return async (tableName, rowIdOrPayload, payloadOrOptions, options) => {
|
|
14
|
+
const payload = typeof rowIdOrPayload === 'string'
|
|
15
|
+
? payloadOrOptions || {}
|
|
16
|
+
: rowIdOrPayload || {};
|
|
17
|
+
guard.assertCan('create', tableName, payload);
|
|
18
|
+
return target.insert(tableName, rowIdOrPayload, payloadOrOptions, options);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (prop === 'update') {
|
|
22
|
+
return async (tableName, rowId, payload, options) => {
|
|
23
|
+
guard.assertCan('update', tableName, { id: rowId, ...payload });
|
|
24
|
+
return target.update(tableName, rowId, payload, options);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (prop === 'delete') {
|
|
28
|
+
return async (tableName, rowId, options) => {
|
|
29
|
+
guard.assertCan('delete', tableName, { id: rowId });
|
|
30
|
+
return target.delete(tableName, rowId, options);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (prop === 'findById') {
|
|
34
|
+
return async (tableName, id, options) => {
|
|
35
|
+
const record = await target.findById(tableName, id, options);
|
|
36
|
+
if (!record)
|
|
37
|
+
return null;
|
|
38
|
+
if (!guard.can('read', tableName, record)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return guard.filterAllowedFields('read', tableName, record, record);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (prop === 'findMany') {
|
|
45
|
+
return async (tableName, options) => {
|
|
46
|
+
const records = await target.findMany(tableName, options);
|
|
47
|
+
return guard.filterRows('read', tableName, records);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (prop === 'accessGuard') {
|
|
51
|
+
return guard;
|
|
52
|
+
}
|
|
53
|
+
return Reflect.get(target, prop, receiver);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineAccessRules = defineAccessRules;
|
|
4
|
+
const guard_js_1 = require("./guard.js");
|
|
5
|
+
class RuleBuilderImpl {
|
|
6
|
+
rules = [];
|
|
7
|
+
addRule(inverted, actions, subjects, arg3, arg4) {
|
|
8
|
+
const actionList = Array.isArray(actions) ? actions : [actions];
|
|
9
|
+
const subjectList = Array.isArray(subjects) ? subjects : [subjects];
|
|
10
|
+
let options;
|
|
11
|
+
let predicate;
|
|
12
|
+
if (typeof arg3 === 'function') {
|
|
13
|
+
predicate = arg3;
|
|
14
|
+
}
|
|
15
|
+
else if (arg3 && typeof arg3 === 'object') {
|
|
16
|
+
options = arg3;
|
|
17
|
+
predicate = arg4;
|
|
18
|
+
}
|
|
19
|
+
this.rules.push({
|
|
20
|
+
inverted,
|
|
21
|
+
actions: actionList,
|
|
22
|
+
subjects: subjectList,
|
|
23
|
+
predicate,
|
|
24
|
+
fields: options?.fields,
|
|
25
|
+
description: options?.description,
|
|
26
|
+
});
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
can(action, subject, arg3, arg4) {
|
|
30
|
+
return this.addRule(false, action, subject, arg3, arg4);
|
|
31
|
+
}
|
|
32
|
+
cannot(action, subject, arg3, arg4) {
|
|
33
|
+
return this.addRule(true, action, subject, arg3, arg4);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function defineAccessRules(policy) {
|
|
37
|
+
const createGuard = (user) => {
|
|
38
|
+
const builder = new RuleBuilderImpl();
|
|
39
|
+
policy(builder, user);
|
|
40
|
+
return new guard_js_1.AccessGuard(user, builder.rules);
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
createGuard,
|
|
44
|
+
for: createGuard,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class AccessDeniedError extends Error {
|
|
2
|
+
readonly action: string;
|
|
3
|
+
readonly subject: string;
|
|
4
|
+
readonly fields?: string[];
|
|
5
|
+
readonly record?: any;
|
|
6
|
+
readonly reason?: string;
|
|
7
|
+
constructor(options: {
|
|
8
|
+
action: string;
|
|
9
|
+
subject: string;
|
|
10
|
+
fields?: string[];
|
|
11
|
+
record?: any;
|
|
12
|
+
reason?: string;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessDeniedError = void 0;
|
|
4
|
+
class AccessDeniedError extends Error {
|
|
5
|
+
action;
|
|
6
|
+
subject;
|
|
7
|
+
fields;
|
|
8
|
+
record;
|
|
9
|
+
reason;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
const fieldsText = options.fields && options.fields.length > 0 ? ` (fields: ${options.fields.join(', ')})` : '';
|
|
12
|
+
const message = options.reason ||
|
|
13
|
+
`Access denied: cannot perform action '${options.action}' on subject '${options.subject}'${fieldsText}.`;
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'AccessDeniedError';
|
|
16
|
+
this.action = options.action;
|
|
17
|
+
this.subject = options.subject;
|
|
18
|
+
this.fields = options.fields;
|
|
19
|
+
this.record = options.record;
|
|
20
|
+
this.reason = options.reason;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.AccessDeniedError = AccessDeniedError;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AccessAction, AccessRule, AccessSubject } from './types.js';
|
|
2
|
+
export declare class AccessGuard<TUser = any> {
|
|
3
|
+
readonly user: TUser | null;
|
|
4
|
+
readonly rules: AccessRule<TUser>[];
|
|
5
|
+
constructor(user: TUser | null, rules: AccessRule<TUser>[]);
|
|
6
|
+
/**
|
|
7
|
+
* Evaluates if the current user has permission to perform an action on a subject.
|
|
8
|
+
*/
|
|
9
|
+
can(action: AccessAction, subject: AccessSubject, record?: any): boolean;
|
|
10
|
+
cannot(action: AccessAction, subject: AccessSubject, record?: any): boolean;
|
|
11
|
+
assertCan(action: AccessAction, subject: AccessSubject, record?: any): void;
|
|
12
|
+
/**
|
|
13
|
+
* Check if a specific field is accessible for an action.
|
|
14
|
+
*/
|
|
15
|
+
canAccessField(action: AccessAction, subject: AccessSubject, field: string, record?: any): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Strips restricted fields from an object based on rules.
|
|
18
|
+
*/
|
|
19
|
+
filterAllowedFields<T extends Record<string, any>>(action: AccessAction, subject: AccessSubject, data: T, record?: any): Partial<T>;
|
|
20
|
+
/**
|
|
21
|
+
* Filters an array of rows, removing unauthorized records and stripping forbidden fields.
|
|
22
|
+
*/
|
|
23
|
+
filterRows<T extends Record<string, any>>(action: AccessAction, subject: AccessSubject, rows: T[]): T[];
|
|
24
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessGuard = void 0;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
class AccessGuard {
|
|
6
|
+
user;
|
|
7
|
+
rules;
|
|
8
|
+
constructor(user, rules) {
|
|
9
|
+
this.user = user;
|
|
10
|
+
this.rules = rules;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Evaluates if the current user has permission to perform an action on a subject.
|
|
14
|
+
*/
|
|
15
|
+
can(action, subject, record) {
|
|
16
|
+
let allowed = false;
|
|
17
|
+
for (const rule of this.rules) {
|
|
18
|
+
const actionMatches = rule.actions.includes('manage') ||
|
|
19
|
+
rule.actions.includes('all') ||
|
|
20
|
+
rule.actions.includes(action);
|
|
21
|
+
const subjectMatches = rule.subjects.includes('all') ||
|
|
22
|
+
rule.subjects.includes(subject);
|
|
23
|
+
if (!actionMatches || !subjectMatches) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (rule.predicate) {
|
|
27
|
+
try {
|
|
28
|
+
if (!rule.predicate(record, this.user)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// If cannot rule matches (with no field filter), reject immediately
|
|
37
|
+
if (rule.inverted && (!rule.fields || rule.fields.length === 0)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// If can rule matches, mark allowed
|
|
41
|
+
if (!rule.inverted) {
|
|
42
|
+
allowed = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return allowed;
|
|
46
|
+
}
|
|
47
|
+
cannot(action, subject, record) {
|
|
48
|
+
return !this.can(action, subject, record);
|
|
49
|
+
}
|
|
50
|
+
assertCan(action, subject, record) {
|
|
51
|
+
if (!this.can(action, subject, record)) {
|
|
52
|
+
throw new errors_js_1.AccessDeniedError({ action, subject, record });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a specific field is accessible for an action.
|
|
57
|
+
*/
|
|
58
|
+
canAccessField(action, subject, field, record) {
|
|
59
|
+
if (!this.can(action, subject, record)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
for (const rule of this.rules) {
|
|
63
|
+
if (!rule.inverted || !rule.fields || !rule.fields.includes(field)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const actionMatches = rule.actions.includes('manage') ||
|
|
67
|
+
rule.actions.includes('all') ||
|
|
68
|
+
rule.actions.includes(action);
|
|
69
|
+
const subjectMatches = rule.subjects.includes('all') ||
|
|
70
|
+
rule.subjects.includes(subject);
|
|
71
|
+
if (actionMatches && subjectMatches) {
|
|
72
|
+
if (!rule.predicate || rule.predicate(record, this.user)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Strips restricted fields from an object based on rules.
|
|
81
|
+
*/
|
|
82
|
+
filterAllowedFields(action, subject, data, record) {
|
|
83
|
+
if (!data || typeof data !== 'object')
|
|
84
|
+
return data;
|
|
85
|
+
const result = {};
|
|
86
|
+
for (const [key, value] of Object.entries(data)) {
|
|
87
|
+
if (this.canAccessField(action, subject, key, record || data)) {
|
|
88
|
+
result[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Filters an array of rows, removing unauthorized records and stripping forbidden fields.
|
|
95
|
+
*/
|
|
96
|
+
filterRows(action, subject, rows) {
|
|
97
|
+
if (!Array.isArray(rows))
|
|
98
|
+
return [];
|
|
99
|
+
return rows
|
|
100
|
+
.filter((row) => this.can(action, subject, row))
|
|
101
|
+
.map((row) => this.filterAllowedFields(action, subject, row, row));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.AccessGuard = AccessGuard;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types.js"), exports);
|
|
18
|
+
__exportStar(require("./errors.js"), exports);
|
|
19
|
+
__exportStar(require("./guard.js"), exports);
|
|
20
|
+
__exportStar(require("./engine.js"), exports);
|
|
21
|
+
__exportStar(require("./client.js"), exports);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { AccessGuard } from '../guard.js';
|
|
3
|
+
import { AccessAction, AccessSubject } from '../types.js';
|
|
4
|
+
export interface AccessContextValue<TUser = any> {
|
|
5
|
+
guard: AccessGuard<TUser> | null;
|
|
6
|
+
user: TUser | null;
|
|
7
|
+
can: (action: AccessAction, subject: AccessSubject, record?: any) => boolean;
|
|
8
|
+
cannot: (action: AccessAction, subject: AccessSubject, record?: any) => boolean;
|
|
9
|
+
assertCan: (action: AccessAction, subject: AccessSubject, record?: any) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface AccessProviderProps<TUser = any> {
|
|
12
|
+
guard: AccessGuard<TUser>;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export declare function AccessProvider<TUser = any>({ guard, children, }: AccessProviderProps<TUser>): React.JSX.Element;
|
|
16
|
+
export declare function useAccess<TUser = any>(): AccessContextValue<TUser>;
|
|
17
|
+
export interface CanProps {
|
|
18
|
+
I: AccessAction;
|
|
19
|
+
a: AccessSubject;
|
|
20
|
+
this?: any;
|
|
21
|
+
fallback?: ReactNode;
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export declare function Can({ I, a, this: record, fallback, children }: CanProps): React.JSX.Element;
|
|
25
|
+
export interface CannotProps {
|
|
26
|
+
I: AccessAction;
|
|
27
|
+
a: AccessSubject;
|
|
28
|
+
this?: any;
|
|
29
|
+
fallback?: ReactNode;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export declare function Cannot({ I, a, this: record, fallback, children }: CannotProps): React.JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessProvider = AccessProvider;
|
|
4
|
+
exports.useAccess = useAccess;
|
|
5
|
+
exports.Can = Can;
|
|
6
|
+
exports.Cannot = Cannot;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const AccessContext = (0, react_1.createContext)(null);
|
|
10
|
+
function AccessProvider({ guard, children, }) {
|
|
11
|
+
const value = (0, react_1.useMemo)(() => {
|
|
12
|
+
return {
|
|
13
|
+
guard,
|
|
14
|
+
user: guard.user,
|
|
15
|
+
can: (action, subject, record) => guard.can(action, subject, record),
|
|
16
|
+
cannot: (action, subject, record) => guard.cannot(action, subject, record),
|
|
17
|
+
assertCan: (action, subject, record) => guard.assertCan(action, subject, record),
|
|
18
|
+
};
|
|
19
|
+
}, [guard]);
|
|
20
|
+
return (0, jsx_runtime_1.jsx)(AccessContext.Provider, { value: value, children: children });
|
|
21
|
+
}
|
|
22
|
+
function useAccess() {
|
|
23
|
+
const context = (0, react_1.useContext)(AccessContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error('useAccess must be used within an <AccessProvider>');
|
|
26
|
+
}
|
|
27
|
+
return context;
|
|
28
|
+
}
|
|
29
|
+
function Can({ I, a, this: record, fallback = null, children }) {
|
|
30
|
+
const { can } = useAccess();
|
|
31
|
+
if (can(I, a, record)) {
|
|
32
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
33
|
+
}
|
|
34
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: fallback });
|
|
35
|
+
}
|
|
36
|
+
function Cannot({ I, a, this: record, fallback = null, children }) {
|
|
37
|
+
const { cannot } = useAccess();
|
|
38
|
+
if (cannot(I, a, record)) {
|
|
39
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
40
|
+
}
|
|
41
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: fallback });
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type AccessAction = 'create' | 'read' | 'update' | 'delete' | 'manage' | (string & {});
|
|
2
|
+
export type AccessSubject = 'all' | (string & {});
|
|
3
|
+
export type AccessPredicate<TRecord = any, TUser = any> = (record?: TRecord, user?: TUser | null) => boolean;
|
|
4
|
+
export interface RuleOptions {
|
|
5
|
+
fields?: string[];
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AccessRule<TUser = any> {
|
|
9
|
+
inverted: boolean;
|
|
10
|
+
actions: string[];
|
|
11
|
+
subjects: string[];
|
|
12
|
+
predicate?: AccessPredicate<any, TUser>;
|
|
13
|
+
fields?: string[];
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AccessRuleBuilder<TUser = any> {
|
|
17
|
+
can(action: AccessAction | AccessAction[], subject: AccessSubject | AccessSubject[], predicate?: AccessPredicate<any, TUser>): this;
|
|
18
|
+
can(action: AccessAction | AccessAction[], subject: AccessSubject | AccessSubject[], options: RuleOptions, predicate?: AccessPredicate<any, TUser>): this;
|
|
19
|
+
cannot(action: AccessAction | AccessAction[], subject: AccessSubject | AccessSubject[], predicate?: AccessPredicate<any, TUser>): this;
|
|
20
|
+
cannot(action: AccessAction | AccessAction[], subject: AccessSubject | AccessSubject[], options: RuleOptions, predicate?: AccessPredicate<any, TUser>): this;
|
|
21
|
+
}
|
|
22
|
+
export type AccessPolicyDefinition<TUser = any> = (builder: AccessRuleBuilder<TUser>, user: TUser | null) => void;
|
|
23
|
+
export interface AccessEngine<TUser = any> {
|
|
24
|
+
createGuard(user: TUser | null): any;
|
|
25
|
+
for(user: TUser | null): any;
|
|
26
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const APPLE_AUTHORIZATION_ENDPOINT = "https://appleid.apple.com/auth/authorize";
|
|
2
|
+
export declare const APPLE_TOKEN_ENDPOINT = "https://appleid.apple.com/auth/token";
|
|
3
|
+
export declare const DEFAULT_APPLE_AUTH_SCOPES: string[];
|
|
4
|
+
export interface AppleUser {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
name: string;
|
|
8
|
+
givenName?: string;
|
|
9
|
+
familyName?: string;
|
|
10
|
+
identityToken?: string;
|
|
11
|
+
authorizationCode?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AppleAuthTokens {
|
|
14
|
+
accessToken?: string;
|
|
15
|
+
refreshToken?: string;
|
|
16
|
+
idToken: string;
|
|
17
|
+
expiresAt?: number;
|
|
18
|
+
tokenType?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface AppleAuthTokenStore {
|
|
21
|
+
getTokens(): Promise<AppleAuthTokens | null>;
|
|
22
|
+
setTokens(tokens: AppleAuthTokens): Promise<void>;
|
|
23
|
+
removeTokens(): Promise<void>;
|
|
24
|
+
getUser(): Promise<AppleUser | null>;
|
|
25
|
+
setUser(user: AppleUser | null): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export interface AppleAuthKeyValueStorage {
|
|
28
|
+
getItem(key: string): Promise<unknown>;
|
|
29
|
+
setItem(key: string, value: unknown): Promise<void>;
|
|
30
|
+
removeItem(key: string): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export interface AppleAuthorizationRequest {
|
|
33
|
+
authorizationUrl: string;
|
|
34
|
+
redirectUri: string;
|
|
35
|
+
state: string;
|
|
36
|
+
nonce: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AppleAuthorizationResponse {
|
|
39
|
+
code?: string;
|
|
40
|
+
id_token?: string;
|
|
41
|
+
state?: string;
|
|
42
|
+
user?: {
|
|
43
|
+
name?: {
|
|
44
|
+
firstName?: string;
|
|
45
|
+
lastName?: string;
|
|
46
|
+
};
|
|
47
|
+
email?: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface AppleNativeCredential {
|
|
51
|
+
user: string;
|
|
52
|
+
email?: string | null;
|
|
53
|
+
fullName?: {
|
|
54
|
+
givenName?: string | null;
|
|
55
|
+
familyName?: string | null;
|
|
56
|
+
} | null;
|
|
57
|
+
identityToken?: string | null;
|
|
58
|
+
authorizationCode?: string | null;
|
|
59
|
+
}
|
|
60
|
+
export interface AppleAuthClientOptions {
|
|
61
|
+
clientId: string;
|
|
62
|
+
redirectUri?: string;
|
|
63
|
+
scopes?: ('name' | 'email' | string)[];
|
|
64
|
+
authorize?: (request: AppleAuthorizationRequest) => Promise<AppleAuthorizationResponse | null>;
|
|
65
|
+
tokenStore?: AppleAuthTokenStore;
|
|
66
|
+
storage?: AppleAuthKeyValueStorage;
|
|
67
|
+
randomBytes?: (length: number) => Uint8Array;
|
|
68
|
+
fetch?: typeof fetch;
|
|
69
|
+
}
|
|
70
|
+
export declare function createInMemoryAppleTokenStore(): AppleAuthTokenStore;
|
|
71
|
+
export declare function createAppleAuthTokenStore(storage: AppleAuthKeyValueStorage, prefix?: string): AppleAuthTokenStore;
|
|
72
|
+
export declare class AppleAuthClient {
|
|
73
|
+
private readonly options;
|
|
74
|
+
private readonly tokenStore;
|
|
75
|
+
private readonly listeners;
|
|
76
|
+
private currentUser;
|
|
77
|
+
constructor(options: AppleAuthClientOptions);
|
|
78
|
+
onAuthStateChange(listener: (user: AppleUser | null) => void): () => void;
|
|
79
|
+
private notify;
|
|
80
|
+
/**
|
|
81
|
+
* Signs in with Apple.
|
|
82
|
+
* If a native credential object (from expo-apple-authentication or native ASAuthorizationAppleIDCredential)
|
|
83
|
+
* is passed, processes and verifies it directly.
|
|
84
|
+
* Otherwise, launches the PKCE web/browser authorization flow.
|
|
85
|
+
*/
|
|
86
|
+
signIn(nativeCredential?: AppleNativeCredential): Promise<AppleUser | null>;
|
|
87
|
+
private processNativeCredential;
|
|
88
|
+
signOut(): Promise<void>;
|
|
89
|
+
isSignedIn(): Promise<boolean>;
|
|
90
|
+
getUser(): Promise<AppleUser | null>;
|
|
91
|
+
}
|