@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.
@@ -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":"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"}
package/package.json CHANGED
@@ -1,10 +1,41 @@
1
1
  {
2
2
  "name": "@mj-biz-apps/issues-core",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @mj-biz-apps/issues-core",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
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**