@things-factory/auth-ui 10.1.258 → 10.1.260
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/dist-client/pages/appliance/home.d.ts +0 -1
- package/dist-client/pages/appliance/home.js +5 -6
- package/dist-client/pages/appliance/home.js.map +1 -1
- package/dist-client/pages/application/applications.d.ts +0 -1
- package/dist-client/pages/application/applications.js +5 -6
- package/dist-client/pages/application/applications.js.map +1 -1
- package/dist-client/pages/audit/audit-page.d.ts +15 -14
- package/dist-client/pages/audit/audit-page.js +107 -139
- package/dist-client/pages/audit/audit-page.js.map +1 -1
- package/dist-client/pages/auth-provider/auth-provider-management.d.ts +9 -4
- package/dist-client/pages/auth-provider/auth-provider-management.js +29 -13
- package/dist-client/pages/auth-provider/auth-provider-management.js.map +1 -1
- package/dist-client/pages/domain-owner/domain-owner-management.js +2 -2
- package/dist-client/pages/domain-owner/domain-owner-management.js.map +1 -1
- package/dist-client/pages/list-address.test.d.ts +1 -0
- package/dist-client/pages/list-address.test.js +51 -0
- package/dist-client/pages/list-address.test.js.map +1 -0
- package/dist-client/pages/role/role-inheritance-shown.test.js +7 -6
- package/dist-client/pages/role/role-inheritance-shown.test.js.map +1 -1
- package/dist-client/pages/role/role-list-page.d.ts +20 -27
- package/dist-client/pages/role/role-list-page.js +49 -136
- package/dist-client/pages/role/role-list-page.js.map +1 -1
- package/dist-client/pages/user/user-list-page.d.ts +0 -1
- package/dist-client/pages/user/user-list-page.js +5 -6
- package/dist-client/pages/user/user-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/audit-words.d.ts +28 -0
- package/dist-client/utils/audit-words.js +36 -0
- package/dist-client/utils/audit-words.js.map +1 -1
- package/dist-client/utils/audit-words.test.js +33 -1
- package/dist-client/utils/audit-words.test.js.map +1 -1
- package/dist-client/utils/role-scope.d.ts +15 -0
- package/dist-client/utils/role-scope.js +13 -0
- package/dist-client/utils/role-scope.js.map +1 -0
- package/dist-client/utils/role-scope.test.d.ts +1 -0
- package/dist-client/utils/role-scope.test.js +18 -0
- package/dist-client/utils/role-scope.test.js.map +1 -0
- package/package.json +8 -7
- package/translations/en.json +1 -1
- package/translations/es.json +1 -1
- package/translations/ja.json +1 -1
- package/translations/ko.json +1 -1
- package/translations/ms.json +1 -1
- package/translations/zh.json +1 -1
|
@@ -27,4 +27,32 @@ export declare function lastDays(days: number, now?: Date): {
|
|
|
27
27
|
from: Date;
|
|
28
28
|
to: Date;
|
|
29
29
|
};
|
|
30
|
+
/** A grid filter as the audit screen's list holds it (data-grist `FilterValue`). */
|
|
31
|
+
type GridFilter = {
|
|
32
|
+
name: string;
|
|
33
|
+
operator: string;
|
|
34
|
+
value: any;
|
|
35
|
+
};
|
|
36
|
+
export type AuditOutcome = '' | 'denied' | 'error' | 'failed';
|
|
37
|
+
/** The day window the audit screen opens on, and reads when no window or date is chosen. */
|
|
38
|
+
export declare const AUDIT_DEFAULT_DAYS = 30;
|
|
39
|
+
/**
|
|
40
|
+
* What the audit screen reads, from its grid's filters (FOOTER-BUTTON-REVIEW G09 - the conditions are the grid's, kept
|
|
41
|
+
* in the address): the period, the person and the privilege narrowed to, and the outcome.
|
|
42
|
+
*
|
|
43
|
+
* The period is the dates given (`period`, between), else the day window (`days`), else the last 30 days. Dates win over
|
|
44
|
+
* the window, as picking a date used to drop the window. A range given only one bound runs from the window's start
|
|
45
|
+
* before its end, or up to now. The person is `{ id, name }` - the name is what the screen shows. An outcome of `all` or
|
|
46
|
+
* none is every outcome. Pure.
|
|
47
|
+
*/
|
|
48
|
+
export declare function auditQueryOf(filters: readonly GridFilter[], now?: Date): {
|
|
49
|
+
from: any;
|
|
50
|
+
to: any;
|
|
51
|
+
actor: {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
} | undefined;
|
|
55
|
+
door: string | undefined;
|
|
56
|
+
outcome: AuditOutcome;
|
|
57
|
+
};
|
|
30
58
|
export {};
|
|
@@ -56,4 +56,40 @@ export function lastDays(days, now = new Date()) {
|
|
|
56
56
|
from.setDate(from.getDate() - (days - 1));
|
|
57
57
|
return { from, to: now };
|
|
58
58
|
}
|
|
59
|
+
const AUDIT_OUTCOMES = ['denied', 'error', 'failed'];
|
|
60
|
+
/** The day window the audit screen opens on, and reads when no window or date is chosen. */
|
|
61
|
+
export const AUDIT_DEFAULT_DAYS = 30;
|
|
62
|
+
/** A `YYYY-MM-DD` day as the local start of that day, or its last moment. */
|
|
63
|
+
function localDay(day, end) {
|
|
64
|
+
const [year, month, date] = String(day).split('-').map(Number);
|
|
65
|
+
if (!year || !month || !date)
|
|
66
|
+
return undefined;
|
|
67
|
+
return end ? new Date(year, month - 1, date, 23, 59, 59, 999) : new Date(year, month - 1, date, 0, 0, 0);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* What the audit screen reads, from its grid's filters (FOOTER-BUTTON-REVIEW G09 - the conditions are the grid's, kept
|
|
71
|
+
* in the address): the period, the person and the privilege narrowed to, and the outcome.
|
|
72
|
+
*
|
|
73
|
+
* The period is the dates given (`period`, between), else the day window (`days`), else the last 30 days. Dates win over
|
|
74
|
+
* the window, as picking a date used to drop the window. A range given only one bound runs from the window's start
|
|
75
|
+
* before its end, or up to now. The person is `{ id, name }` - the name is what the screen shows. An outcome of `all` or
|
|
76
|
+
* none is every outcome. Pure.
|
|
77
|
+
*/
|
|
78
|
+
export function auditQueryOf(filters, now = new Date()) {
|
|
79
|
+
const valueOf = (name) => filters.find(one => one.name === name)?.value;
|
|
80
|
+
const [start, end] = Array.isArray(valueOf('period')) ? valueOf('period') : [];
|
|
81
|
+
const days = Number(Array.isArray(valueOf('days')) ? valueOf('days')[0] : valueOf('days')) || AUDIT_DEFAULT_DAYS;
|
|
82
|
+
const upTo = (end && localDay(end, true)) || now;
|
|
83
|
+
const from = (start && localDay(start, false)) || lastDays(days, upTo).from;
|
|
84
|
+
const actor = valueOf('actor');
|
|
85
|
+
const door = valueOf('door');
|
|
86
|
+
const outcome = valueOf('outcome');
|
|
87
|
+
return {
|
|
88
|
+
from,
|
|
89
|
+
to: upTo,
|
|
90
|
+
actor: actor && typeof actor === 'object' && actor.id ? actor : undefined,
|
|
91
|
+
door: typeof door === 'string' && door ? door : undefined,
|
|
92
|
+
outcome: (AUDIT_OUTCOMES.includes(outcome) ? outcome : '')
|
|
93
|
+
};
|
|
94
|
+
}
|
|
59
95
|
//# sourceMappingURL=audit-words.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-words.js","sourceRoot":"","sources":["../../client/utils/audit-words.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How the audit screen says what an audit row holds (ADR-0141 §6). The rows hold codes; the screen\n * shows words. Pure, so the words are tested without a screen.\n */\n\ntype Translate = (key: string, options?: Record<string, unknown>) => string\n\nexport interface AuditActionRow {\n kind: string\n door?: string | null\n decision?: string | null\n outcome?: string | null\n code?: string | null\n count?: number | null\n objectId?: string | null\n detail?: Record<string, unknown> | null\n}\n\n/** The door in words: the category's name and the declared axis - `payroll:query` → 「급여 query」. */\nexport function doorWords(t: Translate, door?: string | null): string {\n if (!door) return ''\n if (!door.includes(':')) {\n return door\n .split('+')\n .map(part => t(`audit.door.${part}`, { defaultValue: part }))\n .join(' · ')\n }\n const [category, privilege] = door.split(':')\n return `${t(`privilege.category.${category}`, { defaultValue: category })} ${privilege}`\n}\n\nfunction statusWords(t: Translate, status: unknown) {\n return t(`label.${status}-user`, { defaultValue: String(status ?? '') })\n}\n\n/** One action in words, with how many times when more than once. */\nexport function actionWords(t: Translate, action: AuditActionRow): string {\n const times = (action.count ?? 1) > 1 ? ` ×${action.count}` : ''\n\n if (action.kind === 'door.check') {\n const refused = action.decision === 'denied' ? ` ${t('audit.outcome.denied')}` : ''\n return `${doorWords(t, action.door)}${refused}${times}`\n }\n if (action.kind === 'door.result') {\n return action.outcome === 'error' ? `${doorWords(t, action.door)} ${t('audit.outcome.error')}${times}` : ''\n }\n\n const kind = t(`audit.kind.${action.kind}`, { defaultValue: action.kind })\n const failed = action.outcome === 'error' ? ` ${t('audit.outcome.failed')}` : ''\n const change =\n action.kind === 'user.status' && action.detail\n ? ` ${statusWords(t, action.detail.from)} → ${statusWords(t, action.detail.to)}`\n : ''\n /* A password sign-in is the usual one and says nothing more; a passkey says so (ADR-0156). */\n const method = action.kind === 'auth.signin' && action.detail?.method === 'passkey' ? ` (${t('audit.method.passkey')})` : ''\n return `${kind}${method}${change}${failed}${times}`\n}\n\n/**\n * A request's actions in one line - refusals and errors first, then events, then the doors passed.\n * A door passed and its result say the same thing twice, so only an error result is shown.\n */\nexport function recordWords(t: Translate, actions: AuditActionRow[]): string {\n const weight = (action: AuditActionRow) =>\n action.decision === 'denied' || action.outcome === 'error' ? 0 : action.kind.startsWith('door.') ? 2 : 1\n return [...actions]\n .sort((a, b) => weight(a) - weight(b))\n .map(action => actionWords(t, action))\n .filter(Boolean)\n .join(', ')\n}\n\n/** The last `days` days up to `now`. */\nexport function lastDays(days: number, now = new Date()): { from: Date; to: Date } {\n const from = new Date(now)\n from.setHours(0, 0, 0, 0)\n from.setDate(from.getDate() - (days - 1))\n return { from, to: now }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"audit-words.js","sourceRoot":"","sources":["../../client/utils/audit-words.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How the audit screen says what an audit row holds (ADR-0141 §6). The rows hold codes; the screen\n * shows words. Pure, so the words are tested without a screen.\n */\n\ntype Translate = (key: string, options?: Record<string, unknown>) => string\n\nexport interface AuditActionRow {\n kind: string\n door?: string | null\n decision?: string | null\n outcome?: string | null\n code?: string | null\n count?: number | null\n objectId?: string | null\n detail?: Record<string, unknown> | null\n}\n\n/** The door in words: the category's name and the declared axis - `payroll:query` → 「급여 query」. */\nexport function doorWords(t: Translate, door?: string | null): string {\n if (!door) return ''\n if (!door.includes(':')) {\n return door\n .split('+')\n .map(part => t(`audit.door.${part}`, { defaultValue: part }))\n .join(' · ')\n }\n const [category, privilege] = door.split(':')\n return `${t(`privilege.category.${category}`, { defaultValue: category })} ${privilege}`\n}\n\nfunction statusWords(t: Translate, status: unknown) {\n return t(`label.${status}-user`, { defaultValue: String(status ?? '') })\n}\n\n/** One action in words, with how many times when more than once. */\nexport function actionWords(t: Translate, action: AuditActionRow): string {\n const times = (action.count ?? 1) > 1 ? ` ×${action.count}` : ''\n\n if (action.kind === 'door.check') {\n const refused = action.decision === 'denied' ? ` ${t('audit.outcome.denied')}` : ''\n return `${doorWords(t, action.door)}${refused}${times}`\n }\n if (action.kind === 'door.result') {\n return action.outcome === 'error' ? `${doorWords(t, action.door)} ${t('audit.outcome.error')}${times}` : ''\n }\n\n const kind = t(`audit.kind.${action.kind}`, { defaultValue: action.kind })\n const failed = action.outcome === 'error' ? ` ${t('audit.outcome.failed')}` : ''\n const change =\n action.kind === 'user.status' && action.detail\n ? ` ${statusWords(t, action.detail.from)} → ${statusWords(t, action.detail.to)}`\n : ''\n /* A password sign-in is the usual one and says nothing more; a passkey says so (ADR-0156). */\n const method = action.kind === 'auth.signin' && action.detail?.method === 'passkey' ? ` (${t('audit.method.passkey')})` : ''\n return `${kind}${method}${change}${failed}${times}`\n}\n\n/**\n * A request's actions in one line - refusals and errors first, then events, then the doors passed.\n * A door passed and its result say the same thing twice, so only an error result is shown.\n */\nexport function recordWords(t: Translate, actions: AuditActionRow[]): string {\n const weight = (action: AuditActionRow) =>\n action.decision === 'denied' || action.outcome === 'error' ? 0 : action.kind.startsWith('door.') ? 2 : 1\n return [...actions]\n .sort((a, b) => weight(a) - weight(b))\n .map(action => actionWords(t, action))\n .filter(Boolean)\n .join(', ')\n}\n\n/** The last `days` days up to `now`. */\nexport function lastDays(days: number, now = new Date()): { from: Date; to: Date } {\n const from = new Date(now)\n from.setHours(0, 0, 0, 0)\n from.setDate(from.getDate() - (days - 1))\n return { from, to: now }\n}\n\n/** A grid filter as the audit screen's list holds it (data-grist `FilterValue`). */\ntype GridFilter = { name: string; operator: string; value: any }\n\nexport type AuditOutcome = '' | 'denied' | 'error' | 'failed'\nconst AUDIT_OUTCOMES: AuditOutcome[] = ['denied', 'error', 'failed']\n\n/** The day window the audit screen opens on, and reads when no window or date is chosen. */\nexport const AUDIT_DEFAULT_DAYS = 30\n\n/** A `YYYY-MM-DD` day as the local start of that day, or its last moment. */\nfunction localDay(day: string, end: boolean): Date | undefined {\n const [year, month, date] = String(day).split('-').map(Number)\n if (!year || !month || !date) return undefined\n return end ? new Date(year, month - 1, date, 23, 59, 59, 999) : new Date(year, month - 1, date, 0, 0, 0)\n}\n\n/**\n * What the audit screen reads, from its grid's filters (FOOTER-BUTTON-REVIEW G09 - the conditions are the grid's, kept\n * in the address): the period, the person and the privilege narrowed to, and the outcome.\n *\n * The period is the dates given (`period`, between), else the day window (`days`), else the last 30 days. Dates win over\n * the window, as picking a date used to drop the window. A range given only one bound runs from the window's start\n * before its end, or up to now. The person is `{ id, name }` - the name is what the screen shows. An outcome of `all` or\n * none is every outcome. Pure.\n */\nexport function auditQueryOf(filters: readonly GridFilter[], now = new Date()) {\n const valueOf = (name: string) => filters.find(one => one.name === name)?.value\n const [start, end] = Array.isArray(valueOf('period')) ? valueOf('period') : []\n const days = Number(Array.isArray(valueOf('days')) ? valueOf('days')[0] : valueOf('days')) || AUDIT_DEFAULT_DAYS\n const upTo = (end && localDay(end, true)) || now\n const from = (start && localDay(start, false)) || lastDays(days, upTo).from\n const actor = valueOf('actor')\n const door = valueOf('door')\n const outcome = valueOf('outcome')\n return {\n from,\n to: upTo,\n actor: actor && typeof actor === 'object' && actor.id ? (actor as { id: string; name: string }) : undefined,\n door: typeof door === 'string' && door ? door : undefined,\n outcome: (AUDIT_OUTCOMES.includes(outcome) ? outcome : '') as AuditOutcome\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { actionWords, doorWords, lastDays, recordWords } from './audit-words';
|
|
1
|
+
import { actionWords, auditQueryOf, doorWords, lastDays, recordWords } from './audit-words';
|
|
2
2
|
const WORDS = {
|
|
3
3
|
'privilege.category.payroll': '급여',
|
|
4
4
|
'audit.door.super': '시스템 소유자',
|
|
@@ -50,4 +50,36 @@ describe('the audit screen says what a row holds', () => {
|
|
|
50
50
|
expect(to).toBe(now);
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
|
+
describe('the audit screen reads its conditions from the grid filters', () => {
|
|
54
|
+
const now = new Date(2026, 10, 8, 15, 30);
|
|
55
|
+
it('★ opens on the last 30 days when nothing is chosen', () => {
|
|
56
|
+
const query = auditQueryOf([], now);
|
|
57
|
+
expect(query.from).toEqual(new Date(2026, 9, 10, 0, 0, 0));
|
|
58
|
+
expect(query.to).toBe(now);
|
|
59
|
+
expect(query).toMatchObject({ actor: undefined, door: undefined, outcome: '' });
|
|
60
|
+
});
|
|
61
|
+
it('a day window counts back from today', () => {
|
|
62
|
+
expect(auditQueryOf([{ name: 'days', operator: 'eq', value: '7' }], now).from).toEqual(new Date(2026, 10, 2, 0, 0, 0));
|
|
63
|
+
});
|
|
64
|
+
it('★ dates win over the day window, from the first moment to the last of the days', () => {
|
|
65
|
+
const query = auditQueryOf([
|
|
66
|
+
{ name: 'days', operator: 'eq', value: '90' },
|
|
67
|
+
{ name: 'period', operator: 'between', value: ['2026-10-01', '2026-10-05'] }
|
|
68
|
+
], now);
|
|
69
|
+
expect(query.from).toEqual(new Date(2026, 9, 1, 0, 0, 0));
|
|
70
|
+
expect(query.to).toEqual(new Date(2026, 9, 5, 23, 59, 59, 999));
|
|
71
|
+
});
|
|
72
|
+
it('one date alone keeps the window for the other bound', () => {
|
|
73
|
+
expect(auditQueryOf([{ name: 'period', operator: 'between', value: ['2026-11-01', ''] }], now)).toMatchObject({ from: new Date(2026, 10, 1, 0, 0, 0), to: now });
|
|
74
|
+
});
|
|
75
|
+
it('★ the person, the privilege and the outcome narrowed to', () => {
|
|
76
|
+
const query = auditQueryOf([
|
|
77
|
+
{ name: 'actor', operator: 'eq', value: { id: 'u1', name: '김하티' } },
|
|
78
|
+
{ name: 'door', operator: 'eq', value: 'payroll:query' },
|
|
79
|
+
{ name: 'outcome', operator: 'eq', value: 'denied' }
|
|
80
|
+
], now);
|
|
81
|
+
expect(query).toMatchObject({ actor: { id: 'u1', name: '김하티' }, door: 'payroll:query', outcome: 'denied' });
|
|
82
|
+
expect(auditQueryOf([{ name: 'outcome', operator: 'eq', value: 'all' }], now).outcome).toBe('');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
53
85
|
//# sourceMappingURL=audit-words.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-words.test.js","sourceRoot":"","sources":["../../client/utils/audit-words.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { actionWords, doorWords, lastDays, recordWords } from './audit-words'\n\nconst WORDS: Record<string, string> = {\n 'privilege.category.payroll': '급여',\n 'audit.door.super': '시스템 소유자',\n 'audit.outcome.denied': '거절',\n 'audit.outcome.error': '오류',\n 'audit.outcome.failed': '실패',\n 'audit.kind.auth.signin': '로그인',\n 'audit.method.passkey': '패스키',\n 'audit.kind.role.grant': '역할 부여',\n 'audit.kind.user.status': '계정 상태',\n 'label.activated-user': '정상',\n 'label.locked-user': '잠김'\n}\nconst t = (key: string, options?: any) => WORDS[key] ?? options?.defaultValue ?? key\n\ndescribe('the audit screen says what a row holds', () => {\n it('a door is its category in words and the declared axis', () => {\n expect(doorWords(t, 'payroll:query')).toBe('급여 query')\n expect(doorWords(t, 'super')).toBe('시스템 소유자')\n expect(doorWords(t, 'unknown-category:query')).toBe('unknown-category query')\n })\n\n it('a refusal says so, with how many times', () => {\n expect(actionWords(t, { kind: 'door.check', door: 'payroll:query', decision: 'denied', count: 3 })).toBe('급여 query 거절 ×3')\n })\n\n it('a result is shown only when it failed - passing already said it ran', () => {\n expect(actionWords(t, { kind: 'door.result', door: 'payroll:query', outcome: 'ok' })).toBe('')\n expect(actionWords(t, { kind: 'door.result', door: 'payroll:query', outcome: 'error' })).toBe('급여 query 오류')\n })\n\n it('an event in words, a status change with both states', () => {\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'error', code: 'user not found' })).toBe('로그인 실패')\n expect(actionWords(t, { kind: 'user.status', detail: { from: 'activated', to: 'locked' } })).toBe('계정 상태 정상 → 잠김')\n })\n\n it('a passkey sign-in says so; a password sign-in says nothing more', () => {\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'ok', detail: { method: 'passkey' } })).toBe('로그인 (패스키)')\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'error', detail: { method: 'passkey' } })).toBe('로그인 (패스키) 실패')\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'ok', detail: { method: 'password' } })).toBe('로그인')\n })\n\n it('a request reads refusals first, then events, then doors passed', () => {\n expect(\n recordWords(t, [\n { kind: 'door.check', door: 'payroll:query', decision: 'granted' },\n { kind: 'door.result', door: 'payroll:query', outcome: 'ok' },\n { kind: 'role.grant' },\n { kind: 'door.check', door: 'payroll:mutation', decision: 'denied' }\n ])\n ).toBe('급여 mutation 거절, 역할 부여, 급여 query')\n })\n\n it('the last seven days start at midnight six days back', () => {\n const now = new Date(2026, 9, 19, 15, 30)\n const { from, to } = lastDays(7, now)\n expect(from).toEqual(new Date(2026, 9, 13, 0, 0))\n expect(to).toBe(now)\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"audit-words.test.js","sourceRoot":"","sources":["../../client/utils/audit-words.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3F,MAAM,KAAK,GAA2B;IACpC,4BAA4B,EAAE,IAAI;IAClC,kBAAkB,EAAE,SAAS;IAC7B,sBAAsB,EAAE,IAAI;IAC5B,qBAAqB,EAAE,IAAI;IAC3B,sBAAsB,EAAE,IAAI;IAC5B,wBAAwB,EAAE,KAAK;IAC/B,sBAAsB,EAAE,KAAK;IAC7B,uBAAuB,EAAE,OAAO;IAChC,wBAAwB,EAAE,OAAO;IACjC,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;CAC1B,CAAA;AACD,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,OAAa,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,EAAE,YAAY,IAAI,GAAG,CAAA;AAEpF,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACtD,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC7C,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;IAC/E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAC5H,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC9F,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAC9G,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACxG,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IACpH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC/G,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QACrH,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5G,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,CACJ,WAAW,CAAC,CAAC,EAAE;YACb,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE;YAClE,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE;YAC7D,EAAE,IAAI,EAAE,YAAY,EAAE;YACtB,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,EAAE;SACrE,CAAC,CACH,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;QACzC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACjD,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,6DAA6D,EAAE,GAAG,EAAE;IAC3E,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IAEzC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;QACnC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC1D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;IACjF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACxH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,KAAK,GAAG,YAAY,CACxB;YACE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;YAC7C,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,EAAE;SAC7E,EACD,GAAG,CACJ,CAAA;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACzD,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;IAClK,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAG,YAAY,CACxB;YACE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YACnE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE;YACxD,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;SACrD,EACD,GAAG,CACJ,CAAA;QACD,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC3G,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACjG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { actionWords, auditQueryOf, doorWords, lastDays, recordWords } from './audit-words'\n\nconst WORDS: Record<string, string> = {\n 'privilege.category.payroll': '급여',\n 'audit.door.super': '시스템 소유자',\n 'audit.outcome.denied': '거절',\n 'audit.outcome.error': '오류',\n 'audit.outcome.failed': '실패',\n 'audit.kind.auth.signin': '로그인',\n 'audit.method.passkey': '패스키',\n 'audit.kind.role.grant': '역할 부여',\n 'audit.kind.user.status': '계정 상태',\n 'label.activated-user': '정상',\n 'label.locked-user': '잠김'\n}\nconst t = (key: string, options?: any) => WORDS[key] ?? options?.defaultValue ?? key\n\ndescribe('the audit screen says what a row holds', () => {\n it('a door is its category in words and the declared axis', () => {\n expect(doorWords(t, 'payroll:query')).toBe('급여 query')\n expect(doorWords(t, 'super')).toBe('시스템 소유자')\n expect(doorWords(t, 'unknown-category:query')).toBe('unknown-category query')\n })\n\n it('a refusal says so, with how many times', () => {\n expect(actionWords(t, { kind: 'door.check', door: 'payroll:query', decision: 'denied', count: 3 })).toBe('급여 query 거절 ×3')\n })\n\n it('a result is shown only when it failed - passing already said it ran', () => {\n expect(actionWords(t, { kind: 'door.result', door: 'payroll:query', outcome: 'ok' })).toBe('')\n expect(actionWords(t, { kind: 'door.result', door: 'payroll:query', outcome: 'error' })).toBe('급여 query 오류')\n })\n\n it('an event in words, a status change with both states', () => {\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'error', code: 'user not found' })).toBe('로그인 실패')\n expect(actionWords(t, { kind: 'user.status', detail: { from: 'activated', to: 'locked' } })).toBe('계정 상태 정상 → 잠김')\n })\n\n it('a passkey sign-in says so; a password sign-in says nothing more', () => {\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'ok', detail: { method: 'passkey' } })).toBe('로그인 (패스키)')\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'error', detail: { method: 'passkey' } })).toBe('로그인 (패스키) 실패')\n expect(actionWords(t, { kind: 'auth.signin', outcome: 'ok', detail: { method: 'password' } })).toBe('로그인')\n })\n\n it('a request reads refusals first, then events, then doors passed', () => {\n expect(\n recordWords(t, [\n { kind: 'door.check', door: 'payroll:query', decision: 'granted' },\n { kind: 'door.result', door: 'payroll:query', outcome: 'ok' },\n { kind: 'role.grant' },\n { kind: 'door.check', door: 'payroll:mutation', decision: 'denied' }\n ])\n ).toBe('급여 mutation 거절, 역할 부여, 급여 query')\n })\n\n it('the last seven days start at midnight six days back', () => {\n const now = new Date(2026, 9, 19, 15, 30)\n const { from, to } = lastDays(7, now)\n expect(from).toEqual(new Date(2026, 9, 13, 0, 0))\n expect(to).toBe(now)\n })\n})\n\ndescribe('the audit screen reads its conditions from the grid filters', () => {\n const now = new Date(2026, 10, 8, 15, 30)\n\n it('★ opens on the last 30 days when nothing is chosen', () => {\n const query = auditQueryOf([], now)\n expect(query.from).toEqual(new Date(2026, 9, 10, 0, 0, 0))\n expect(query.to).toBe(now)\n expect(query).toMatchObject({ actor: undefined, door: undefined, outcome: '' })\n })\n\n it('a day window counts back from today', () => {\n expect(auditQueryOf([{ name: 'days', operator: 'eq', value: '7' }], now).from).toEqual(new Date(2026, 10, 2, 0, 0, 0))\n })\n\n it('★ dates win over the day window, from the first moment to the last of the days', () => {\n const query = auditQueryOf(\n [\n { name: 'days', operator: 'eq', value: '90' },\n { name: 'period', operator: 'between', value: ['2026-10-01', '2026-10-05'] }\n ],\n now\n )\n expect(query.from).toEqual(new Date(2026, 9, 1, 0, 0, 0))\n expect(query.to).toEqual(new Date(2026, 9, 5, 23, 59, 59, 999))\n })\n\n it('one date alone keeps the window for the other bound', () => {\n expect(auditQueryOf([{ name: 'period', operator: 'between', value: ['2026-11-01', ''] }], now)).toMatchObject({ from: new Date(2026, 10, 1, 0, 0, 0), to: now })\n })\n\n it('★ the person, the privilege and the outcome narrowed to', () => {\n const query = auditQueryOf(\n [\n { name: 'actor', operator: 'eq', value: { id: 'u1', name: '김하티' } },\n { name: 'door', operator: 'eq', value: 'payroll:query' },\n { name: 'outcome', operator: 'eq', value: 'denied' }\n ],\n now\n )\n expect(query).toMatchObject({ actor: { id: 'u1', name: '김하티' }, door: 'payroll:query', outcome: 'denied' })\n expect(auditQueryOf([{ name: 'outcome', operator: 'eq', value: 'all' }], now).outcome).toBe('')\n })\n})\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Which roles a role list shows - the server's `InheritedValueType` (@operato/shell), by value. */
|
|
2
|
+
export type RoleScope = 'None' | 'Only' | 'Include';
|
|
3
|
+
/**
|
|
4
|
+
* The role list's grid filters as the roles query takes them (FOOTER-BUTTON-REVIEW G09): the 묶음 chip - the hidden
|
|
5
|
+
* `inherited` column's filter - goes as the query's `inherited` argument, the rest as filters. The list opens on
|
|
6
|
+
* 「우리 역할」 (`None`), the grid's default written to the address; no chip pressed is every role (`Include`). Pure.
|
|
7
|
+
*/
|
|
8
|
+
export declare function roleQueryOf<F extends {
|
|
9
|
+
name: string;
|
|
10
|
+
operator: string;
|
|
11
|
+
value: any;
|
|
12
|
+
}>(filters: readonly F[]): {
|
|
13
|
+
filters: F[];
|
|
14
|
+
inherited: RoleScope;
|
|
15
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const SCOPES = ['None', 'Only', 'Include'];
|
|
2
|
+
/**
|
|
3
|
+
* The role list's grid filters as the roles query takes them (FOOTER-BUTTON-REVIEW G09): the 묶음 chip - the hidden
|
|
4
|
+
* `inherited` column's filter - goes as the query's `inherited` argument, the rest as filters. The list opens on
|
|
5
|
+
* 「우리 역할」 (`None`), the grid's default written to the address; no chip pressed is every role (`Include`). Pure.
|
|
6
|
+
*/
|
|
7
|
+
export function roleQueryOf(filters) {
|
|
8
|
+
const chosen = filters.find(one => one.name === 'inherited');
|
|
9
|
+
const value = Array.isArray(chosen?.value) ? chosen?.value[0] : chosen?.value;
|
|
10
|
+
const inherited = SCOPES.includes(value) ? value : 'Include';
|
|
11
|
+
return { filters: filters.filter(one => one.name !== 'inherited'), inherited };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=role-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-scope.js","sourceRoot":"","sources":["../../client/utils/role-scope.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;AAEvD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAA2D,OAAqB;IACzG,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAA;IAC7E,MAAM,SAAS,GAAc,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IACvE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,SAAS,EAAE,CAAA;AAChF,CAAC","sourcesContent":["/** Which roles a role list shows - the server's `InheritedValueType` (@operato/shell), by value. */\nexport type RoleScope = 'None' | 'Only' | 'Include'\nconst SCOPES: RoleScope[] = ['None', 'Only', 'Include']\n\n/**\n * The role list's grid filters as the roles query takes them (FOOTER-BUTTON-REVIEW G09): the 묶음 chip - the hidden\n * `inherited` column's filter - goes as the query's `inherited` argument, the rest as filters. The list opens on\n * 「우리 역할」 (`None`), the grid's default written to the address; no chip pressed is every role (`Include`). Pure.\n */\nexport function roleQueryOf<F extends { name: string; operator: string; value: any }>(filters: readonly F[]) {\n const chosen = filters.find(one => one.name === 'inherited')\n const value = Array.isArray(chosen?.value) ? chosen?.value[0] : chosen?.value\n const inherited: RoleScope = SCOPES.includes(value) ? value : 'Include'\n return { filters: filters.filter(one => one.name !== 'inherited'), inherited }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { roleQueryOf } from './role-scope';
|
|
2
|
+
describe('the role list reads its 묶음 from the grid filters', () => {
|
|
3
|
+
it('★ the chip goes as the query\'s inherited argument and leaves the filters', () => {
|
|
4
|
+
const asked = [
|
|
5
|
+
{ name: 'inherited', operator: 'eq', value: 'Only' },
|
|
6
|
+
{ name: 'name', operator: 'search', value: '%영업%' }
|
|
7
|
+
];
|
|
8
|
+
expect(roleQueryOf(asked)).toEqual({ filters: [{ name: 'name', operator: 'search', value: '%영업%' }], inherited: 'Only' });
|
|
9
|
+
});
|
|
10
|
+
it('a chip handed as one value of a list reads the same', () => {
|
|
11
|
+
expect(roleQueryOf([{ name: 'inherited', operator: 'eq', value: ['None'] }]).inherited).toBe('None');
|
|
12
|
+
});
|
|
13
|
+
it('★ no chip pressed, or a value the server does not know, is every role', () => {
|
|
14
|
+
expect(roleQueryOf([])).toEqual({ filters: [], inherited: 'Include' });
|
|
15
|
+
expect(roleQueryOf([{ name: 'inherited', operator: 'eq', value: 'mine' }]).inherited).toBe('Include');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=role-scope.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-scope.test.js","sourceRoot":"","sources":["../../client/utils/role-scope.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAChE,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,KAAK,GAAG;YACZ,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;YACpD,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE;SACpD,CAAA;QACD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IAC3H,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACtG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;QACtE,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACvG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { roleQueryOf } from './role-scope'\n\ndescribe('the role list reads its 묶음 from the grid filters', () => {\n it('★ the chip goes as the query\\'s inherited argument and leaves the filters', () => {\n const asked = [\n { name: 'inherited', operator: 'eq', value: 'Only' },\n { name: 'name', operator: 'search', value: '%영업%' }\n ]\n expect(roleQueryOf(asked)).toEqual({ filters: [{ name: 'name', operator: 'search', value: '%영업%' }], inherited: 'Only' })\n })\n\n it('a chip handed as one value of a list reads the same', () => {\n expect(roleQueryOf([{ name: 'inherited', operator: 'eq', value: ['None'] }]).inherited).toBe('None')\n })\n\n it('★ no chip pressed, or a value the server does not know, is every role', () => {\n expect(roleQueryOf([])).toEqual({ filters: [], inherited: 'Include' })\n expect(roleQueryOf([{ name: 'inherited', operator: 'eq', value: 'mine' }]).inherited).toBe('Include')\n })\n})\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/auth-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.260",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "dist-client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@material/web": "^2.0.0",
|
|
31
31
|
"@operato/attribute": "^10.29.39",
|
|
32
|
-
"@operato/data-grist": "^10.29.
|
|
32
|
+
"@operato/data-grist": "^10.29.71",
|
|
33
33
|
"@operato/graphql": "^10.29.69",
|
|
34
34
|
"@operato/i18n": "^10.0.0",
|
|
35
35
|
"@operato/input": "^10.29.39",
|
|
@@ -43,12 +43,13 @@
|
|
|
43
43
|
"@operato/shell": "^10.29.69",
|
|
44
44
|
"@operato/styles": "^10.0.0",
|
|
45
45
|
"@operato/utils": "^10.29.69",
|
|
46
|
-
"@things-factory/
|
|
47
|
-
"@things-factory/
|
|
48
|
-
"@things-factory/
|
|
49
|
-
"@things-factory/
|
|
46
|
+
"@things-factory/ai-assistant": "^10.1.260",
|
|
47
|
+
"@things-factory/apptool-base": "^10.1.260",
|
|
48
|
+
"@things-factory/auth-base": "^10.1.260",
|
|
49
|
+
"@things-factory/component-ui": "^10.1.260",
|
|
50
|
+
"@things-factory/more-base": "^10.1.260",
|
|
50
51
|
"base64url": "^3.0.1",
|
|
51
52
|
"clipboard": "^2.0.6"
|
|
52
53
|
},
|
|
53
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "72de583ba0394dcf8195ef723b0ee4217507e0e5"
|
|
54
55
|
}
|
package/translations/en.json
CHANGED
|
@@ -114,7 +114,6 @@
|
|
|
114
114
|
"label.add to domain": "add to domain",
|
|
115
115
|
"label.add user": "Add user",
|
|
116
116
|
"label.admin-user": "admin",
|
|
117
|
-
"label.all": "All",
|
|
118
117
|
"label.application": "application",
|
|
119
118
|
"label.banned-user": "banned",
|
|
120
119
|
"label.category": "category",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count} days",
|
|
463
462
|
"audit.period.from": "From",
|
|
464
463
|
"audit.period.to": "To",
|
|
464
|
+
"audit.period.range": "Period",
|
|
465
465
|
"audit.outcome.all": "All",
|
|
466
466
|
"audit.outcome.denied": "Refused",
|
|
467
467
|
"audit.outcome.error": "Error",
|
package/translations/es.json
CHANGED
|
@@ -114,7 +114,6 @@
|
|
|
114
114
|
"label.add to domain": "añadir al dominio",
|
|
115
115
|
"label.add user": "Añadir usuario",
|
|
116
116
|
"label.admin-user": "administrador",
|
|
117
|
-
"label.all": "Todos",
|
|
118
117
|
"label.application": "aplicación",
|
|
119
118
|
"label.banned-user": "vetado",
|
|
120
119
|
"label.category": "categoría",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count} días",
|
|
463
462
|
"audit.period.from": "Desde",
|
|
464
463
|
"audit.period.to": "Hasta",
|
|
464
|
+
"audit.period.range": "Periodo",
|
|
465
465
|
"audit.outcome.all": "Todo",
|
|
466
466
|
"audit.outcome.denied": "Rechazado",
|
|
467
467
|
"audit.outcome.error": "Error",
|
package/translations/ja.json
CHANGED
|
@@ -305,7 +305,6 @@
|
|
|
305
305
|
"label.add role": "ロールを追加",
|
|
306
306
|
"label.add to domain": "$t(term.domain)に追加",
|
|
307
307
|
"label.add user": "ユーザーを追加",
|
|
308
|
-
"label.all": "すべて",
|
|
309
308
|
"label.create account and add": "アカウントを作成して追加",
|
|
310
309
|
"label.execute": "実行",
|
|
311
310
|
"label.inherited": "継承",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count}日",
|
|
463
462
|
"audit.period.from": "開始日",
|
|
464
463
|
"audit.period.to": "終了日",
|
|
464
|
+
"audit.period.range": "照会期間",
|
|
465
465
|
"audit.outcome.all": "すべて",
|
|
466
466
|
"audit.outcome.denied": "拒否",
|
|
467
467
|
"audit.outcome.error": "エラー",
|
package/translations/ko.json
CHANGED
|
@@ -114,7 +114,6 @@
|
|
|
114
114
|
"label.add to domain": "$t(term.domain)에 넣기",
|
|
115
115
|
"label.add user": "사용자 추가",
|
|
116
116
|
"label.admin-user": "관리자",
|
|
117
|
-
"label.all": "전체",
|
|
118
117
|
"label.application": "응용프로그램",
|
|
119
118
|
"label.banned-user": "사용금지",
|
|
120
119
|
"label.category": "카테고리",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count}일",
|
|
463
462
|
"audit.period.from": "시작일",
|
|
464
463
|
"audit.period.to": "종료일",
|
|
464
|
+
"audit.period.range": "조회 기간",
|
|
465
465
|
"audit.outcome.all": "전체",
|
|
466
466
|
"audit.outcome.denied": "거절",
|
|
467
467
|
"audit.outcome.error": "오류",
|
package/translations/ms.json
CHANGED
|
@@ -305,7 +305,6 @@
|
|
|
305
305
|
"label.add role": "tambah peranan",
|
|
306
306
|
"label.add to domain": "tambah ke $t(term.domain)",
|
|
307
307
|
"label.add user": "tambah pengguna",
|
|
308
|
-
"label.all": "semua",
|
|
309
308
|
"label.create account and add": "cipta akaun dan tambah",
|
|
310
309
|
"label.execute": "laksana",
|
|
311
310
|
"label.inherited": "diwarisi",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count} hari",
|
|
463
462
|
"audit.period.from": "Dari",
|
|
464
463
|
"audit.period.to": "Hingga",
|
|
464
|
+
"audit.period.range": "Tempoh",
|
|
465
465
|
"audit.outcome.all": "Semua",
|
|
466
466
|
"audit.outcome.denied": "Ditolak",
|
|
467
467
|
"audit.outcome.error": "Ralat",
|
package/translations/zh.json
CHANGED
|
@@ -305,7 +305,6 @@
|
|
|
305
305
|
"label.add role": "添加角色",
|
|
306
306
|
"label.add to domain": "添加到$t(term.domain)",
|
|
307
307
|
"label.add user": "添加用户",
|
|
308
|
-
"label.all": "全部",
|
|
309
308
|
"label.create account and add": "创建账户并添加",
|
|
310
309
|
"label.execute": "执行",
|
|
311
310
|
"label.inherited": "继承",
|
|
@@ -462,6 +461,7 @@
|
|
|
462
461
|
"audit.period.days": "{count}天",
|
|
463
462
|
"audit.period.from": "开始日期",
|
|
464
463
|
"audit.period.to": "结束日期",
|
|
464
|
+
"audit.period.range": "查询期间",
|
|
465
465
|
"audit.outcome.all": "全部",
|
|
466
466
|
"audit.outcome.denied": "拒绝",
|
|
467
467
|
"audit.outcome.error": "错误",
|