@mj-biz-apps/issues-core 0.0.1 → 1.0.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/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/services/IssueEngine.d.ts +57 -0
- package/dist/services/IssueEngine.d.ts.map +1 -0
- package/dist/services/IssueEngine.js +119 -0
- package/dist/services/IssueEngine.js.map +1 -0
- package/dist/services/IssueService.d.ts +66 -0
- package/dist/services/IssueService.d.ts.map +1 -0
- package/dist/services/IssueService.js +120 -0
- package/dist/services/IssueService.js.map +1 -0
- package/dist/services/IssueWorkService.d.ts +39 -0
- package/dist/services/IssueWorkService.d.ts.map +1 -0
- package/dist/services/IssueWorkService.js +69 -0
- package/dist/services/IssueWorkService.js.map +1 -0
- package/package.json +38 -7
- package/README.md +0 -45
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mj-biz-apps/issues-core
|
|
3
|
+
*
|
|
4
|
+
* Core logic for the BizApps Issues Open App:
|
|
5
|
+
* - IssueEngine — cached/reactive lookup of IssueTypes & IssueStatuses
|
|
6
|
+
* - IssueService — create / triage / transition / close, fires IssueType action hooks
|
|
7
|
+
* - IssueWorkService — spawns linked bizapps-tasks Tasks via TaskLink
|
|
8
|
+
*
|
|
9
|
+
* Server-side entity subclasses (lifecycle hooks) live in the separate
|
|
10
|
+
* @mj-biz-apps/issues-core-entities-server package.
|
|
11
|
+
*/
|
|
12
|
+
export * from './services/IssueEngine.js';
|
|
13
|
+
export * from './services/IssueService.js';
|
|
14
|
+
export * from './services/IssueWorkService.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mj-biz-apps/issues-core
|
|
3
|
+
*
|
|
4
|
+
* Core logic for the BizApps Issues Open App:
|
|
5
|
+
* - IssueEngine — cached/reactive lookup of IssueTypes & IssueStatuses
|
|
6
|
+
* - IssueService — create / triage / transition / close, fires IssueType action hooks
|
|
7
|
+
* - IssueWorkService — spawns linked bizapps-tasks Tasks via TaskLink
|
|
8
|
+
*
|
|
9
|
+
* Server-side entity subclasses (lifecycle hooks) live in the separate
|
|
10
|
+
* @mj-biz-apps/issues-core-entities-server package.
|
|
11
|
+
*/
|
|
12
|
+
export * from './services/IssueEngine.js';
|
|
13
|
+
export * from './services/IssueService.js';
|
|
14
|
+
export * from './services/IssueWorkService.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { BaseEngine, IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { mjBizAppsIssuesIssueTypeEntity, mjBizAppsIssuesIssueStatusEntity } from '@mj-biz-apps/issues-entities';
|
|
4
|
+
/**
|
|
5
|
+
* IssueEngine is a singleton engine providing centralized, cached, reactive access to the
|
|
6
|
+
* BizApps Issues reference data: Issue Types and Issue Statuses. Both are small, unfiltered
|
|
7
|
+
* lookup sets that drive issue classification and the workflow board, so they are fully
|
|
8
|
+
* cached in memory.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the canonical MJ BaseEngine pattern (see ApplicationSettingEngine / UserInfoEngine):
|
|
11
|
+
* the BaseEnginePropertyConfig entries auto-subscribe to BaseEntity events, so the caches stay
|
|
12
|
+
* fresh on save/delete/remote-invalidate and the `$` observables re-emit automatically — no
|
|
13
|
+
* hand-written invalidation.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const engine = IssueEngine.Instance;
|
|
18
|
+
* await engine.Config(false, contextUser); // lazy, no-op once loaded
|
|
19
|
+
* const newStatus = engine.DefaultStatus; // the IsDefault status
|
|
20
|
+
* const bug = engine.IssueTypeByName('Bug');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class IssueEngine extends BaseEngine<IssueEngine> {
|
|
24
|
+
static get Instance(): IssueEngine;
|
|
25
|
+
private _IssueTypes;
|
|
26
|
+
private _IssueStatuses;
|
|
27
|
+
/**
|
|
28
|
+
* Loads Issue Types and Issue Statuses into the in-memory cache. Lazy and idempotent —
|
|
29
|
+
* callers invoke this at entry and it is a no-op once loaded.
|
|
30
|
+
*/
|
|
31
|
+
Config(forceRefresh?: boolean, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<void>;
|
|
32
|
+
/** Observable stream of the cached Issue Types; re-emits on any change. */
|
|
33
|
+
get IssueTypes$(): Observable<mjBizAppsIssuesIssueTypeEntity[]>;
|
|
34
|
+
/** Observable stream of the cached Issue Statuses; re-emits on any change. */
|
|
35
|
+
get IssueStatuses$(): Observable<mjBizAppsIssuesIssueStatusEntity[]>;
|
|
36
|
+
/** All cached Issue Types. */
|
|
37
|
+
get IssueTypes(): mjBizAppsIssuesIssueTypeEntity[];
|
|
38
|
+
/** All cached Issue Statuses. */
|
|
39
|
+
get IssueStatuses(): mjBizAppsIssuesIssueStatusEntity[];
|
|
40
|
+
/** Issue Statuses ordered by Sequence (board column order). */
|
|
41
|
+
get OrderedStatuses(): mjBizAppsIssuesIssueStatusEntity[];
|
|
42
|
+
/** The default status applied to new issues (the IsDefault row), or undefined. */
|
|
43
|
+
get DefaultStatus(): mjBizAppsIssuesIssueStatusEntity | undefined;
|
|
44
|
+
/** Look up an Issue Type by ID. */
|
|
45
|
+
IssueTypeByID(id: string): mjBizAppsIssuesIssueTypeEntity | undefined;
|
|
46
|
+
/** Look up an Issue Type by exact Name. */
|
|
47
|
+
IssueTypeByName(name: string): mjBizAppsIssuesIssueTypeEntity | undefined;
|
|
48
|
+
/** Look up an Issue Status by ID. */
|
|
49
|
+
IssueStatusByID(id: string): mjBizAppsIssuesIssueStatusEntity | undefined;
|
|
50
|
+
/** Look up an Issue Status by exact Name. */
|
|
51
|
+
IssueStatusByName(name: string): mjBizAppsIssuesIssueStatusEntity | undefined;
|
|
52
|
+
/** True if the given status ID is a terminal (closed/end) state. */
|
|
53
|
+
IsTerminalStatus(statusID: string): boolean;
|
|
54
|
+
/** True if the given status ID is the resolved-but-not-closed state. */
|
|
55
|
+
IsResolvedStatus(statusID: string): boolean;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=IssueEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueEngine.d.ts","sourceRoot":"","sources":["../../src/services/IssueEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,iBAAiB,EAEjB,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,EACL,8BAA8B,EAC9B,gCAAgC,EACjC,MAAM,8BAA8B,CAAC;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,WAAY,SAAQ,UAAU,CAAC,WAAW,CAAC;IACtD,WAAkB,QAAQ,IAAI,WAAW,CAExC;IAED,OAAO,CAAC,WAAW,CAAwC;IAC3D,OAAO,CAAC,cAAc,CAA0C;IAEhE;;;OAGG;IACU,MAAM,CACjB,YAAY,CAAC,EAAE,OAAO,EACtB,WAAW,CAAC,EAAE,QAAQ,EACtB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAuBhB,2EAA2E;IAC3E,IAAW,WAAW,IAAI,UAAU,CAAC,8BAA8B,EAAE,CAAC,CAErE;IAED,8EAA8E;IAC9E,IAAW,cAAc,IAAI,UAAU,CAAC,gCAAgC,EAAE,CAAC,CAE1E;IAMD,8BAA8B;IAC9B,IAAW,UAAU,IAAI,8BAA8B,EAAE,CAExD;IAED,iCAAiC;IACjC,IAAW,aAAa,IAAI,gCAAgC,EAAE,CAE7D;IAED,+DAA+D;IAC/D,IAAW,eAAe,IAAI,gCAAgC,EAAE,CAE/D;IAED,kFAAkF;IAClF,IAAW,aAAa,IAAI,gCAAgC,GAAG,SAAS,CAEvE;IAED,mCAAmC;IAC5B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,8BAA8B,GAAG,SAAS;IAI5E,2CAA2C;IACpC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,8BAA8B,GAAG,SAAS;IAKhF,qCAAqC;IAC9B,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,gCAAgC,GAAG,SAAS;IAIhF,6CAA6C;IACtC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,gCAAgC,GAAG,SAAS;IAKpF,oEAAoE;IAC7D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIlD,wEAAwE;IACjE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGnD"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { BaseEngine, RegisterForStartup, } from '@memberjunction/core';
|
|
8
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
9
|
+
/**
|
|
10
|
+
* IssueEngine is a singleton engine providing centralized, cached, reactive access to the
|
|
11
|
+
* BizApps Issues reference data: Issue Types and Issue Statuses. Both are small, unfiltered
|
|
12
|
+
* lookup sets that drive issue classification and the workflow board, so they are fully
|
|
13
|
+
* cached in memory.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors the canonical MJ BaseEngine pattern (see ApplicationSettingEngine / UserInfoEngine):
|
|
16
|
+
* the BaseEnginePropertyConfig entries auto-subscribe to BaseEntity events, so the caches stay
|
|
17
|
+
* fresh on save/delete/remote-invalidate and the `$` observables re-emit automatically — no
|
|
18
|
+
* hand-written invalidation.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const engine = IssueEngine.Instance;
|
|
23
|
+
* await engine.Config(false, contextUser); // lazy, no-op once loaded
|
|
24
|
+
* const newStatus = engine.DefaultStatus; // the IsDefault status
|
|
25
|
+
* const bug = engine.IssueTypeByName('Bug');
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
let IssueEngine = class IssueEngine extends BaseEngine {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
this._IssueTypes = [];
|
|
32
|
+
this._IssueStatuses = [];
|
|
33
|
+
}
|
|
34
|
+
static get Instance() {
|
|
35
|
+
return super.getInstance();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Loads Issue Types and Issue Statuses into the in-memory cache. Lazy and idempotent —
|
|
39
|
+
* callers invoke this at entry and it is a no-op once loaded.
|
|
40
|
+
*/
|
|
41
|
+
async Config(forceRefresh, contextUser, provider) {
|
|
42
|
+
const configs = [
|
|
43
|
+
{
|
|
44
|
+
Type: 'entity',
|
|
45
|
+
EntityName: 'MJ_BizApps_Issues: Issue Types',
|
|
46
|
+
PropertyName: '_IssueTypes',
|
|
47
|
+
CacheLocal: true,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
Type: 'entity',
|
|
51
|
+
EntityName: 'MJ_BizApps_Issues: Issue Status',
|
|
52
|
+
PropertyName: '_IssueStatuses',
|
|
53
|
+
CacheLocal: true,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
await super.Load(configs, provider, forceRefresh, contextUser);
|
|
57
|
+
}
|
|
58
|
+
// ========================================================================
|
|
59
|
+
// OBSERVABLE ACCESSORS
|
|
60
|
+
// ========================================================================
|
|
61
|
+
/** Observable stream of the cached Issue Types; re-emits on any change. */
|
|
62
|
+
get IssueTypes$() {
|
|
63
|
+
return this.ObserveProperty('_IssueTypes');
|
|
64
|
+
}
|
|
65
|
+
/** Observable stream of the cached Issue Statuses; re-emits on any change. */
|
|
66
|
+
get IssueStatuses$() {
|
|
67
|
+
return this.ObserveProperty('_IssueStatuses');
|
|
68
|
+
}
|
|
69
|
+
// ========================================================================
|
|
70
|
+
// PUBLIC ACCESSORS
|
|
71
|
+
// ========================================================================
|
|
72
|
+
/** All cached Issue Types. */
|
|
73
|
+
get IssueTypes() {
|
|
74
|
+
return this._IssueTypes ?? [];
|
|
75
|
+
}
|
|
76
|
+
/** All cached Issue Statuses. */
|
|
77
|
+
get IssueStatuses() {
|
|
78
|
+
return this._IssueStatuses ?? [];
|
|
79
|
+
}
|
|
80
|
+
/** Issue Statuses ordered by Sequence (board column order). */
|
|
81
|
+
get OrderedStatuses() {
|
|
82
|
+
return [...this.IssueStatuses].sort((a, b) => a.Sequence - b.Sequence);
|
|
83
|
+
}
|
|
84
|
+
/** The default status applied to new issues (the IsDefault row), or undefined. */
|
|
85
|
+
get DefaultStatus() {
|
|
86
|
+
return this.IssueStatuses.find((s) => s.IsDefault);
|
|
87
|
+
}
|
|
88
|
+
/** Look up an Issue Type by ID. */
|
|
89
|
+
IssueTypeByID(id) {
|
|
90
|
+
return this.IssueTypes.find((t) => UUIDsEqual(t.ID, id));
|
|
91
|
+
}
|
|
92
|
+
/** Look up an Issue Type by exact Name. */
|
|
93
|
+
IssueTypeByName(name) {
|
|
94
|
+
const target = name.trim().toLowerCase();
|
|
95
|
+
return this.IssueTypes.find((t) => t.Name.trim().toLowerCase() === target);
|
|
96
|
+
}
|
|
97
|
+
/** Look up an Issue Status by ID. */
|
|
98
|
+
IssueStatusByID(id) {
|
|
99
|
+
return this.IssueStatuses.find((s) => UUIDsEqual(s.ID, id));
|
|
100
|
+
}
|
|
101
|
+
/** Look up an Issue Status by exact Name. */
|
|
102
|
+
IssueStatusByName(name) {
|
|
103
|
+
const target = name.trim().toLowerCase();
|
|
104
|
+
return this.IssueStatuses.find((s) => s.Name.trim().toLowerCase() === target);
|
|
105
|
+
}
|
|
106
|
+
/** True if the given status ID is a terminal (closed/end) state. */
|
|
107
|
+
IsTerminalStatus(statusID) {
|
|
108
|
+
return this.IssueStatusByID(statusID)?.IsTerminal ?? false;
|
|
109
|
+
}
|
|
110
|
+
/** True if the given status ID is the resolved-but-not-closed state. */
|
|
111
|
+
IsResolvedStatus(statusID) {
|
|
112
|
+
return this.IssueStatusByID(statusID)?.IsResolved ?? false;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
IssueEngine = __decorate([
|
|
116
|
+
RegisterForStartup()
|
|
117
|
+
], IssueEngine);
|
|
118
|
+
export { IssueEngine };
|
|
119
|
+
//# sourceMappingURL=IssueEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueEngine.js","sourceRoot":"","sources":["../../src/services/IssueEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { mjBizAppsIssuesIssueEntity } from '@mj-biz-apps/issues-entities';
|
|
3
|
+
/** Severity / Priority enum shared by Issue and IssueType. */
|
|
4
|
+
export type IssuePriority = 'Low' | 'Medium' | 'High' | 'Critical';
|
|
5
|
+
/** Parameters for creating a new Issue. */
|
|
6
|
+
export interface CreateIssueParams {
|
|
7
|
+
Title: string;
|
|
8
|
+
Description?: string | null;
|
|
9
|
+
/** IssueType — by ID or Name. If omitted, the caller must supply IssueTypeID. */
|
|
10
|
+
IssueTypeID?: string;
|
|
11
|
+
IssueTypeName?: string;
|
|
12
|
+
/** Optional explicit status; defaults to the IsDefault status when omitted. */
|
|
13
|
+
StatusID?: string;
|
|
14
|
+
Severity?: IssuePriority;
|
|
15
|
+
/** Defaults to the IssueType's DefaultPriority when omitted. */
|
|
16
|
+
Priority?: IssuePriority;
|
|
17
|
+
ReporterPersonID?: string | null;
|
|
18
|
+
ReporterEmail?: string | null;
|
|
19
|
+
SourceEntityID?: string | null;
|
|
20
|
+
SourceRecordID?: string | null;
|
|
21
|
+
AppScope?: string | null;
|
|
22
|
+
CreatedByPersonID?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convenience service for the common issue operations: create, transition status,
|
|
26
|
+
* assign, and close.
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT — this service is a THIN convenience layer. The authoritative lifecycle
|
|
29
|
+
* side-effects (IssueNumber assignment, ResolvedAt/ClosedAt stamping, and firing the
|
|
30
|
+
* IssueType OnCreate/OnStatusChange/OnClose/OnAssign action hooks) live in
|
|
31
|
+
* `IssueEntityServer.Save()` so they fire on EVERY save path — including a plain
|
|
32
|
+
* `entity.Save()` from the UI/API/automation that never touches this service. These
|
|
33
|
+
* methods just set the right field(s) and call Save(); the entity does the rest.
|
|
34
|
+
*
|
|
35
|
+
* Server-side: always pass contextUser.
|
|
36
|
+
*/
|
|
37
|
+
export declare class IssueService {
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new Issue. Resolves the IssueType (by ID or name) and defaults status to
|
|
40
|
+
* the IsDefault status and priority to the type's DefaultPriority, then saves. The
|
|
41
|
+
* entity server assigns IssueNumber, stamps lifecycle timestamps, and fires OnCreate.
|
|
42
|
+
* Returns the saved entity, or null on failure (details logged).
|
|
43
|
+
*/
|
|
44
|
+
CreateIssue(params: CreateIssueParams, contextUser: UserInfo): Promise<mjBizAppsIssuesIssueEntity | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Transitions an issue to a new status by setting StatusID and saving. The entity
|
|
47
|
+
* server stamps ResolvedAt/ClosedAt and fires OnStatusChange (+ OnClose on terminal).
|
|
48
|
+
* No-ops when the status is unchanged. Returns true on success.
|
|
49
|
+
*/
|
|
50
|
+
TransitionStatus(issue: mjBizAppsIssuesIssueEntity, newStatusID: string, contextUser: UserInfo): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Assigns an issue to a polymorphic assignee (Person or AI Agent) and saves. The
|
|
53
|
+
* entity server fires OnAssign. Pass null/null to clear the assignee. Returns true
|
|
54
|
+
* on success.
|
|
55
|
+
*/
|
|
56
|
+
Assign(issue: mjBizAppsIssuesIssueEntity, assigneeEntityID: string | null, assigneeRecordID: string | null, contextUser: UserInfo): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Closes an issue by transitioning it to the supplied terminal status (or the first
|
|
59
|
+
* terminal status if none given). The entity server stamps ClosedAt and fires
|
|
60
|
+
* OnStatusChange + OnClose.
|
|
61
|
+
*/
|
|
62
|
+
Close(issue: mjBizAppsIssuesIssueEntity, contextUser: UserInfo, terminalStatusID?: string): Promise<boolean>;
|
|
63
|
+
/** Resolves the IssueType from explicit ID or name via the engine cache. */
|
|
64
|
+
private resolveIssueType;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=IssueService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueService.d.ts","sourceRoot":"","sources":["../../src/services/IssueService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAY,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EACL,0BAA0B,EAE3B,MAAM,8BAA8B,CAAC;AAItC,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnE,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;IACvB;;;;;OAKG;IACU,WAAW,CACtB,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,QAAQ,GACpB,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC;IAsC7C;;;;OAIG;IACU,gBAAgB,CAC3B,KAAK,EAAE,0BAA0B,EACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,QAAQ,GACpB,OAAO,CAAC,OAAO,CAAC;IAmBnB;;;;OAIG;IACU,MAAM,CACjB,KAAK,EAAE,0BAA0B,EACjC,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,WAAW,EAAE,QAAQ,GACpB,OAAO,CAAC,OAAO,CAAC;IAYnB;;;;OAIG;IACU,KAAK,CAChB,KAAK,EAAE,0BAA0B,EACjC,WAAW,EAAE,QAAQ,EACrB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,OAAO,CAAC;IAenB,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB;CASzB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Metadata, LogError } from '@memberjunction/core';
|
|
2
|
+
import { IssueEngine } from './IssueEngine.js';
|
|
3
|
+
/**
|
|
4
|
+
* Convenience service for the common issue operations: create, transition status,
|
|
5
|
+
* assign, and close.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT — this service is a THIN convenience layer. The authoritative lifecycle
|
|
8
|
+
* side-effects (IssueNumber assignment, ResolvedAt/ClosedAt stamping, and firing the
|
|
9
|
+
* IssueType OnCreate/OnStatusChange/OnClose/OnAssign action hooks) live in
|
|
10
|
+
* `IssueEntityServer.Save()` so they fire on EVERY save path — including a plain
|
|
11
|
+
* `entity.Save()` from the UI/API/automation that never touches this service. These
|
|
12
|
+
* methods just set the right field(s) and call Save(); the entity does the rest.
|
|
13
|
+
*
|
|
14
|
+
* Server-side: always pass contextUser.
|
|
15
|
+
*/
|
|
16
|
+
export class IssueService {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new Issue. Resolves the IssueType (by ID or name) and defaults status to
|
|
19
|
+
* the IsDefault status and priority to the type's DefaultPriority, then saves. The
|
|
20
|
+
* entity server assigns IssueNumber, stamps lifecycle timestamps, and fires OnCreate.
|
|
21
|
+
* Returns the saved entity, or null on failure (details logged).
|
|
22
|
+
*/
|
|
23
|
+
async CreateIssue(params, contextUser) {
|
|
24
|
+
await IssueEngine.Instance.Config(false, contextUser);
|
|
25
|
+
const issueType = this.resolveIssueType(params);
|
|
26
|
+
if (!issueType) {
|
|
27
|
+
LogError(`IssueService.CreateIssue: could not resolve IssueType (ID=${params.IssueTypeID}, Name=${params.IssueTypeName})`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const statusID = params.StatusID ?? IssueEngine.Instance.DefaultStatus?.ID;
|
|
31
|
+
if (!statusID) {
|
|
32
|
+
LogError('IssueService.CreateIssue: no StatusID provided and no default IssueStatus is configured');
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const md = new Metadata();
|
|
36
|
+
const issue = await md.GetEntityObject('MJ_BizApps_Issues: Issues', contextUser);
|
|
37
|
+
issue.NewRecord();
|
|
38
|
+
issue.Title = params.Title;
|
|
39
|
+
issue.Description = params.Description ?? null;
|
|
40
|
+
issue.IssueTypeID = issueType.ID;
|
|
41
|
+
issue.StatusID = statusID;
|
|
42
|
+
issue.Severity = params.Severity ?? 'Medium';
|
|
43
|
+
issue.Priority = params.Priority ?? issueType.DefaultPriority;
|
|
44
|
+
issue.ReporterPersonID = params.ReporterPersonID ?? null;
|
|
45
|
+
issue.ReporterEmail = params.ReporterEmail ?? null;
|
|
46
|
+
issue.SourceEntityID = params.SourceEntityID ?? null;
|
|
47
|
+
issue.SourceRecordID = params.SourceRecordID ?? null;
|
|
48
|
+
issue.AppScope = params.AppScope ?? null;
|
|
49
|
+
issue.CreatedByPersonID = params.CreatedByPersonID ?? null;
|
|
50
|
+
if (!(await issue.Save())) {
|
|
51
|
+
LogError(`IssueService.CreateIssue: save failed: ${issue.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return issue;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transitions an issue to a new status by setting StatusID and saving. The entity
|
|
58
|
+
* server stamps ResolvedAt/ClosedAt and fires OnStatusChange (+ OnClose on terminal).
|
|
59
|
+
* No-ops when the status is unchanged. Returns true on success.
|
|
60
|
+
*/
|
|
61
|
+
async TransitionStatus(issue, newStatusID, contextUser) {
|
|
62
|
+
await IssueEngine.Instance.Config(false, contextUser);
|
|
63
|
+
if (!IssueEngine.Instance.IssueStatusByID(newStatusID)) {
|
|
64
|
+
LogError(`IssueService.TransitionStatus: unknown status ID ${newStatusID}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (issue.StatusID === newStatusID) {
|
|
68
|
+
return true; // no-op
|
|
69
|
+
}
|
|
70
|
+
issue.StatusID = newStatusID;
|
|
71
|
+
if (!(await issue.Save())) {
|
|
72
|
+
LogError(`IssueService.TransitionStatus: save failed: ${issue.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Assigns an issue to a polymorphic assignee (Person or AI Agent) and saves. The
|
|
79
|
+
* entity server fires OnAssign. Pass null/null to clear the assignee. Returns true
|
|
80
|
+
* on success.
|
|
81
|
+
*/
|
|
82
|
+
async Assign(issue, assigneeEntityID, assigneeRecordID, contextUser) {
|
|
83
|
+
await IssueEngine.Instance.Config(false, contextUser);
|
|
84
|
+
issue.AssigneeEntityID = assigneeEntityID;
|
|
85
|
+
issue.AssigneeRecordID = assigneeRecordID;
|
|
86
|
+
if (!(await issue.Save())) {
|
|
87
|
+
LogError(`IssueService.Assign: save failed: ${issue.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Closes an issue by transitioning it to the supplied terminal status (or the first
|
|
94
|
+
* terminal status if none given). The entity server stamps ClosedAt and fires
|
|
95
|
+
* OnStatusChange + OnClose.
|
|
96
|
+
*/
|
|
97
|
+
async Close(issue, contextUser, terminalStatusID) {
|
|
98
|
+
await IssueEngine.Instance.Config(false, contextUser);
|
|
99
|
+
const statusID = terminalStatusID ?? IssueEngine.Instance.IssueStatuses.find((s) => s.IsTerminal)?.ID;
|
|
100
|
+
if (!statusID) {
|
|
101
|
+
LogError('IssueService.Close: no terminal IssueStatus is configured');
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return this.TransitionStatus(issue, statusID, contextUser);
|
|
105
|
+
}
|
|
106
|
+
// ------------------------------------------------------------------
|
|
107
|
+
// Helpers
|
|
108
|
+
// ------------------------------------------------------------------
|
|
109
|
+
/** Resolves the IssueType from explicit ID or name via the engine cache. */
|
|
110
|
+
resolveIssueType(params) {
|
|
111
|
+
if (params.IssueTypeID) {
|
|
112
|
+
return IssueEngine.Instance.IssueTypeByID(params.IssueTypeID);
|
|
113
|
+
}
|
|
114
|
+
if (params.IssueTypeName) {
|
|
115
|
+
return IssueEngine.Instance.IssueTypeByName(params.IssueTypeName);
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=IssueService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueService.js","sourceRoot":"","sources":["../../src/services/IssueService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { mjBizAppsTasksTaskEntity } from '@mj-biz-apps/tasks-entities';
|
|
3
|
+
import { mjBizAppsIssuesIssueEntity } from '@mj-biz-apps/issues-entities';
|
|
4
|
+
/** Task priority enum (mirrors bizapps-tasks Task.Priority). */
|
|
5
|
+
export type TaskPriority = 'Low' | 'Medium' | 'High' | 'Critical';
|
|
6
|
+
/** Parameters for spawning work from an issue. */
|
|
7
|
+
export interface SpawnTaskParams {
|
|
8
|
+
/** Task name; defaults to the issue Title. */
|
|
9
|
+
Name?: string;
|
|
10
|
+
Description?: string | null;
|
|
11
|
+
/**
|
|
12
|
+
* The bizapps-tasks TaskType for the spawned Task. If omitted, falls back to the issue's
|
|
13
|
+
* IssueType.DefaultTaskTypeID. Required — a Task cannot be created without a TypeID.
|
|
14
|
+
*/
|
|
15
|
+
TaskTypeID?: string;
|
|
16
|
+
/** Task priority; defaults to the issue's Priority. */
|
|
17
|
+
Priority?: TaskPriority;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Bridges an Issue to the bizapps-tasks work-management system. An Issue does not own a
|
|
21
|
+
* work-item table; instead it spawns a Task (bizapps-tasks) and links it back to the Issue via
|
|
22
|
+
* the existing polymorphic TaskLink (EntityID = the Issues entity, RecordID = the Issue's ID).
|
|
23
|
+
* Sub-tasks, assignment, dependencies, and Kanban/Gantt all come from bizapps-tasks for free.
|
|
24
|
+
*
|
|
25
|
+
* Server-side: always pass contextUser.
|
|
26
|
+
*/
|
|
27
|
+
export declare class IssueWorkService {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a Task for the given issue and a TaskLink pointing back at the issue. Returns the
|
|
30
|
+
* saved Task, or null on failure (details logged). Resolves the TaskType from the param or the
|
|
31
|
+
* issue's IssueType.DefaultTaskTypeID; fails clearly if neither is available.
|
|
32
|
+
*/
|
|
33
|
+
SpawnTask(issue: mjBizAppsIssuesIssueEntity, contextUser: UserInfo, params?: SpawnTaskParams): Promise<mjBizAppsTasksTaskEntity | null>;
|
|
34
|
+
/** Resolves the TaskType: explicit param wins, else the issue's IssueType.DefaultTaskTypeID. */
|
|
35
|
+
private resolveTaskTypeID;
|
|
36
|
+
/** Resolves the __mj.Entity ID for the Issues entity (the polymorphic TaskLink target). */
|
|
37
|
+
private getIssuesEntityID;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=IssueWorkService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueWorkService.d.ts","sourceRoot":"","sources":["../../src/services/IssueWorkService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAY,MAAM,sBAAsB,CAAC;AAEpE,OAAO,EACL,wBAAwB,EAEzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAI1E,gEAAgE;AAChE,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAElE,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAC3B;;;;OAIG;IACU,SAAS,CACpB,KAAK,EAAE,0BAA0B,EACjC,WAAW,EAAE,QAAQ,EACrB,MAAM,GAAE,eAAoB,GAC3B,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;IAkD3C,gGAAgG;IAChG,OAAO,CAAC,iBAAiB;IAOzB,2FAA2F;IAC3F,OAAO,CAAC,iBAAiB;CAG1B"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Metadata, LogError } from '@memberjunction/core';
|
|
2
|
+
import { IssueEngine } from './IssueEngine.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bridges an Issue to the bizapps-tasks work-management system. An Issue does not own a
|
|
5
|
+
* work-item table; instead it spawns a Task (bizapps-tasks) and links it back to the Issue via
|
|
6
|
+
* the existing polymorphic TaskLink (EntityID = the Issues entity, RecordID = the Issue's ID).
|
|
7
|
+
* Sub-tasks, assignment, dependencies, and Kanban/Gantt all come from bizapps-tasks for free.
|
|
8
|
+
*
|
|
9
|
+
* Server-side: always pass contextUser.
|
|
10
|
+
*/
|
|
11
|
+
export class IssueWorkService {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a Task for the given issue and a TaskLink pointing back at the issue. Returns the
|
|
14
|
+
* saved Task, or null on failure (details logged). Resolves the TaskType from the param or the
|
|
15
|
+
* issue's IssueType.DefaultTaskTypeID; fails clearly if neither is available.
|
|
16
|
+
*/
|
|
17
|
+
async SpawnTask(issue, contextUser, params = {}) {
|
|
18
|
+
await IssueEngine.Instance.Config(false, contextUser);
|
|
19
|
+
const taskTypeID = this.resolveTaskTypeID(issue, params);
|
|
20
|
+
if (!taskTypeID) {
|
|
21
|
+
LogError(`IssueWorkService.SpawnTask: no TaskTypeID provided and IssueType for issue ${issue.ID} has no DefaultTaskTypeID`);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const issuesEntityID = this.getIssuesEntityID();
|
|
25
|
+
if (!issuesEntityID) {
|
|
26
|
+
LogError('IssueWorkService.SpawnTask: could not resolve the "MJ_BizApps_Issues: Issues" entity ID');
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const md = new Metadata();
|
|
30
|
+
const task = await md.GetEntityObject('MJ_BizApps_Tasks: Tasks', contextUser);
|
|
31
|
+
task.NewRecord();
|
|
32
|
+
task.Name = params.Name ?? issue.Title;
|
|
33
|
+
task.Description = params.Description ?? issue.Description ?? null;
|
|
34
|
+
task.TypeID = taskTypeID;
|
|
35
|
+
task.Status = 'Open';
|
|
36
|
+
task.Priority = params.Priority ?? issue.Priority;
|
|
37
|
+
if (!(await task.Save())) {
|
|
38
|
+
LogError(`IssueWorkService.SpawnTask: task save failed: ${task.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const link = await md.GetEntityObject('MJ_BizApps_Tasks: Task Links', contextUser);
|
|
42
|
+
link.NewRecord();
|
|
43
|
+
link.TaskID = task.ID;
|
|
44
|
+
link.EntityID = issuesEntityID;
|
|
45
|
+
link.RecordID = issue.ID;
|
|
46
|
+
link.Description = `Spawned from issue: ${issue.Title}`;
|
|
47
|
+
if (!(await link.Save())) {
|
|
48
|
+
LogError(`IssueWorkService.SpawnTask: task link save failed: ${link.LatestResult?.CompleteMessage ?? 'unknown error'}`);
|
|
49
|
+
// The Task was created but the link failed — surface the partial state to the caller.
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return task;
|
|
53
|
+
}
|
|
54
|
+
// ------------------------------------------------------------------
|
|
55
|
+
// Helpers
|
|
56
|
+
// ------------------------------------------------------------------
|
|
57
|
+
/** Resolves the TaskType: explicit param wins, else the issue's IssueType.DefaultTaskTypeID. */
|
|
58
|
+
resolveTaskTypeID(issue, params) {
|
|
59
|
+
if (params.TaskTypeID) {
|
|
60
|
+
return params.TaskTypeID;
|
|
61
|
+
}
|
|
62
|
+
return IssueEngine.Instance.IssueTypeByID(issue.IssueTypeID)?.DefaultTaskTypeID ?? null;
|
|
63
|
+
}
|
|
64
|
+
/** Resolves the __mj.Entity ID for the Issues entity (the polymorphic TaskLink target). */
|
|
65
|
+
getIssuesEntityID() {
|
|
66
|
+
return new Metadata().EntityByName('MJ_BizApps_Issues: Issues')?.ID;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=IssueWorkService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueWorkService.js","sourceRoot":"","sources":["../../src/services/IssueWorkService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQpE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAmB/C;;;;;;;GAOG;AACH,MAAM,OAAO,gBAAgB;IAC3B;;;;OAIG;IACI,KAAK,CAAC,SAAS,CACpB,KAAiC,EACjC,WAAqB,EACrB,SAA0B,EAAE;QAE5B,MAAM,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAEtD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,QAAQ,CAAC,8EAA8E,KAAK,CAAC,EAAE,2BAA2B,CAAC,CAAC;YAC5H,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAChD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,QAAQ,CAAC,yFAAyF,CAAC,CAAC;YACpG,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,QAAQ,EAAE,CAAC;QAE1B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,eAAe,CAA2B,yBAAyB,EAAE,WAAW,CAAC,CAAC;QACxG,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC;QACvC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;QACnE,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAK,KAAK,CAAC,QAAyB,CAAC;QAEpE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,iDAAiD,IAAI,CAAC,YAAY,EAAE,eAAe,IAAI,eAAe,EAAE,CAAC,CAAC;YACnH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,eAAe,CAA+B,8BAA8B,EAAE,WAAW,CAAC,CAAC;QACjH,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,uBAAuB,KAAK,CAAC,KAAK,EAAE,CAAC;QAExD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,sDAAsD,IAAI,CAAC,YAAY,EAAE,eAAe,IAAI,eAAe,EAAE,CAAC,CAAC;YACxH,sFAAsF;YACtF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qEAAqE;IACrE,UAAU;IACV,qEAAqE;IAErE,gGAAgG;IACxF,iBAAiB,CAAC,KAAiC,EAAE,MAAuB;QAClF,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,OAAO,MAAM,CAAC,UAAU,CAAC;QAC3B,CAAC;QACD,OAAO,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,iBAAiB,IAAI,IAAI,CAAC;IAC1F,CAAC;IAED,2FAA2F;IACnF,iBAAiB;QACvB,OAAO,IAAI,QAAQ,EAAE,CAAC,YAAY,CAAC,2BAA2B,CAAC,EAAE,EAAE,CAAC;IACtE,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mj-biz-apps/issues-core",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "BizApps Issues Core Services - IssueEngine, IssueService, IssueWorkService",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"/dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && tsc-alias -f",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest"
|
|
18
|
+
},
|
|
19
|
+
"author": "MemberJunction.com",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@mj-biz-apps/issues-entities": "1.0.0",
|
|
26
|
+
"@mj-biz-apps/tasks-entities": "^1.0.0",
|
|
27
|
+
"@mj-biz-apps/common-entities": "^5.30.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@memberjunction/core": "^5.40.2",
|
|
31
|
+
"@memberjunction/core-entities": "^5.40.2",
|
|
32
|
+
"@memberjunction/global": "^5.40.2",
|
|
33
|
+
"@memberjunction/sqlserver-dataprovider": "^5.40.2",
|
|
34
|
+
"@memberjunction/actions": "^5.40.2",
|
|
35
|
+
"@memberjunction/actions-base": "^5.40.2"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/MemberJunction/bizapps-issues"
|
|
40
|
+
}
|
|
10
41
|
}
|
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @mj-biz-apps/issues-core
|
|
2
|
-
|
|
3
|
-
## ⚠️ IMPORTANT NOTICE ⚠️
|
|
4
|
-
|
|
5
|
-
**This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
|
|
6
|
-
|
|
7
|
-
This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@mj-biz-apps/issues-core`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|