@mj-biz-apps/tasks-server 0.0.1 → 1.0.1
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/event-handlers/TaskNotificationHandler.d.ts +6 -0
- package/dist/event-handlers/TaskNotificationHandler.d.ts.map +1 -0
- package/dist/event-handlers/TaskNotificationHandler.js +363 -0
- package/dist/event-handlers/TaskNotificationHandler.js.map +1 -0
- package/dist/generated/generated.d.ts +930 -0
- package/dist/generated/generated.d.ts.map +1 -0
- package/dist/generated/generated.js +5354 -0
- package/dist/generated/generated.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/scheduled-jobs/OverdueTaskNotificationJob.d.ts +45 -0
- package/dist/scheduled-jobs/OverdueTaskNotificationJob.d.ts.map +1 -0
- package/dist/scheduled-jobs/OverdueTaskNotificationJob.js +305 -0
- package/dist/scheduled-jobs/OverdueTaskNotificationJob.js.map +1 -0
- package/package.json +41 -7
- package/README.md +0 -45
|
@@ -0,0 +1,305 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* Overdue Task Notification Scheduled Job
|
|
9
|
+
*
|
|
10
|
+
* Runs on a cron schedule (default: daily at 8 AM) to find tasks that are
|
|
11
|
+
* past their DueAt and send in-app notifications to assignees and/or the
|
|
12
|
+
* task creator. Optionally invokes a configured MJ Action for external
|
|
13
|
+
* notification delivery (email, SMS, Teams, etc.).
|
|
14
|
+
*
|
|
15
|
+
* Configuration is read from the `TaskNotificationConfig` table:
|
|
16
|
+
* - One row with TaskTypeID = NULL serves as the global default
|
|
17
|
+
* - Per-TaskType rows override the global defaults
|
|
18
|
+
*
|
|
19
|
+
* Resolution order for the overdue action:
|
|
20
|
+
* 1. TaskNotificationConfig with matching TaskTypeID → OverdueActionID
|
|
21
|
+
* 2. TaskType.OnOverdueActionID
|
|
22
|
+
* 3. TaskNotificationConfig global default → OverdueActionID
|
|
23
|
+
* 4. Built-in: create MJ: User Notifications (in-app only)
|
|
24
|
+
*/
|
|
25
|
+
import { Metadata, RunView, ValidationResult } from '@memberjunction/core';
|
|
26
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
27
|
+
import { BaseScheduledJob } from '@memberjunction/scheduling-engine';
|
|
28
|
+
/**
|
|
29
|
+
* Scheduled job driver for overdue task notifications.
|
|
30
|
+
*
|
|
31
|
+
* Registered with the MJ scheduling engine via `@RegisterClass`.
|
|
32
|
+
* The engine instantiates this class when a ScheduledJob record references
|
|
33
|
+
* the `ScheduledJobOverdueTaskNotification` driver class.
|
|
34
|
+
*/
|
|
35
|
+
let OverdueTaskNotificationJob = class OverdueTaskNotificationJob extends BaseScheduledJob {
|
|
36
|
+
async Execute(context) {
|
|
37
|
+
const { ContextUser } = context;
|
|
38
|
+
try {
|
|
39
|
+
// 1. Load all notification configs
|
|
40
|
+
const { globalConfig, typeConfigs } = await this.loadConfigs(ContextUser);
|
|
41
|
+
if (!globalConfig) {
|
|
42
|
+
return { Success: true, Details: { message: 'No global config found, skipping', tasksNotified: 0 } };
|
|
43
|
+
}
|
|
44
|
+
// 2. Find overdue tasks
|
|
45
|
+
const overdueTasks = await this.findOverdueTasks(globalConfig, typeConfigs, ContextUser);
|
|
46
|
+
if (overdueTasks.length === 0) {
|
|
47
|
+
return { Success: true, Details: { tasksNotified: 0 } };
|
|
48
|
+
}
|
|
49
|
+
// 3. Process each overdue task
|
|
50
|
+
let notifiedCount = 0;
|
|
51
|
+
for (const task of overdueTasks) {
|
|
52
|
+
const config = this.resolveConfig(task.TypeID, typeConfigs, globalConfig);
|
|
53
|
+
if (!config.OverdueNotificationsEnabled)
|
|
54
|
+
continue;
|
|
55
|
+
const recipients = await this.resolveRecipients(task, config, ContextUser);
|
|
56
|
+
if (recipients.length === 0)
|
|
57
|
+
continue;
|
|
58
|
+
// Create in-app notifications
|
|
59
|
+
await this.sendNotifications(task, recipients, ContextUser);
|
|
60
|
+
// Invoke action if configured
|
|
61
|
+
const actionID = await this.resolveActionID(task, config, ContextUser);
|
|
62
|
+
if (actionID) {
|
|
63
|
+
await this.invokeAction(actionID, task, ContextUser);
|
|
64
|
+
}
|
|
65
|
+
// Log and stamp
|
|
66
|
+
await this.logNotifications(task, recipients, ContextUser);
|
|
67
|
+
await this.stampOverdueNotifiedAt(task.ID, ContextUser);
|
|
68
|
+
notifiedCount++;
|
|
69
|
+
}
|
|
70
|
+
this.log(`Notified ${notifiedCount} overdue task(s)`);
|
|
71
|
+
return { Success: true, Details: { tasksNotified: notifiedCount } };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
75
|
+
this.logError('Overdue task notification failed', err);
|
|
76
|
+
return { Success: false, ErrorMessage: msg };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
ValidateConfiguration(_schedule) {
|
|
80
|
+
// Configuration is minimal (just batchSize). Always valid.
|
|
81
|
+
const result = new ValidationResult();
|
|
82
|
+
result.Success = true;
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
FormatNotification(_context, result) {
|
|
86
|
+
const count = result.Details?.tasksNotified ?? 0;
|
|
87
|
+
return {
|
|
88
|
+
Subject: `Overdue Task Check: ${count} task(s) notified`,
|
|
89
|
+
Body: count > 0
|
|
90
|
+
? `Successfully sent overdue notifications for ${count} task(s).`
|
|
91
|
+
: 'No overdue tasks found that need notification.',
|
|
92
|
+
Priority: count > 0 ? 'Normal' : 'Low',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------
|
|
96
|
+
// Config Loading
|
|
97
|
+
// ---------------------------------------------------------------
|
|
98
|
+
async loadConfigs(contextUser) {
|
|
99
|
+
const rv = new RunView();
|
|
100
|
+
const result = await rv.RunView({
|
|
101
|
+
EntityName: 'MJ_BizApps_Tasks: Task Notification Configs',
|
|
102
|
+
ResultType: 'simple',
|
|
103
|
+
}, contextUser);
|
|
104
|
+
let globalConfig = null;
|
|
105
|
+
const typeConfigs = new Map();
|
|
106
|
+
for (const row of result?.Results ?? []) {
|
|
107
|
+
if (!row.TaskTypeID) {
|
|
108
|
+
globalConfig = row;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
typeConfigs.set(row.TaskTypeID, row);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { globalConfig, typeConfigs };
|
|
115
|
+
}
|
|
116
|
+
resolveConfig(taskTypeID, typeConfigs, globalConfig) {
|
|
117
|
+
return typeConfigs.get(taskTypeID) ?? globalConfig;
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------
|
|
120
|
+
// Overdue Task Query
|
|
121
|
+
// ---------------------------------------------------------------
|
|
122
|
+
async findOverdueTasks(globalConfig, typeConfigs, contextUser) {
|
|
123
|
+
const rv = new RunView();
|
|
124
|
+
const now = new Date();
|
|
125
|
+
// Base query: overdue + not completed/cancelled
|
|
126
|
+
const result = await rv.RunView({
|
|
127
|
+
EntityName: 'MJ_BizApps_Tasks: Tasks',
|
|
128
|
+
ExtraFilter: `DueAt < GETUTCDATE() AND Status NOT IN ('Completed', 'Cancelled')`,
|
|
129
|
+
ResultType: 'simple',
|
|
130
|
+
}, contextUser);
|
|
131
|
+
// Filter by grace period and repeat interval
|
|
132
|
+
return (result?.Results ?? []).filter(task => {
|
|
133
|
+
const config = this.resolveConfig(task.TypeID, typeConfigs, globalConfig);
|
|
134
|
+
if (!config.OverdueNotificationsEnabled)
|
|
135
|
+
return false;
|
|
136
|
+
const dueAt = new Date(task.DueAt);
|
|
137
|
+
const graceCutoff = new Date(dueAt.getTime() + config.OverdueGracePeriodHours * 3600000);
|
|
138
|
+
if (now < graceCutoff)
|
|
139
|
+
return false;
|
|
140
|
+
// Check repeat interval
|
|
141
|
+
if (task.OverdueNotifiedAt) {
|
|
142
|
+
if (!config.OverdueRepeatIntervalHours)
|
|
143
|
+
return false; // notify once only
|
|
144
|
+
const lastNotified = new Date(task.OverdueNotifiedAt);
|
|
145
|
+
const nextNotifyAt = new Date(lastNotified.getTime() + config.OverdueRepeatIntervalHours * 3600000);
|
|
146
|
+
if (now < nextNotifyAt)
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------
|
|
153
|
+
// Recipient Resolution
|
|
154
|
+
// ---------------------------------------------------------------
|
|
155
|
+
async resolveRecipients(task, config, contextUser) {
|
|
156
|
+
const userIDs = new Set();
|
|
157
|
+
const rv = new RunView();
|
|
158
|
+
// Assignees
|
|
159
|
+
if (config.NotifyAssignees) {
|
|
160
|
+
const assignments = await rv.RunView({
|
|
161
|
+
EntityName: 'MJ_BizApps_Tasks: Task Assignments',
|
|
162
|
+
ExtraFilter: `TaskID='${task.ID}'`,
|
|
163
|
+
ResultType: 'simple',
|
|
164
|
+
}, contextUser);
|
|
165
|
+
const personIDs = (assignments?.Results ?? []).map((a) => a.AssigneeRecordID);
|
|
166
|
+
for (const personID of personIDs) {
|
|
167
|
+
const uid = await this.getLinkedUserID(personID, contextUser);
|
|
168
|
+
if (uid)
|
|
169
|
+
userIDs.add(uid);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Creator
|
|
173
|
+
if (config.NotifyCreator && task.CreatedByPersonID) {
|
|
174
|
+
const uid = await this.getLinkedUserID(task.CreatedByPersonID, contextUser);
|
|
175
|
+
if (uid)
|
|
176
|
+
userIDs.add(uid);
|
|
177
|
+
}
|
|
178
|
+
return [...userIDs];
|
|
179
|
+
}
|
|
180
|
+
async getLinkedUserID(personID, contextUser) {
|
|
181
|
+
const rv = new RunView();
|
|
182
|
+
const result = await rv.RunView({
|
|
183
|
+
EntityName: 'MJ_BizApps_Common: People',
|
|
184
|
+
ExtraFilter: `ID='${personID}'`,
|
|
185
|
+
Fields: ['ID', 'LinkedUserID'],
|
|
186
|
+
ResultType: 'simple',
|
|
187
|
+
MaxRows: 1,
|
|
188
|
+
}, contextUser);
|
|
189
|
+
return result?.Results?.[0]?.LinkedUserID ?? null;
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------
|
|
192
|
+
// Notification Delivery
|
|
193
|
+
// ---------------------------------------------------------------
|
|
194
|
+
async sendNotifications(task, userIDs, contextUser) {
|
|
195
|
+
const dueDate = new Date(task.DueAt).toLocaleDateString('en-US', {
|
|
196
|
+
month: 'short', day: 'numeric', year: 'numeric',
|
|
197
|
+
});
|
|
198
|
+
const isReminder = !!task.OverdueNotifiedAt;
|
|
199
|
+
const title = isReminder
|
|
200
|
+
? `Overdue Reminder: ${task.Name}`
|
|
201
|
+
: `Task Overdue: ${task.Name}`;
|
|
202
|
+
const message = `"${task.Name}" was due ${dueDate} and is still ${task.Status}. Priority: ${task.Priority}.`;
|
|
203
|
+
const md = new Metadata();
|
|
204
|
+
for (const userID of userIDs) {
|
|
205
|
+
try {
|
|
206
|
+
const notification = await md.GetEntityObject('MJ: User Notifications', contextUser);
|
|
207
|
+
notification.NewRecord();
|
|
208
|
+
notification.Set('UserID', userID);
|
|
209
|
+
notification.Set('Title', title);
|
|
210
|
+
notification.Set('Message', message);
|
|
211
|
+
notification.Set('Unread', true);
|
|
212
|
+
await notification.Save();
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
this.logError(`Failed to create notification for user ${userID}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// ---------------------------------------------------------------
|
|
220
|
+
// Action Invocation
|
|
221
|
+
// ---------------------------------------------------------------
|
|
222
|
+
async resolveActionID(task, config, contextUser) {
|
|
223
|
+
// 1. Config-level override
|
|
224
|
+
if (config.OverdueActionID)
|
|
225
|
+
return config.OverdueActionID;
|
|
226
|
+
// 2. TaskType.OnOverdueActionID
|
|
227
|
+
const rv = new RunView();
|
|
228
|
+
const typeResult = await rv.RunView({
|
|
229
|
+
EntityName: 'MJ_BizApps_Tasks: Task Types',
|
|
230
|
+
ExtraFilter: `ID='${task.TypeID}'`,
|
|
231
|
+
ResultType: 'simple',
|
|
232
|
+
MaxRows: 1,
|
|
233
|
+
}, contextUser);
|
|
234
|
+
const onOverdueActionID = typeResult?.Results?.[0]?.OnOverdueActionID;
|
|
235
|
+
if (onOverdueActionID)
|
|
236
|
+
return onOverdueActionID;
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
async invokeAction(actionID, task, contextUser) {
|
|
240
|
+
try {
|
|
241
|
+
const { ActionEngineServer } = await import('@memberjunction/actions');
|
|
242
|
+
await ActionEngineServer.Instance.Config(false, contextUser);
|
|
243
|
+
const action = ActionEngineServer.Instance.Actions.find((a) => a.ID === actionID);
|
|
244
|
+
if (!action) {
|
|
245
|
+
this.logError(`Action ${actionID} not found`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
await ActionEngineServer.Instance.RunAction({
|
|
249
|
+
Action: action,
|
|
250
|
+
ContextUser: contextUser,
|
|
251
|
+
Params: [
|
|
252
|
+
{ Name: 'TaskID', Value: task.ID, Type: 'Input' },
|
|
253
|
+
{ Name: 'TaskName', Value: task.Name, Type: 'Input' },
|
|
254
|
+
{ Name: 'Status', Value: task.Status, Type: 'Input' },
|
|
255
|
+
{ Name: 'Priority', Value: task.Priority, Type: 'Input' },
|
|
256
|
+
{ Name: 'DueAt', Value: task.DueAt, Type: 'Input' },
|
|
257
|
+
],
|
|
258
|
+
Filters: [],
|
|
259
|
+
});
|
|
260
|
+
this.log(`Invoked overdue action for "${task.Name}"`);
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
this.logError(`Failed to invoke action ${actionID}`, err);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// ---------------------------------------------------------------
|
|
267
|
+
// Logging + Tracking
|
|
268
|
+
// ---------------------------------------------------------------
|
|
269
|
+
async logNotifications(task, userIDs, contextUser) {
|
|
270
|
+
const notificationType = task.OverdueNotifiedAt ? 'OverdueReminder' : 'Overdue';
|
|
271
|
+
const md = new Metadata();
|
|
272
|
+
for (const userID of userIDs) {
|
|
273
|
+
try {
|
|
274
|
+
const log = await md.GetEntityObject('MJ_BizApps_Tasks: Task Notification Logs', contextUser);
|
|
275
|
+
log.NewRecord();
|
|
276
|
+
log.Set('TaskID', task.ID);
|
|
277
|
+
log.Set('NotificationType', notificationType);
|
|
278
|
+
log.Set('NotifiedUserID', userID);
|
|
279
|
+
await log.Save();
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
this.logError(`Failed to log notification for task ${task.ID}, user ${userID}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async stampOverdueNotifiedAt(taskID, contextUser) {
|
|
287
|
+
try {
|
|
288
|
+
const md = new Metadata();
|
|
289
|
+
const task = await md.GetEntityObject('MJ_BizApps_Tasks: Tasks', contextUser);
|
|
290
|
+
const { CompositeKey } = await import('@memberjunction/core');
|
|
291
|
+
const pk = new CompositeKey([{ FieldName: 'ID', Value: taskID }]);
|
|
292
|
+
await task.InnerLoad(pk);
|
|
293
|
+
task.Set('OverdueNotifiedAt', new Date());
|
|
294
|
+
await task.Save();
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
this.logError(`Failed to stamp OverdueNotifiedAt for task ${taskID}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
OverdueTaskNotificationJob = __decorate([
|
|
302
|
+
RegisterClass(BaseScheduledJob, 'ScheduledJobOverdueTaskNotification')
|
|
303
|
+
], OverdueTaskNotificationJob);
|
|
304
|
+
export { OverdueTaskNotificationJob };
|
|
305
|
+
//# sourceMappingURL=OverdueTaskNotificationJob.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverdueTaskNotificationJob.js","sourceRoot":"","sources":["../../src/scheduled-jobs/OverdueTaskNotificationJob.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mj-biz-apps/tasks-server",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"description": "BizApps Tasks server bootstrap - GraphQL resolvers and class registrations",
|
|
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
|
+
},
|
|
18
|
+
"author": "MemberJunction.com",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.9.3"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@mj-biz-apps/tasks-entities": "1.0.1",
|
|
25
|
+
"@mj-biz-apps/tasks-entities-server": "1.0.1",
|
|
26
|
+
"@mj-biz-apps/tasks-actions": "1.0.1",
|
|
27
|
+
"@mj-biz-apps/tasks-core": "1.0.1",
|
|
28
|
+
"class-validator": "^0.14.3"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@memberjunction/core": "^5.38.0",
|
|
32
|
+
"@memberjunction/core-entities": "^5.38.0",
|
|
33
|
+
"@memberjunction/global": "^5.38.0",
|
|
34
|
+
"@memberjunction/server": "^5.38.0",
|
|
35
|
+
"@memberjunction/sqlserver-dataprovider": "^5.38.0",
|
|
36
|
+
"@memberjunction/scheduling-engine": "^5.38.0",
|
|
37
|
+
"@memberjunction/scheduling-base-types": "^5.38.0",
|
|
38
|
+
"@memberjunction/actions": "^5.38.0"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/MemberJunction/bizapps-tasks"
|
|
43
|
+
}
|
|
10
44
|
}
|
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @mj-biz-apps/tasks-server
|
|
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/tasks-server`
|
|
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**
|