@shamar/adonis 0.1.0
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/assets/admin.css +2 -0
- package/assets/alpine.min.js +5 -0
- package/assets/input.css +2286 -0
- package/assets/shamar-ui.js +3658 -0
- package/dist/assets-controller.d.ts +12 -0
- package/dist/assets-controller.d.ts.map +1 -0
- package/dist/assets-controller.js +32 -0
- package/dist/assets-controller.js.map +1 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +21 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +20 -0
- package/dist/configure.js.map +1 -0
- package/dist/context.d.ts +22 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/controller.d.ts +31 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +45 -0
- package/dist/controller.js.map +1 -0
- package/dist/controllers/admin_controller.d.ts +72 -0
- package/dist/controllers/admin_controller.d.ts.map +1 -0
- package/dist/controllers/admin_controller.js +1075 -0
- package/dist/controllers/admin_controller.js.map +1 -0
- package/dist/discover.d.ts +7 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +50 -0
- package/dist/discover.js.map +1 -0
- package/dist/form-state.d.ts +41 -0
- package/dist/form-state.d.ts.map +1 -0
- package/dist/form-state.js +74 -0
- package/dist/form-state.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/require_api_key_middleware.d.ts +30 -0
- package/dist/middleware/require_api_key_middleware.d.ts.map +1 -0
- package/dist/middleware/require_api_key_middleware.js +59 -0
- package/dist/middleware/require_api_key_middleware.js.map +1 -0
- package/dist/provider.d.ts +9 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +66 -0
- package/dist/provider.js.map +1 -0
- package/dist/resources/api_key_resource.d.ts +35 -0
- package/dist/resources/api_key_resource.d.ts.map +1 -0
- package/dist/resources/api_key_resource.js +225 -0
- package/dist/resources/api_key_resource.js.map +1 -0
- package/dist/routes.d.ts +5 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +97 -0
- package/dist/routes.js.map +1 -0
- package/dist/runtime.d.ts +27 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +74 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shamar/auth.d.ts +30 -0
- package/dist/shamar/auth.d.ts.map +1 -0
- package/dist/shamar/auth.js +218 -0
- package/dist/shamar/auth.js.map +1 -0
- package/dist/shamar/branding.d.ts +24 -0
- package/dist/shamar/branding.d.ts.map +1 -0
- package/dist/shamar/branding.js +91 -0
- package/dist/shamar/branding.js.map +1 -0
- package/dist/shamar/display-relations.d.ts +7 -0
- package/dist/shamar/display-relations.d.ts.map +1 -0
- package/dist/shamar/display-relations.js +89 -0
- package/dist/shamar/display-relations.js.map +1 -0
- package/dist/shamar/list-headers.d.ts +38 -0
- package/dist/shamar/list-headers.d.ts.map +1 -0
- package/dist/shamar/list-headers.js +218 -0
- package/dist/shamar/list-headers.js.map +1 -0
- package/dist/shamar/list-query.d.ts +229 -0
- package/dist/shamar/list-query.d.ts.map +1 -0
- package/dist/shamar/list-query.js +564 -0
- package/dist/shamar/list-query.js.map +1 -0
- package/dist/shamar/menu.d.ts +48 -0
- package/dist/shamar/menu.d.ts.map +1 -0
- package/dist/shamar/menu.js +93 -0
- package/dist/shamar/menu.js.map +1 -0
- package/dist/shamar/paths.d.ts +5 -0
- package/dist/shamar/paths.d.ts.map +1 -0
- package/dist/shamar/paths.js +16 -0
- package/dist/shamar/paths.js.map +1 -0
- package/dist/shamar/relation-fields.d.ts +54 -0
- package/dist/shamar/relation-fields.d.ts.map +1 -0
- package/dist/shamar/relation-fields.js +94 -0
- package/dist/shamar/relation-fields.js.map +1 -0
- package/dist/shamar/resource-actions.d.ts +9 -0
- package/dist/shamar/resource-actions.d.ts.map +1 -0
- package/dist/shamar/resource-actions.js +42 -0
- package/dist/shamar/resource-actions.js.map +1 -0
- package/dist/shamar/view-context.d.ts +79 -0
- package/dist/shamar/view-context.d.ts.map +1 -0
- package/dist/shamar/view-context.js +74 -0
- package/dist/shamar/view-context.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/resources/views/shamar/dashboard.edge +24 -0
- package/resources/views/shamar/form.edge +87 -0
- package/resources/views/shamar/index.edge +149 -0
- package/resources/views/shamar/partials/boolean-value.edge +25 -0
- package/resources/views/shamar/partials/csrf.edge +1 -0
- package/resources/views/shamar/partials/detail-entry.edge +72 -0
- package/resources/views/shamar/partials/dialog-host.edge +125 -0
- package/resources/views/shamar/partials/field-affix-icon.edge +23 -0
- package/resources/views/shamar/partials/field-input.edge +585 -0
- package/resources/views/shamar/partials/field-relation-checkbox.edge +104 -0
- package/resources/views/shamar/partials/field-relation-m2m.edge +110 -0
- package/resources/views/shamar/partials/field-relation-m2o.edge +116 -0
- package/resources/views/shamar/partials/field-relation-table.edge +139 -0
- package/resources/views/shamar/partials/form-container.edge +82 -0
- package/resources/views/shamar/partials/icon.edge +14 -0
- package/resources/views/shamar/partials/list-row.edge +73 -0
- package/resources/views/shamar/partials/list-toolbar.edge +269 -0
- package/resources/views/shamar/partials/nav-mobile.edge +21 -0
- package/resources/views/shamar/partials/nav.edge +58 -0
- package/resources/views/shamar/partials/page-heading.edge +130 -0
- package/resources/views/shamar/partials/pagination.edge +73 -0
- package/resources/views/shamar/partials/record-pager.edge +37 -0
- package/resources/views/shamar/partials/schema-node.edge +289 -0
- package/resources/views/shamar/partials/shell-close.edge +20 -0
- package/resources/views/shamar/partials/shell-open.edge +51 -0
- package/resources/views/shamar/partials/toast.edge +5 -0
- package/resources/views/shamar/partials/topbar.edge +115 -0
- package/resources/views/shamar/show.edge +17 -0
- package/stubs/config/shamar.stub +29 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { Authorizer, ForbiddenError, UnauthorizedError, extractAuthCredentials, intersectPermissions, isMachineApiKeyPrincipal, resolveUserPermissions, toCherubimUser, } from '@shamar/cherubim';
|
|
2
|
+
export { ForbiddenError, UnauthorizedError };
|
|
3
|
+
/** Whether panel routes should require an authenticated user. */
|
|
4
|
+
export function authRequired(config) {
|
|
5
|
+
if (config.auth?.required === false)
|
|
6
|
+
return false;
|
|
7
|
+
if (config.auth?.required === true)
|
|
8
|
+
return true;
|
|
9
|
+
return Boolean(config.auth?.guard || config.auth?.resolveUser || config.auth?.apiKeys);
|
|
10
|
+
}
|
|
11
|
+
export function createAuthorizer(config, policies) {
|
|
12
|
+
return new Authorizer({
|
|
13
|
+
superUser: config.auth?.superUser,
|
|
14
|
+
treatAdminsAsSuper: config.auth?.strictPermissions !== false,
|
|
15
|
+
}, undefined, policies);
|
|
16
|
+
}
|
|
17
|
+
function requestHeaders(ctx) {
|
|
18
|
+
const request = ctx.request;
|
|
19
|
+
return {
|
|
20
|
+
get(name) {
|
|
21
|
+
if (typeof request.header === 'function') {
|
|
22
|
+
return request.header(name) ?? undefined;
|
|
23
|
+
}
|
|
24
|
+
const headers = typeof request.headers === 'function' ? request.headers() : (request.headers ?? {});
|
|
25
|
+
const raw = headers[name] ?? headers[name.toLowerCase()];
|
|
26
|
+
if (Array.isArray(raw))
|
|
27
|
+
return raw[0];
|
|
28
|
+
return typeof raw === 'string' ? raw : undefined;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function resolveHeaderCredentials(ctx, config) {
|
|
33
|
+
const resolver = config.auth?.apiKeys?.resolve;
|
|
34
|
+
if (!resolver)
|
|
35
|
+
return { gateway: null, bearer: null, bearerInvalid: false };
|
|
36
|
+
const headers = requestHeaders(ctx);
|
|
37
|
+
const { apiKey, bearer } = extractAuthCredentials(headers);
|
|
38
|
+
let gateway = null;
|
|
39
|
+
let bearerUser = null;
|
|
40
|
+
let bearerInvalid = false;
|
|
41
|
+
if (apiKey) {
|
|
42
|
+
const principal = await resolver(apiKey, ctx);
|
|
43
|
+
if (principal && isMachineApiKeyPrincipal(principal)) {
|
|
44
|
+
gateway = principal;
|
|
45
|
+
}
|
|
46
|
+
else if (principal && !bearer) {
|
|
47
|
+
// PAT mistakenly in X-Api-Key with no Bearer — still accept as identity.
|
|
48
|
+
bearerUser = principal;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (bearer) {
|
|
52
|
+
const principal = await resolver(bearer, ctx);
|
|
53
|
+
if (principal) {
|
|
54
|
+
if (isMachineApiKeyPrincipal(principal) && !gateway) {
|
|
55
|
+
// Single-credential: machine key only in Authorization.
|
|
56
|
+
gateway = principal;
|
|
57
|
+
}
|
|
58
|
+
else if (!isMachineApiKeyPrincipal(principal)) {
|
|
59
|
+
bearerUser = principal;
|
|
60
|
+
}
|
|
61
|
+
// Duplicate machine key in Bearer while X-Api-Key already set — ignore Bearer.
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
bearerInvalid = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { gateway, bearer: bearerUser, bearerInvalid };
|
|
68
|
+
}
|
|
69
|
+
async function resolveSessionUser(ctx, config) {
|
|
70
|
+
if (config.auth?.resolveUser) {
|
|
71
|
+
const user = await config.auth.resolveUser(ctx);
|
|
72
|
+
return user ?? null;
|
|
73
|
+
}
|
|
74
|
+
const guard = config.auth?.guard ?? 'web';
|
|
75
|
+
try {
|
|
76
|
+
const auth = ctx.auth?.use(guard);
|
|
77
|
+
if (!auth || !(await auth.check()))
|
|
78
|
+
return null;
|
|
79
|
+
const raw = auth.user;
|
|
80
|
+
if (!raw)
|
|
81
|
+
return null;
|
|
82
|
+
const id = raw.id ?? raw._id;
|
|
83
|
+
if (id == null || id === '')
|
|
84
|
+
return null;
|
|
85
|
+
const name = (typeof raw.fullName === 'string' && raw.fullName.trim()) ||
|
|
86
|
+
(typeof raw.name === 'string' && raw.name.trim()) ||
|
|
87
|
+
(typeof raw.email === 'string' ? raw.email.split('@')[0] : '') ||
|
|
88
|
+
String(id);
|
|
89
|
+
return toCherubimUser({
|
|
90
|
+
id: String(id),
|
|
91
|
+
name,
|
|
92
|
+
email: typeof raw.email === 'string' ? raw.email : undefined,
|
|
93
|
+
permissions: Array.isArray(raw.permissions) ? raw.permissions : undefined,
|
|
94
|
+
roleIds: Array.isArray(raw.roleIds)
|
|
95
|
+
? raw.roleIds
|
|
96
|
+
: raw.roleId != null && raw.roleId !== ''
|
|
97
|
+
? [String(raw.roleId)]
|
|
98
|
+
: Array.isArray(raw.roles)
|
|
99
|
+
? raw.roles
|
|
100
|
+
: undefined,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Map header credentials / session to a Cherubim principal. */
|
|
108
|
+
export async function resolveShamarUser(ctx, config) {
|
|
109
|
+
const { gateway, bearer } = await resolveHeaderCredentials(ctx, config);
|
|
110
|
+
if (bearer)
|
|
111
|
+
return bearer;
|
|
112
|
+
if (gateway)
|
|
113
|
+
return gateway;
|
|
114
|
+
return resolveSessionUser(ctx, config);
|
|
115
|
+
}
|
|
116
|
+
export async function buildAuthContext(ctx, config, panelId) {
|
|
117
|
+
const { gateway, bearer, bearerInvalid } = await resolveHeaderCredentials(ctx, config);
|
|
118
|
+
// Invalid Bearer must not fall through to the gateway principal.
|
|
119
|
+
if (bearerInvalid) {
|
|
120
|
+
return {
|
|
121
|
+
user: null,
|
|
122
|
+
panelId,
|
|
123
|
+
gatewayApiKeyId: gateway?.apiKeyId,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const sessionUser = bearer || gateway ? null : await resolveSessionUser(ctx, config);
|
|
127
|
+
let user = bearer ?? sessionUser ?? gateway;
|
|
128
|
+
let authMethod;
|
|
129
|
+
if (bearer) {
|
|
130
|
+
authMethod = 'pat';
|
|
131
|
+
}
|
|
132
|
+
else if (sessionUser) {
|
|
133
|
+
authMethod = 'session';
|
|
134
|
+
}
|
|
135
|
+
else if (gateway) {
|
|
136
|
+
authMethod = 'api_key';
|
|
137
|
+
}
|
|
138
|
+
const apiKeyId = user?.apiKeyId;
|
|
139
|
+
const gatewayApiKeyId = gateway?.apiKeyId;
|
|
140
|
+
// Session + PAT: merge role permissions. Machine keys already carry abilities.
|
|
141
|
+
if (user && config.auth?.roleResolver && !isMachineApiKeyPrincipal(user)) {
|
|
142
|
+
user = await resolveUserPermissions(user, config.auth.roleResolver);
|
|
143
|
+
}
|
|
144
|
+
// PAT: optional ability narrowing after role merge (empty = full user access).
|
|
145
|
+
if (authMethod === 'pat') {
|
|
146
|
+
user = {
|
|
147
|
+
...user,
|
|
148
|
+
permissions: intersectPermissions(user.permissions ?? [], user.apiKeyAbilities),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Dual credentials: optionally cap the user by the gateway machine key’s abilities.
|
|
152
|
+
const intersectGateway = config.auth?.apiKeys?.intersectGatewayAbilities !== false;
|
|
153
|
+
if (intersectGateway &&
|
|
154
|
+
gateway &&
|
|
155
|
+
user &&
|
|
156
|
+
!isMachineApiKeyPrincipal(user) &&
|
|
157
|
+
Array.isArray(gateway.permissions) &&
|
|
158
|
+
gateway.permissions.length > 0) {
|
|
159
|
+
user = {
|
|
160
|
+
...user,
|
|
161
|
+
permissions: intersectPermissions(user.permissions ?? [], gateway.permissions),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { user, panelId, authMethod, apiKeyId, gatewayApiKeyId };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* API-prefix requests may require a valid machine gateway key when the
|
|
168
|
+
* `requireApiKey` middleware is applied to the route (or `auth.apiKeys.protectApi`).
|
|
169
|
+
*
|
|
170
|
+
* @deprecated Prefer {@link RequireApiKeyMiddleware} / `protectApi`. Kept for
|
|
171
|
+
* callers that still pass an explicit API-route flag.
|
|
172
|
+
*/
|
|
173
|
+
export function missingRequiredMachineKey(authCtx, config, isApiRoute) {
|
|
174
|
+
if (!isApiRoute)
|
|
175
|
+
return false;
|
|
176
|
+
if (!config.auth?.apiKeys?.protectApi)
|
|
177
|
+
return false;
|
|
178
|
+
return !authCtx.gatewayApiKeyId;
|
|
179
|
+
}
|
|
180
|
+
export function resourcePolicyFlags(authorizer, authCtx, registry, meta, record) {
|
|
181
|
+
const ResourceClass = registry.resourceClass(meta.slug);
|
|
182
|
+
if (!ResourceClass || !authCtx.user) {
|
|
183
|
+
return { create: true, update: true, delete: true };
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
create: authorizer.canResource(authCtx, ResourceClass, 'create'),
|
|
187
|
+
update: authorizer.canResource(authCtx, ResourceClass, 'update', record ?? undefined),
|
|
188
|
+
delete: authorizer.canResource(authCtx, ResourceClass, 'delete', record ?? undefined),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export function canViewResource(authorizer, authCtx, registry, slug) {
|
|
192
|
+
const ResourceClass = registry.resourceClass(slug);
|
|
193
|
+
if (!ResourceClass)
|
|
194
|
+
return true;
|
|
195
|
+
if (!authCtx.user)
|
|
196
|
+
return false;
|
|
197
|
+
return authorizer.canResource(authCtx, ResourceClass, 'viewAny');
|
|
198
|
+
}
|
|
199
|
+
export function respondUnauthorized(ctx, config, asJson, message = 'Authentication required.') {
|
|
200
|
+
if (asJson) {
|
|
201
|
+
return ctx.response.status(401).json({ message });
|
|
202
|
+
}
|
|
203
|
+
return ctx.response.redirect(config.auth?.loginPath ?? '/login');
|
|
204
|
+
}
|
|
205
|
+
export function respondForbidden(ctx, message, asJson, fallbackPath) {
|
|
206
|
+
if (asJson) {
|
|
207
|
+
return ctx.response.status(403).json({ message });
|
|
208
|
+
}
|
|
209
|
+
ctx.session.flash('error', message);
|
|
210
|
+
return ctx.response.redirect(fallbackPath);
|
|
211
|
+
}
|
|
212
|
+
export function assertResourceAccess(authorizer, authCtx, registry, meta, action, record) {
|
|
213
|
+
const ResourceClass = registry.resourceClass(meta.slug);
|
|
214
|
+
if (!ResourceClass)
|
|
215
|
+
return;
|
|
216
|
+
authorizer.assertResource(authCtx, ResourceClass, action, record);
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/shamar/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ShamarBranding {
|
|
2
|
+
brandName: string;
|
|
3
|
+
logoUrl?: string;
|
|
4
|
+
logoDarkUrl?: string;
|
|
5
|
+
copyrightText?: string;
|
|
6
|
+
fontFamily: string;
|
|
7
|
+
fontUrl?: string;
|
|
8
|
+
primaryColor: string;
|
|
9
|
+
accentColor: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const DEFAULT_SHAMAR_BRANDING: ShamarBranding;
|
|
12
|
+
export declare function mergeBranding(base: ShamarBranding, override?: Partial<ShamarBranding>): ShamarBranding;
|
|
13
|
+
export declare function resolveBranding(config?: {
|
|
14
|
+
name?: string;
|
|
15
|
+
logo?: string;
|
|
16
|
+
logoDark?: string;
|
|
17
|
+
copyright?: string;
|
|
18
|
+
fontFamily?: string;
|
|
19
|
+
fontUrl?: string;
|
|
20
|
+
primaryColor?: string;
|
|
21
|
+
accentColor?: string;
|
|
22
|
+
}): ShamarBranding;
|
|
23
|
+
export declare function buildBrandingCss(branding: ShamarBranding): string;
|
|
24
|
+
//# sourceMappingURL=branding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/shamar/branding.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,cAKrC,CAAC;AAEF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GACjC,cAAc,CAQhB;AAED,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,cAAc,CAWjB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAkDjE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export const DEFAULT_SHAMAR_BRANDING = {
|
|
2
|
+
brandName: 'Admin',
|
|
3
|
+
fontFamily: 'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
|
|
4
|
+
primaryColor: '#f1511b',
|
|
5
|
+
accentColor: '#286291',
|
|
6
|
+
};
|
|
7
|
+
export function mergeBranding(base, override) {
|
|
8
|
+
if (!override)
|
|
9
|
+
return { ...base };
|
|
10
|
+
return {
|
|
11
|
+
...base,
|
|
12
|
+
...Object.fromEntries(Object.entries(override).filter(([, value]) => value !== undefined && value !== '')),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function resolveBranding(config) {
|
|
16
|
+
return mergeBranding(DEFAULT_SHAMAR_BRANDING, {
|
|
17
|
+
brandName: config?.name,
|
|
18
|
+
logoUrl: config?.logo,
|
|
19
|
+
logoDarkUrl: config?.logoDark,
|
|
20
|
+
copyrightText: config?.copyright,
|
|
21
|
+
fontFamily: config?.fontFamily,
|
|
22
|
+
fontUrl: config?.fontUrl,
|
|
23
|
+
primaryColor: config?.primaryColor,
|
|
24
|
+
accentColor: config?.accentColor,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function buildBrandingCss(branding) {
|
|
28
|
+
const primary = normalizeHexColor(branding.primaryColor) ?? DEFAULT_SHAMAR_BRANDING.primaryColor;
|
|
29
|
+
const accent = normalizeHexColor(branding.accentColor) ?? DEFAULT_SHAMAR_BRANDING.accentColor;
|
|
30
|
+
const fontFamily = branding.fontFamily;
|
|
31
|
+
return `/* Shamar admin branding — generated at runtime */
|
|
32
|
+
:root {
|
|
33
|
+
--shamar-font-family: ${fontFamily};
|
|
34
|
+
--color-primary-500: ${primary};
|
|
35
|
+
--color-primary-50: color-mix(in srgb, ${primary} 10%, white);
|
|
36
|
+
--color-primary-100: color-mix(in srgb, ${primary} 20%, white);
|
|
37
|
+
--color-primary-200: color-mix(in srgb, ${primary} 35%, white);
|
|
38
|
+
--color-primary-300: color-mix(in srgb, ${primary} 55%, white);
|
|
39
|
+
--color-primary-400: color-mix(in srgb, ${primary} 78%, white);
|
|
40
|
+
--color-primary-600: color-mix(in srgb, ${primary} 88%, black);
|
|
41
|
+
--color-primary-700: color-mix(in srgb, ${primary} 75%, black);
|
|
42
|
+
--color-primary-800: color-mix(in srgb, ${primary} 62%, black);
|
|
43
|
+
--color-primary-900: color-mix(in srgb, ${primary} 48%, black);
|
|
44
|
+
--color-primary-950: color-mix(in srgb, ${primary} 32%, black);
|
|
45
|
+
--color-shamar-primary: ${primary};
|
|
46
|
+
--color-fg-brand: ${primary};
|
|
47
|
+
--color-fg-brand-strong: color-mix(in srgb, ${primary} 62%, black);
|
|
48
|
+
--color-brand-soft: color-mix(in srgb, ${primary} 20%, white);
|
|
49
|
+
--color-brand-softer: color-mix(in srgb, ${primary} 20%, white);
|
|
50
|
+
--color-default: color-mix(in srgb, ${primary} 35%, white);
|
|
51
|
+
--color-neutral-secondary: color-mix(in srgb, ${primary} 10%, white);
|
|
52
|
+
--color-neutral-tertiary: color-mix(in srgb, ${primary} 20%, white);
|
|
53
|
+
--color-shamar-shell: color-mix(in srgb, color-mix(in srgb, ${primary} 10%, white) 35%, white);
|
|
54
|
+
--color-shamar-accent: ${accent};
|
|
55
|
+
accent-color: var(--color-fg-brand);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.dark {
|
|
59
|
+
--color-fg-brand: ${primary};
|
|
60
|
+
--color-fg-brand-strong: color-mix(in srgb, ${primary} 78%, white);
|
|
61
|
+
--color-brand-soft: color-mix(in srgb, ${primary} 48%, black);
|
|
62
|
+
--color-brand-softer: color-mix(in srgb, ${primary} 32%, black);
|
|
63
|
+
--color-shamar-shell: var(--color-gray-800);
|
|
64
|
+
--color-neutral-primary: var(--color-gray-900);
|
|
65
|
+
--color-neutral-secondary: var(--color-gray-800);
|
|
66
|
+
--color-neutral-tertiary: #151c28;
|
|
67
|
+
--color-default: var(--color-gray-700);
|
|
68
|
+
accent-color: var(--color-fg-brand);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
body {
|
|
72
|
+
font-family: var(--shamar-font-family);
|
|
73
|
+
accent-color: var(--color-fg-brand);
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
function normalizeHexColor(value) {
|
|
78
|
+
const trimmed = value.trim();
|
|
79
|
+
const match = trimmed.match(/^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
|
|
80
|
+
if (!match)
|
|
81
|
+
return undefined;
|
|
82
|
+
const hex = match[1];
|
|
83
|
+
if (hex.length === 3) {
|
|
84
|
+
return `#${hex
|
|
85
|
+
.split('')
|
|
86
|
+
.map((char) => char + char)
|
|
87
|
+
.join('')}`;
|
|
88
|
+
}
|
|
89
|
+
return `#${hex.toLowerCase()}`;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=branding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding.js","sourceRoot":"","sources":["../../src/shamar/branding.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,uBAAuB,GAAmB;IACrD,SAAS,EAAE,OAAO;IAClB,UAAU,EAAE,6EAA6E;IACzF,YAAY,EAAE,SAAS;IACvB,WAAW,EAAE,SAAS;CACvB,CAAC;AAEF,MAAM,UAAU,aAAa,CAC3B,IAAoB,EACpB,QAAkC;IAElC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IAClC,OAAO;QACL,GAAG,IAAI;QACP,GAAG,MAAM,CAAC,WAAW,CACnB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CACpF;KACgB,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAS/B;IACC,OAAO,aAAa,CAAC,uBAAuB,EAAE;QAC5C,SAAS,EAAE,MAAM,EAAE,IAAI;QACvB,OAAO,EAAE,MAAM,EAAE,IAAI;QACrB,WAAW,EAAE,MAAM,EAAE,QAAQ;QAC7B,aAAa,EAAE,MAAM,EAAE,SAAS;QAChC,UAAU,EAAE,MAAM,EAAE,UAAU;QAC9B,OAAO,EAAE,MAAM,EAAE,OAAO;QACxB,YAAY,EAAE,MAAM,EAAE,YAAY;QAClC,WAAW,EAAE,MAAM,EAAE,WAAW;KACjC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAwB;IACvD,MAAM,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,uBAAuB,CAAC,YAAY,CAAC;IACjG,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,uBAAuB,CAAC,WAAW,CAAC;IAC9F,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IAEvC,OAAO;;0BAEiB,UAAU;yBACX,OAAO;2CACW,OAAO;4CACN,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4CACP,OAAO;4BACvB,OAAO;sBACb,OAAO;gDACmB,OAAO;2CACZ,OAAO;6CACL,OAAO;wCACZ,OAAO;kDACG,OAAO;iDACR,OAAO;gEACQ,OAAO;2BAC5C,MAAM;;;;;sBAKX,OAAO;gDACmB,OAAO;2CACZ,OAAO;6CACL,OAAO;;;;;;;;;;;;;CAanD,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACtB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,GAAG;aACX,KAAK,CAAC,EAAE,CAAC;aACT,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC;aAC1B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DataAdapter, ResourceMeta, ResourceRegistry } from '@shamar/core';
|
|
2
|
+
/**
|
|
3
|
+
* Hydrate relation dot-paths on records for table columns / infolist entries.
|
|
4
|
+
* e.g. `company.name` attaches `record.company = { name: 'Acme' }`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function hydrateRecordsForDisplay(meta: ResourceMeta, records: Record<string, unknown>[], registry: ResourceRegistry, adapter: DataAdapter): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=display-relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-relations.d.ts","sourceRoot":"","sources":["../../src/shamar/display-relations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAOhF;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { relationTitleAttribute, resolveRelationDisplayBindings, } from '@shamar/core';
|
|
2
|
+
/**
|
|
3
|
+
* Hydrate relation dot-paths on records for table columns / infolist entries.
|
|
4
|
+
* e.g. `company.name` attaches `record.company = { name: 'Acme' }`.
|
|
5
|
+
*/
|
|
6
|
+
export async function hydrateRecordsForDisplay(meta, records, registry, adapter) {
|
|
7
|
+
if (records.length === 0)
|
|
8
|
+
return;
|
|
9
|
+
const bindings = resolveRelationDisplayBindings(meta);
|
|
10
|
+
if (bindings.length === 0)
|
|
11
|
+
return;
|
|
12
|
+
const groups = groupBindingsByField(bindings);
|
|
13
|
+
const lookups = new Map();
|
|
14
|
+
for (const [groupKey, group] of groups) {
|
|
15
|
+
const ids = collectRelatedIds(records, group.binding);
|
|
16
|
+
if (ids.size === 0)
|
|
17
|
+
continue;
|
|
18
|
+
const relatedMeta = registry.require(group.binding.relation.resource);
|
|
19
|
+
const titleAttribute = relationTitleAttribute(group.binding.relation);
|
|
20
|
+
const results = await adapter.search(relatedMeta, {
|
|
21
|
+
ids: [...ids],
|
|
22
|
+
titleAttribute,
|
|
23
|
+
limit: Math.max(ids.size, 25),
|
|
24
|
+
});
|
|
25
|
+
const byId = new Map();
|
|
26
|
+
for (const row of results) {
|
|
27
|
+
const id = String(row.id);
|
|
28
|
+
const existing = byId.get(id) ?? { id };
|
|
29
|
+
for (const binding of group.bindings) {
|
|
30
|
+
existing[binding.attribute] =
|
|
31
|
+
binding.attribute === titleAttribute ? row.label : row.label;
|
|
32
|
+
}
|
|
33
|
+
byId.set(id, existing);
|
|
34
|
+
}
|
|
35
|
+
lookups.set(groupKey, byId);
|
|
36
|
+
}
|
|
37
|
+
for (const record of records) {
|
|
38
|
+
for (const [groupKey, group] of groups) {
|
|
39
|
+
const byId = lookups.get(groupKey);
|
|
40
|
+
if (!byId)
|
|
41
|
+
continue;
|
|
42
|
+
attachRelatedRecord(record, group.binding, byId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function groupBindingsByField(bindings) {
|
|
47
|
+
const groups = new Map();
|
|
48
|
+
for (const binding of bindings) {
|
|
49
|
+
const key = binding.fieldName;
|
|
50
|
+
const existing = groups.get(key);
|
|
51
|
+
if (existing) {
|
|
52
|
+
existing.bindings.push(binding);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
groups.set(key, { binding, bindings: [binding] });
|
|
56
|
+
}
|
|
57
|
+
return groups;
|
|
58
|
+
}
|
|
59
|
+
function collectRelatedIds(records, binding) {
|
|
60
|
+
const ids = new Set();
|
|
61
|
+
for (const record of records) {
|
|
62
|
+
const raw = record[binding.fieldName];
|
|
63
|
+
if (binding.relation.kind === 'belongsTo') {
|
|
64
|
+
if (raw != null && raw !== '')
|
|
65
|
+
ids.add(String(raw));
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(raw)) {
|
|
69
|
+
for (const item of raw) {
|
|
70
|
+
if (item != null && String(item).trim() !== '')
|
|
71
|
+
ids.add(String(item));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return ids;
|
|
76
|
+
}
|
|
77
|
+
function attachRelatedRecord(record, binding, byId) {
|
|
78
|
+
const raw = record[binding.fieldName];
|
|
79
|
+
if (binding.relation.kind === 'belongsTo') {
|
|
80
|
+
const id = raw != null && raw !== '' ? String(raw) : null;
|
|
81
|
+
record[binding.root] = id ? (byId.get(id) ?? null) : null;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const idList = Array.isArray(raw) ? raw.map((item) => String(item)).filter(Boolean) : [];
|
|
85
|
+
record[binding.root] = idList
|
|
86
|
+
.map((id) => byId.get(id))
|
|
87
|
+
.filter((item) => item != null);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=display-relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display-relations.js","sourceRoot":"","sources":["../../src/shamar/display-relations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ListFilter, ResourceMeta } from '@shamar/core';
|
|
2
|
+
export type ListHeaderFilterKind = 'boolean' | 'select' | 'm2o';
|
|
3
|
+
export type ListHeaderGroupKind = ListHeaderFilterKind;
|
|
4
|
+
export interface ListHeaderOption {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string | boolean | number;
|
|
7
|
+
id?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Toolbar filter/group metadata derived from table columns + form fields. */
|
|
10
|
+
export interface ListHeader {
|
|
11
|
+
name: string;
|
|
12
|
+
label: string;
|
|
13
|
+
filter_kind?: ListHeaderFilterKind;
|
|
14
|
+
group_kind?: ListHeaderGroupKind;
|
|
15
|
+
/** DB / filter field (e.g. companyId for company.name). */
|
|
16
|
+
filterField?: string;
|
|
17
|
+
options?: ListHeaderOption[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build toolbar headers for Filters / Group menus.
|
|
21
|
+
* Boolean columns, matching Select fields, and BelongsTo display columns are included by default.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildListHeaders(meta: ResourceMeta): ListHeader[];
|
|
24
|
+
export declare function parseListFilters(raw: unknown): ListFilter[];
|
|
25
|
+
/** Fill missing chip labels for default / URL filters using list headers. */
|
|
26
|
+
export declare function labelListFilters(filters: ListFilter[], headers: ListHeader[]): ListFilter[];
|
|
27
|
+
/**
|
|
28
|
+
* Resolve resource defaultFilters, mapping column names to filter fields when needed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveDefaultFilters(meta: ResourceMeta, headers: ListHeader[]): ListFilter[];
|
|
31
|
+
export interface ListGroupSection {
|
|
32
|
+
key: string;
|
|
33
|
+
label: string;
|
|
34
|
+
items: Record<string, unknown>[];
|
|
35
|
+
}
|
|
36
|
+
/** Group consecutive records for table rendering (caller should sort by groupBy first). */
|
|
37
|
+
export declare function groupRecordsForDisplay(items: Record<string, unknown>[], groupBy: string | undefined, headers: ListHeader[]): ListGroupSection[] | null;
|
|
38
|
+
//# sourceMappingURL=list-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-headers.d.ts","sourceRoot":"","sources":["../../src/shamar/list-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA6B,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGvF,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;AAC/D,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAA;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;IAChC,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IAChC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAyCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,EAAE,CAmEjE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,EAAE,CA8B3D;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,EAAE,UAAU,EAAE,GACpB,UAAU,EAAE,CAqBd;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,UAAU,EAAE,GACpB,UAAU,EAAE,CAed;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CACjC;AAED,2FAA2F;AAC3F,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAChC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,UAAU,EAAE,GACpB,gBAAgB,EAAE,GAAG,IAAI,CA8B3B"}
|