com-angel-authorization 1.0.2 → 1.0.3

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.
@@ -22,27 +22,27 @@ var vue_exports = {};
22
22
  __export(vue_exports, {
23
23
  Can: () => Can,
24
24
  PERMISSION_STORE_KEY: () => PERMISSION_STORE_KEY,
25
- PermissionStore: () => import_core2.PermissionStore,
26
- RESOURCE_PRIVATE_OPTIONS: () => import_resources2.RESOURCE_PRIVATE_OPTIONS,
27
- RESOURCE_STATUS_DISABLED: () => import_resources2.RESOURCE_STATUS_DISABLED,
28
- RESOURCE_STATUS_ENABLED: () => import_resources2.RESOURCE_STATUS_ENABLED,
29
- RESOURCE_STATUS_OPTIONS: () => import_resources2.RESOURCE_STATUS_OPTIONS,
30
- RESOURCE_TYPE_API: () => import_resources2.RESOURCE_TYPE_API,
25
+ PermissionStore: () => PermissionStore,
26
+ RESOURCE_PRIVATE_OPTIONS: () => RESOURCE_PRIVATE_OPTIONS,
27
+ RESOURCE_STATUS_DISABLED: () => RESOURCE_STATUS_DISABLED,
28
+ RESOURCE_STATUS_ENABLED: () => RESOURCE_STATUS_ENABLED,
29
+ RESOURCE_STATUS_OPTIONS: () => RESOURCE_STATUS_OPTIONS,
30
+ RESOURCE_TYPE_API: () => RESOURCE_TYPE_API,
31
31
  ResourceManager: () => ResourceManager,
32
- appendQueryParam: () => import_resources2.appendQueryParam,
33
- buildCreateBody: () => import_resources2.buildCreateBody,
34
- buildUpdateBody: () => import_resources2.buildUpdateBody,
35
- createAuthorizationResourceApi: () => import_resources2.createAuthorizationResourceApi,
36
- createDefaultResourceRequest: () => import_resources2.createDefaultResourceRequest,
32
+ appendQueryParam: () => appendQueryParam,
33
+ buildCreateBody: () => buildCreateBody,
34
+ buildUpdateBody: () => buildUpdateBody,
35
+ createAuthorizationResourceApi: () => createAuthorizationResourceApi,
36
+ createDefaultResourceRequest: () => createDefaultResourceRequest,
37
37
  createPermissionPlugin: () => createPermissionPlugin,
38
- createPermissionStore: () => import_core2.createPermissionStore,
38
+ createPermissionStore: () => createPermissionStore,
39
39
  createVPermission: () => createVPermission,
40
- extractPagination: () => import_resources2.extractPagination,
41
- extractRecords: () => import_resources2.extractRecords,
42
- getAppClientId: () => import_snowflake.getAppClientId,
43
- getDeviceWorkerId: () => import_snowflake.getDeviceWorkerId,
44
- mapAuthorizationResource: () => import_resources2.mapAuthorizationResource,
45
- snowyflake: () => import_snowflake.snowyflake,
40
+ extractPagination: () => extractPagination,
41
+ extractRecords: () => extractRecords,
42
+ getAppClientId: () => getAppClientId,
43
+ getDeviceWorkerId: () => getDeviceWorkerId,
44
+ mapAuthorizationResource: () => mapAuthorizationResource,
45
+ snowyflake: () => snowyflake,
46
46
  useHasPermission: () => useHasPermission,
47
47
  useHasRole: () => useHasRole,
48
48
  usePermission: () => usePermission
@@ -51,7 +51,110 @@ module.exports = __toCommonJS(vue_exports);
51
51
 
52
52
  // src/vue/plugin.ts
53
53
  var import_vue = require("vue");
54
- var import_core = require("../core");
54
+
55
+ // src/core/store.ts
56
+ var DEFAULT_STATE = {
57
+ permissions: [],
58
+ roles: [],
59
+ isSuperAdmin: false
60
+ };
61
+ function toArray(value) {
62
+ return Array.isArray(value) ? value : [value];
63
+ }
64
+ function matchCodes(owned, required, mode, isSuperAdmin) {
65
+ if (isSuperAdmin) return true;
66
+ if (required.length === 0) return true;
67
+ if (mode === "all") {
68
+ return required.every((code) => owned.has(code));
69
+ }
70
+ return required.some((code) => owned.has(code));
71
+ }
72
+ var PermissionStore = class {
73
+ constructor(initial) {
74
+ this.listeners = /* @__PURE__ */ new Set();
75
+ this.state = {
76
+ ...DEFAULT_STATE,
77
+ ...initial,
78
+ permissions: initial?.permissions ?? [],
79
+ roles: initial?.roles ?? []
80
+ };
81
+ this.permissionSet = new Set(this.state.permissions);
82
+ this.roleSet = new Set(this.state.roles);
83
+ }
84
+ getState() {
85
+ return {
86
+ permissions: [...this.state.permissions],
87
+ roles: [...this.state.roles],
88
+ isSuperAdmin: this.state.isSuperAdmin
89
+ };
90
+ }
91
+ setState(next) {
92
+ this.state = {
93
+ permissions: next.permissions ?? this.state.permissions,
94
+ roles: next.roles ?? this.state.roles,
95
+ isSuperAdmin: next.isSuperAdmin ?? this.state.isSuperAdmin
96
+ };
97
+ this.permissionSet = new Set(this.state.permissions);
98
+ this.roleSet = new Set(this.state.roles);
99
+ this.emit();
100
+ }
101
+ setPermissions(permissions) {
102
+ this.setState({ permissions });
103
+ }
104
+ setRoles(roles) {
105
+ this.setState({ roles });
106
+ }
107
+ setSuperAdmin(isSuperAdmin) {
108
+ this.setState({ isSuperAdmin });
109
+ }
110
+ /** Replace permissions, roles and super-admin flag in one shot (typical after login). */
111
+ hydrate(payload) {
112
+ this.setState({
113
+ permissions: payload.permissions ?? [],
114
+ roles: payload.roles ?? [],
115
+ isSuperAdmin: payload.isSuperAdmin ?? false
116
+ });
117
+ }
118
+ /** Clear all authz data (typical after logout). */
119
+ clear() {
120
+ this.hydrate({});
121
+ }
122
+ hasPermission(codes, options = {}) {
123
+ const mode = options.mode ?? "any";
124
+ return matchCodes(
125
+ this.permissionSet,
126
+ toArray(codes).filter(Boolean),
127
+ mode,
128
+ Boolean(this.state.isSuperAdmin)
129
+ );
130
+ }
131
+ hasRole(codes, options = {}) {
132
+ const mode = options.mode ?? "any";
133
+ return matchCodes(
134
+ this.roleSet,
135
+ toArray(codes).filter(Boolean),
136
+ mode,
137
+ Boolean(this.state.isSuperAdmin)
138
+ );
139
+ }
140
+ /** Alias of `hasPermission` for template-friendly APIs. */
141
+ can(codes, options) {
142
+ return this.hasPermission(codes, options);
143
+ }
144
+ subscribe(listener) {
145
+ this.listeners.add(listener);
146
+ return () => {
147
+ this.listeners.delete(listener);
148
+ };
149
+ }
150
+ emit() {
151
+ const snapshot = this.getState();
152
+ this.listeners.forEach((listener) => listener(snapshot));
153
+ }
154
+ };
155
+ function createPermissionStore(initial) {
156
+ return new PermissionStore(initial);
157
+ }
55
158
 
56
159
  // src/vue/directive.ts
57
160
  function parseBinding(binding) {
@@ -131,7 +234,7 @@ var PERMISSION_STORE_KEY = /* @__PURE__ */ Symbol(
131
234
  "angel-authorization-store"
132
235
  );
133
236
  function createPermissionPlugin(options = {}) {
134
- const store = options.store ?? (0, import_core.createPermissionStore)(options.initialState);
237
+ const store = options.store ?? createPermissionStore(options.initialState);
135
238
  const directiveName = options.directiveName ?? "permission";
136
239
  const plugin = {
137
240
  store,
@@ -243,13 +346,293 @@ var Can = (0, import_vue2.defineComponent)({
243
346
 
244
347
  // src/vue/ResourceManager.ts
245
348
  var import_vue3 = require("vue");
246
- var import_resources = require("../resources");
349
+
350
+ // src/resources/types.ts
351
+ var RESOURCE_TYPE_API = "api";
352
+ var RESOURCE_STATUS_ENABLED = 1;
353
+ var RESOURCE_STATUS_DISABLED = 0;
354
+ var RESOURCE_STATUS_OPTIONS = [
355
+ { label: "\u542F\u7528", value: RESOURCE_STATUS_ENABLED },
356
+ { label: "\u7981\u7528", value: RESOURCE_STATUS_DISABLED }
357
+ ];
358
+ var RESOURCE_PRIVATE_OPTIONS = [
359
+ { label: "\u662F", value: true },
360
+ { label: "\u5426", value: false }
361
+ ];
362
+
363
+ // src/utils/snowflake.ts
364
+ var DEVICE_WORKER_ID_KEY = "hgams_device_worker_id";
365
+ function generateDeviceWorkerId() {
366
+ const cached = localStorage.getItem(DEVICE_WORKER_ID_KEY);
367
+ if (cached !== null && cached !== "") {
368
+ return BigInt(cached);
369
+ }
370
+ const components = [
371
+ navigator.userAgent,
372
+ screen.width + "x" + screen.height,
373
+ screen.colorDepth,
374
+ navigator.language,
375
+ (/* @__PURE__ */ new Date()).getTimezoneOffset(),
376
+ navigator.hardwareConcurrency,
377
+ navigator.deviceMemory
378
+ ];
379
+ const fingerprint = components.join("|");
380
+ let hash = 0;
381
+ for (let i = 0; i < fingerprint.length; i++) {
382
+ hash = (hash << 5) - hash + fingerprint.charCodeAt(i);
383
+ hash |= 0;
384
+ }
385
+ const maxWorkerId = Math.pow(2, 14) - 1;
386
+ const workerId = Math.abs(hash % maxWorkerId);
387
+ const workerIdBigInt = BigInt(workerId);
388
+ localStorage.setItem(DEVICE_WORKER_ID_KEY, workerIdBigInt.toString());
389
+ return workerIdBigInt;
390
+ }
391
+ var ConfigurableSnowflake = class {
392
+ constructor({
393
+ epoch = 0n,
394
+ workerId = 0n,
395
+ processId = 0n,
396
+ workerIdBits = 5,
397
+ processIdBits = 5,
398
+ sequenceBits = 12
399
+ } = {}) {
400
+ this.sequence = 0n;
401
+ this.latestTimestamp = BigInt(Date.now());
402
+ this.epoch = epoch;
403
+ const workerIdBitsBig = BigInt(workerIdBits);
404
+ const processIdBitsBig = BigInt(processIdBits);
405
+ const sequenceBitsBig = BigInt(sequenceBits);
406
+ const workerIdMask = -1n ^ -1n << workerIdBitsBig;
407
+ const processIdMask = -1n ^ -1n << processIdBitsBig;
408
+ this.sequenceMask = -1n ^ -1n << sequenceBitsBig;
409
+ this.timestampLeftShift = sequenceBitsBig + workerIdBitsBig + processIdBitsBig;
410
+ this.workerIdShift = sequenceBitsBig + processIdBitsBig;
411
+ this.processIdShift = sequenceBitsBig;
412
+ this.workerId = workerId & workerIdMask;
413
+ this.processId = processId & processIdMask;
414
+ }
415
+ nextId() {
416
+ const timestamp = BigInt(Date.now());
417
+ if (this.latestTimestamp === timestamp) {
418
+ this.sequence = this.sequence + 1n & this.sequenceMask;
419
+ } else {
420
+ this.sequence = 0n;
421
+ this.latestTimestamp = timestamp;
422
+ }
423
+ return timestamp - this.epoch << this.timestampLeftShift | this.workerId << this.workerIdShift | this.processId << this.processIdShift | this.sequence;
424
+ }
425
+ };
426
+ var snowyflake = new ConfigurableSnowflake({
427
+ workerId: generateDeviceWorkerId(),
428
+ workerIdBits: 14,
429
+ processIdBits: 0,
430
+ sequenceBits: 8,
431
+ epoch: BigInt((/* @__PURE__ */ new Date("2026-06-08")).getTime())
432
+ });
433
+ function getAppClientId() {
434
+ return snowyflake.nextId().toString();
435
+ }
436
+ function getDeviceWorkerId() {
437
+ return snowyflake.workerId.toString();
438
+ }
439
+
440
+ // src/resources/pagination.ts
441
+ function appendQueryParam(parts, key, value) {
442
+ if (value !== void 0 && value !== null && String(value) !== "") {
443
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
444
+ }
445
+ }
446
+ function extractPagination(payload) {
447
+ if (!payload || typeof payload !== "object") {
448
+ return {
449
+ pageToken: "",
450
+ hasPreviousPage: false,
451
+ hasNextPage: false
452
+ };
453
+ }
454
+ const envelope = payload;
455
+ const pagination = envelope.pagination && typeof envelope.pagination === "object" ? envelope.pagination : envelope.page && typeof envelope.page === "object" ? envelope.page : null;
456
+ const rawPageToken = pagination?.["page-token"] ?? pagination?.pageToken ?? pagination?.nextPageToken ?? envelope["page-token"] ?? envelope.pageToken ?? "";
457
+ const pageToken = rawPageToken === void 0 || rawPageToken === null ? "" : String(rawPageToken);
458
+ const hasPreviousPage = Boolean(
459
+ pagination?.hasPreviousPage ?? pagination?.hasPrevious ?? pagination?.hasPre ?? pagination?.previous ?? envelope.hasPreviousPage ?? envelope.hasPrevious
460
+ );
461
+ const hasNextPage = Boolean(
462
+ pagination?.hasNextPage ?? pagination?.hasNext ?? pagination?.next ?? envelope.hasNextPage ?? envelope.hasNext
463
+ );
464
+ return {
465
+ pageToken,
466
+ hasPreviousPage,
467
+ hasNextPage
468
+ };
469
+ }
470
+ function extractRecords(payload) {
471
+ if (!payload) return [];
472
+ if (Array.isArray(payload)) return payload;
473
+ if (typeof payload === "object") {
474
+ const envelope = payload;
475
+ const data = envelope.data;
476
+ if (Array.isArray(data)) return data;
477
+ if (data && typeof data === "object") {
478
+ const nested = data;
479
+ if (Array.isArray(nested.records)) return nested.records;
480
+ if (Array.isArray(nested.items)) return nested.items;
481
+ if (Array.isArray(nested.list)) return nested.list;
482
+ if (Array.isArray(nested.content)) return nested.content;
483
+ }
484
+ if (Array.isArray(envelope.records)) return envelope.records;
485
+ if (Array.isArray(envelope.items)) return envelope.items;
486
+ if (Array.isArray(envelope.list)) return envelope.list;
487
+ if (Array.isArray(envelope.content)) return envelope.content;
488
+ }
489
+ return [];
490
+ }
491
+
492
+ // src/resources/api.ts
493
+ function toResourceStatus(value) {
494
+ const n = Number(value);
495
+ return n === RESOURCE_STATUS_ENABLED ? RESOURCE_STATUS_ENABLED : RESOURCE_STATUS_DISABLED;
496
+ }
497
+ function toBoolean(value) {
498
+ if (typeof value === "boolean") return value;
499
+ if (typeof value === "number") return value !== 0;
500
+ if (typeof value === "string") {
501
+ const v = value.trim().toLowerCase();
502
+ return v === "true" || v === "1" || v === "yes" || v === "\u662F";
503
+ }
504
+ return false;
505
+ }
506
+ function mapAuthorizationResource(item) {
507
+ if (!item || typeof item !== "object") return null;
508
+ const row = item;
509
+ const resourceId = row.resourceId ?? row.resource_id ?? row.id;
510
+ if (resourceId === void 0 || resourceId === null || resourceId === "") return null;
511
+ return {
512
+ resourceId: String(resourceId),
513
+ type: RESOURCE_TYPE_API,
514
+ name: String(row.name ?? ""),
515
+ identification: String(row.identification ?? row.identity ?? ""),
516
+ status: toResourceStatus(row.status),
517
+ private: toBoolean(row.private ?? row.isPrivate ?? row.is_private),
518
+ description: String(row.description ?? row.desc ?? "")
519
+ };
520
+ }
521
+ function buildListUrl(params) {
522
+ const parts = [];
523
+ appendQueryParam(parts, "type", RESOURCE_TYPE_API);
524
+ appendQueryParam(parts, "page-token", params?.pagination?.pageToken ?? "");
525
+ appendQueryParam(parts, "page-size", params?.pagination?.pageSize ?? "20");
526
+ appendQueryParam(parts, "page-direction", params?.pagination?.pageDirection ?? "current");
527
+ return `/authorization-resources?${parts.join("&")}`;
528
+ }
529
+ function buildCreateBody(values, resourceId = getAppClientId()) {
530
+ return {
531
+ resourceId,
532
+ type: RESOURCE_TYPE_API,
533
+ name: values.name.trim(),
534
+ identification: values.identification.trim(),
535
+ status: values.status,
536
+ private: values.private,
537
+ description: values.description.trim()
538
+ };
539
+ }
540
+ function buildUpdateBody(values) {
541
+ return {
542
+ type: RESOURCE_TYPE_API,
543
+ name: values.name.trim(),
544
+ identification: values.identification.trim(),
545
+ status: values.status,
546
+ private: values.private,
547
+ description: values.description.trim()
548
+ };
549
+ }
550
+ function createAuthorizationResourceApi(request) {
551
+ return {
552
+ async list(params, signal) {
553
+ const json = await request({
554
+ method: "GET",
555
+ url: buildListUrl(params),
556
+ signal
557
+ });
558
+ const records = extractRecords(json).map(mapAuthorizationResource).filter((item) => item !== null);
559
+ const pagination = extractPagination(json);
560
+ return {
561
+ records,
562
+ ...pagination
563
+ };
564
+ },
565
+ async create(values, signal) {
566
+ return request({
567
+ method: "POST",
568
+ url: "/authorization-resources",
569
+ body: buildCreateBody(values),
570
+ signal
571
+ });
572
+ },
573
+ async update(resourceId, values, signal) {
574
+ return request({
575
+ method: "PATCH",
576
+ url: `/authorization-resources/${encodeURIComponent(resourceId)}`,
577
+ body: buildUpdateBody(values),
578
+ signal
579
+ });
580
+ },
581
+ async remove(resourceId, signal) {
582
+ return request({
583
+ method: "DELETE",
584
+ url: `/authorization-resources/${encodeURIComponent(resourceId)}`,
585
+ signal
586
+ });
587
+ }
588
+ };
589
+ }
590
+ function createDefaultResourceRequest(options = {}) {
591
+ const baseUrl = (options.baseUrl ?? "").replace(/\/+$/, "");
592
+ const credentials = options.credentials ?? "include";
593
+ return async function request({
594
+ method,
595
+ url,
596
+ body,
597
+ signal
598
+ }) {
599
+ const path = url.startsWith("http") ? url : `${baseUrl}${url.startsWith("/") ? url : `/${url}`}`;
600
+ const headers = {
601
+ Accept: "application/json",
602
+ ...await options.getHeaders?.()
603
+ };
604
+ let payload;
605
+ if (body !== void 0) {
606
+ headers["Content-Type"] = "application/json";
607
+ payload = JSON.stringify(body);
608
+ }
609
+ const response = await fetch(path, {
610
+ method,
611
+ headers,
612
+ body: payload,
613
+ credentials,
614
+ signal
615
+ });
616
+ if (!response.ok) {
617
+ const text2 = await response.text().catch(() => "");
618
+ throw new Error(text2 || `HTTP ${response.status}`);
619
+ }
620
+ if (response.status === 204) {
621
+ return void 0;
622
+ }
623
+ const text = await response.text();
624
+ if (!text) return void 0;
625
+ return JSON.parse(text);
626
+ };
627
+ }
628
+
629
+ // src/vue/ResourceManager.ts
247
630
  var PAGE_SIZE_OPTIONS = ["10", "20", "50", "100"];
248
631
  function emptyForm() {
249
632
  return {
250
633
  name: "",
251
634
  identification: "",
252
- status: import_resources.RESOURCE_STATUS_ENABLED,
635
+ status: RESOURCE_STATUS_ENABLED,
253
636
  private: false,
254
637
  description: ""
255
638
  };
@@ -445,7 +828,7 @@ var ResourceManager = (0, import_vue3.defineComponent)({
445
828
  const form = (0, import_vue3.reactive)(emptyForm());
446
829
  const saving = (0, import_vue3.ref)(false);
447
830
  const statusLabel = Object.fromEntries(
448
- import_resources.RESOURCE_STATUS_OPTIONS.map((o) => [o.value, o.label])
831
+ RESOURCE_STATUS_OPTIONS.map((o) => [o.value, o.label])
449
832
  );
450
833
  async function loadList(direction = "current", token = pageToken.value) {
451
834
  loading.value = true;
@@ -580,8 +963,8 @@ var ResourceManager = (0, import_vue3.defineComponent)({
580
963
  {
581
964
  style: {
582
965
  ...s.badge,
583
- background: row.status === import_resources.RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
584
- color: row.status === import_resources.RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
966
+ background: row.status === RESOURCE_STATUS_ENABLED ? "#e8f7ef" : "#f4f4f5",
967
+ color: row.status === RESOURCE_STATUS_ENABLED ? "#067647" : "#667085"
585
968
  }
586
969
  },
587
970
  statusLabel[row.status] ?? String(row.status)
@@ -726,7 +1109,7 @@ var ResourceManager = (0, import_vue3.defineComponent)({
726
1109
  );
727
1110
  }
728
1111
  },
729
- import_resources.RESOURCE_STATUS_OPTIONS.map(
1112
+ RESOURCE_STATUS_OPTIONS.map(
730
1113
  (opt) => (0, import_vue3.h)("option", { key: opt.value, value: opt.value }, opt.label)
731
1114
  )
732
1115
  )
@@ -742,7 +1125,7 @@ var ResourceManager = (0, import_vue3.defineComponent)({
742
1125
  form.private = e.target.value === "1";
743
1126
  }
744
1127
  },
745
- import_resources.RESOURCE_PRIVATE_OPTIONS.map(
1128
+ RESOURCE_PRIVATE_OPTIONS.map(
746
1129
  (opt) => (0, import_vue3.h)(
747
1130
  "option",
748
1131
  {
@@ -798,11 +1181,6 @@ var ResourceManager = (0, import_vue3.defineComponent)({
798
1181
  ]);
799
1182
  }
800
1183
  });
801
-
802
- // src/vue/index.ts
803
- var import_core2 = require("../core");
804
- var import_snowflake = require("../utils/snowflake");
805
- var import_resources2 = require("../resources");
806
1184
  // Annotate the CommonJS export names for ESM import in node:
807
1185
  0 && (module.exports = {
808
1186
  Can,