github-issue-tower-defence-management 1.152.2 → 1.153.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/CHANGELOG.md +14 -0
- package/README.md +16 -1
- package/bin/adapter/entry-points/cli/index.js +36 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DR_LIP9n.js → index-BooMHl5Y.js} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/adapter/entry-points/console/webServer.js +59 -23
- package/bin/adapter/entry-points/console/webServer.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +11 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/ownerCallFileCleaner.js +20 -0
- package/bin/adapter/entry-points/handlers/ownerCallFileCleaner.js.map +1 -0
- package/bin/adapter/entry-points/handlers/ownerCallFileStore.js +80 -0
- package/bin/adapter/entry-points/handlers/ownerCallFileStore.js.map +1 -0
- package/bin/domain/usecases/intmux/OwnerCallFile.js +52 -0
- package/bin/domain/usecases/intmux/OwnerCallFile.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +189 -0
- package/src/adapter/entry-points/cli/index.ts +75 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +1 -4
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +80 -4
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +0 -1
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DR_LIP9n.js → index-BooMHl5Y.js} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/adapter/entry-points/console/webServer.test.ts +317 -0
- package/src/adapter/entry-points/console/webServer.ts +86 -26
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +35 -0
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +15 -0
- package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +109 -0
- package/src/adapter/entry-points/handlers/ownerCallFileCleaner.ts +25 -0
- package/src/adapter/entry-points/handlers/ownerCallFileStore.test.ts +286 -0
- package/src/adapter/entry-points/handlers/ownerCallFileStore.ts +145 -0
- package/src/domain/usecases/intmux/OwnerCallFile.test.ts +184 -0
- package/src/domain/usecases/intmux/OwnerCallFile.ts +80 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/webServer.d.ts +1 -0
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/ownerCallFileCleaner.d.ts +8 -0
- package/types/adapter/entry-points/handlers/ownerCallFileCleaner.d.ts.map +1 -0
- package/types/adapter/entry-points/handlers/ownerCallFileStore.d.ts +21 -0
- package/types/adapter/entry-points/handlers/ownerCallFileStore.d.ts.map +1 -0
- package/types/domain/usecases/intmux/OwnerCallFile.d.ts +18 -0
- package/types/domain/usecases/intmux/OwnerCallFile.d.ts.map +1 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
OWNER_CALL_FILE_DIRECTORY_NAME,
|
|
5
|
+
OwnerCall,
|
|
6
|
+
OwnerCallProjectSessionNames,
|
|
7
|
+
ownerCallFileRelativePath,
|
|
8
|
+
ownerCallProjectCodeOfSession,
|
|
9
|
+
ownerCallYamlDocument,
|
|
10
|
+
} from '../../../domain/usecases/intmux/OwnerCallFile';
|
|
11
|
+
|
|
12
|
+
export type OwnerCallFileAppendParams = {
|
|
13
|
+
dataDir: string;
|
|
14
|
+
projectCode: string | null;
|
|
15
|
+
ownerCall: OwnerCall;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type OwnerCallFileDeleteParams = {
|
|
19
|
+
dataDir: string;
|
|
20
|
+
projectCode: string | null;
|
|
21
|
+
sessionName: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type OwnerCallFileDeleteInEveryProjectParams = {
|
|
25
|
+
dataDir: string;
|
|
26
|
+
sessionName: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const ownerCallFilePath = (
|
|
30
|
+
dataDir: string,
|
|
31
|
+
projectCode: string | null,
|
|
32
|
+
sessionName: string,
|
|
33
|
+
): string =>
|
|
34
|
+
path.join(dataDir, ownerCallFileRelativePath(projectCode, sessionName));
|
|
35
|
+
|
|
36
|
+
export const ownerCallFileAppend = (
|
|
37
|
+
params: OwnerCallFileAppendParams,
|
|
38
|
+
): void => {
|
|
39
|
+
const filePath = ownerCallFilePath(
|
|
40
|
+
params.dataDir,
|
|
41
|
+
params.projectCode,
|
|
42
|
+
params.ownerCall.sessionName,
|
|
43
|
+
);
|
|
44
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
45
|
+
fs.appendFileSync(filePath, ownerCallYamlDocument(params.ownerCall));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const ownerCallFileDelete = (
|
|
49
|
+
params: OwnerCallFileDeleteParams,
|
|
50
|
+
): void => {
|
|
51
|
+
fs.rmSync(
|
|
52
|
+
ownerCallFilePath(params.dataDir, params.projectCode, params.sessionName),
|
|
53
|
+
{ force: true },
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const projectCodeDirectoryNames = (dataDir: string): string[] => {
|
|
58
|
+
const ownerCallDirectory = path.join(dataDir, OWNER_CALL_FILE_DIRECTORY_NAME);
|
|
59
|
+
if (!fs.existsSync(ownerCallDirectory)) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return fs
|
|
63
|
+
.readdirSync(ownerCallDirectory, { withFileTypes: true })
|
|
64
|
+
.filter((entry) => entry.isDirectory())
|
|
65
|
+
.map((entry) => entry.name);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const ownerCallFileDeleteInEveryProject = (
|
|
69
|
+
params: OwnerCallFileDeleteInEveryProjectParams,
|
|
70
|
+
): void => {
|
|
71
|
+
for (const projectCode of projectCodeDirectoryNames(params.dataDir)) {
|
|
72
|
+
ownerCallFileDelete({
|
|
73
|
+
dataDir: params.dataDir,
|
|
74
|
+
projectCode,
|
|
75
|
+
sessionName: params.sessionName,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// The in-tmux-by-human data the scheduled run writes into the same directory
|
|
81
|
+
// serveWeb serves: one `{projectCode}.v4.json` per project, each listing the
|
|
82
|
+
// sessions of that project. `index.v4.json` only names the project files, so
|
|
83
|
+
// it is not read here.
|
|
84
|
+
const IN_TMUX_BY_HUMAN_PROJECT_DATA_SUFFIX = '.v4.json';
|
|
85
|
+
const IN_TMUX_BY_HUMAN_INDEX_FILE_NAME = `index${IN_TMUX_BY_HUMAN_PROJECT_DATA_SUFFIX}`;
|
|
86
|
+
|
|
87
|
+
const isUnknownArray = (value: unknown): value is unknown[] =>
|
|
88
|
+
Array.isArray(value);
|
|
89
|
+
|
|
90
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
91
|
+
typeof value === 'object' && value !== null;
|
|
92
|
+
|
|
93
|
+
const arrayOrEmpty = (value: unknown): unknown[] =>
|
|
94
|
+
isUnknownArray(value) ? value : [];
|
|
95
|
+
|
|
96
|
+
const propertyOrUndefined = (value: unknown, key: string): unknown =>
|
|
97
|
+
isRecord(value) ? value[key] : undefined;
|
|
98
|
+
|
|
99
|
+
const sessionNamesInProjectDataFile = (filePath: string): string[] => {
|
|
100
|
+
let parsed: unknown;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
103
|
+
} catch {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
return arrayOrEmpty(propertyOrUndefined(parsed, 'groups')).flatMap((group) =>
|
|
107
|
+
arrayOrEmpty(propertyOrUndefined(group, 'sessions'))
|
|
108
|
+
.map((session) => propertyOrUndefined(session, 'name'))
|
|
109
|
+
.filter((name): name is string => typeof name === 'string'),
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const inTmuxByHumanProjectSessionNames = (
|
|
114
|
+
dataDir: string,
|
|
115
|
+
): OwnerCallProjectSessionNames[] => {
|
|
116
|
+
if (!fs.existsSync(dataDir)) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return fs
|
|
120
|
+
.readdirSync(dataDir, { withFileTypes: true })
|
|
121
|
+
.filter(
|
|
122
|
+
(entry) =>
|
|
123
|
+
entry.isFile() &&
|
|
124
|
+
entry.name.endsWith(IN_TMUX_BY_HUMAN_PROJECT_DATA_SUFFIX) &&
|
|
125
|
+
entry.name !== IN_TMUX_BY_HUMAN_INDEX_FILE_NAME,
|
|
126
|
+
)
|
|
127
|
+
.map((entry) => ({
|
|
128
|
+
projectCode: entry.name.slice(
|
|
129
|
+
0,
|
|
130
|
+
entry.name.length - IN_TMUX_BY_HUMAN_PROJECT_DATA_SUFFIX.length,
|
|
131
|
+
),
|
|
132
|
+
sessionNames: sessionNamesInProjectDataFile(
|
|
133
|
+
path.join(dataDir, entry.name),
|
|
134
|
+
),
|
|
135
|
+
}));
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const ownerCallProjectCodeInInTmuxByHumanData = (
|
|
139
|
+
dataDir: string,
|
|
140
|
+
sessionName: string,
|
|
141
|
+
): string | null =>
|
|
142
|
+
ownerCallProjectCodeOfSession(
|
|
143
|
+
inTmuxByHumanProjectSessionNames(dataDir),
|
|
144
|
+
sessionName,
|
|
145
|
+
);
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { parseAllDocuments } from 'yaml';
|
|
2
|
+
import {
|
|
3
|
+
OWNER_CALL_FILE_DIRECTORY_NAME,
|
|
4
|
+
OWNER_CALL_FILE_PROJECT_CODE_FOR_NO_PROJECT,
|
|
5
|
+
isOwnerCallCalledAtValid,
|
|
6
|
+
ownerCallFileRelativePath,
|
|
7
|
+
ownerCallProjectCodeOfSession,
|
|
8
|
+
ownerCallYamlDocument,
|
|
9
|
+
} from './OwnerCallFile';
|
|
10
|
+
import { toTmuxSessionName } from './InTmuxByHumanSessionReconcileUseCase';
|
|
11
|
+
|
|
12
|
+
describe('ownerCallFileRelativePath', () => {
|
|
13
|
+
it('places the file under the owner call directory of the given project code', () => {
|
|
14
|
+
expect(ownerCallFileRelativePath('umino', 'secretary')).toBe(
|
|
15
|
+
'call-to-user/umino/secretary.yaml',
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('replaces every slash of the session name with an underscore', () => {
|
|
20
|
+
expect(
|
|
21
|
+
ownerCallFileRelativePath(
|
|
22
|
+
'umino',
|
|
23
|
+
toTmuxSessionName('https://github.com/OWNER/REPO/issues/1'),
|
|
24
|
+
),
|
|
25
|
+
).toBe('call-to-user/umino/https___github_com_OWNER_REPO_issues_1.yaml');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('uses NA for a session that belongs to no project', () => {
|
|
29
|
+
expect(ownerCallFileRelativePath(null, 'app')).toBe(
|
|
30
|
+
'call-to-user/NA/app.yaml',
|
|
31
|
+
);
|
|
32
|
+
expect(
|
|
33
|
+
ownerCallFileRelativePath(
|
|
34
|
+
OWNER_CALL_FILE_PROJECT_CODE_FOR_NO_PROJECT,
|
|
35
|
+
'app',
|
|
36
|
+
),
|
|
37
|
+
).toBe('call-to-user/NA/app.yaml');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('starts with the owner call directory name every reader resolves against', () => {
|
|
41
|
+
expect(
|
|
42
|
+
ownerCallFileRelativePath('umino', 'secretary').startsWith(
|
|
43
|
+
`${OWNER_CALL_FILE_DIRECTORY_NAME}/`,
|
|
44
|
+
),
|
|
45
|
+
).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('gives the same path for the raw issue url and for the tmux session name of it', () => {
|
|
49
|
+
const issueUrl = 'https://github.com/OWNER/REPO/issues/1';
|
|
50
|
+
|
|
51
|
+
expect(ownerCallFileRelativePath('umino', issueUrl)).toBe(
|
|
52
|
+
ownerCallFileRelativePath('umino', toTmuxSessionName(issueUrl)),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('ownerCallProjectCodeOfSession', () => {
|
|
58
|
+
const issueUrl = 'https://github.com/OWNER/REPO/issues/1';
|
|
59
|
+
const otherIssueUrl = 'https://github.com/OWNER/REPO/issues/2';
|
|
60
|
+
|
|
61
|
+
it('gives the code of the project whose session list holds the session', () => {
|
|
62
|
+
expect(
|
|
63
|
+
ownerCallProjectCodeOfSession(
|
|
64
|
+
[
|
|
65
|
+
{ projectCode: 'other', sessionNames: [otherIssueUrl] },
|
|
66
|
+
{ projectCode: 'umino', sessionNames: [issueUrl] },
|
|
67
|
+
],
|
|
68
|
+
toTmuxSessionName(issueUrl),
|
|
69
|
+
),
|
|
70
|
+
).toBe('umino');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('gives null when no project lists the session', () => {
|
|
74
|
+
expect(
|
|
75
|
+
ownerCallProjectCodeOfSession(
|
|
76
|
+
[{ projectCode: 'umino', sessionNames: [otherIssueUrl] }],
|
|
77
|
+
toTmuxSessionName(issueUrl),
|
|
78
|
+
),
|
|
79
|
+
).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('gives null when no project data is present at all', () => {
|
|
83
|
+
expect(
|
|
84
|
+
ownerCallProjectCodeOfSession([], toTmuxSessionName(issueUrl)),
|
|
85
|
+
).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('isOwnerCallCalledAtValid', () => {
|
|
90
|
+
it('accepts a UTC ISO-8601 timestamp with second precision and a trailing Z', () => {
|
|
91
|
+
expect(isOwnerCallCalledAtValid('2026-08-14T04:22:28Z')).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('rejects a timestamp without second precision, without the Z, or with a numeric offset', () => {
|
|
95
|
+
expect(isOwnerCallCalledAtValid('2026-08-14T04:22Z')).toBe(false);
|
|
96
|
+
expect(isOwnerCallCalledAtValid('2026-08-14T04:22:28')).toBe(false);
|
|
97
|
+
expect(isOwnerCallCalledAtValid('2026-08-14T04:22:28+09:00')).toBe(false);
|
|
98
|
+
expect(isOwnerCallCalledAtValid('2026-08-14T04:22:28.123Z')).toBe(false);
|
|
99
|
+
expect(isOwnerCallCalledAtValid('')).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('rejects a timestamp whose calendar fields do not denote a real instant', () => {
|
|
103
|
+
expect(isOwnerCallCalledAtValid('2026-13-14T04:22:28Z')).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('ownerCallYamlDocument', () => {
|
|
108
|
+
const sessionName = toTmuxSessionName(
|
|
109
|
+
'https://github.com/OWNER/REPO/issues/1',
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
it('renders one YAML document opened by the YAML document delimiter', () => {
|
|
113
|
+
expect(
|
|
114
|
+
ownerCallYamlDocument({
|
|
115
|
+
sessionName,
|
|
116
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
117
|
+
body: 'The first line of the call body.\n\nA later line of the call body.\n',
|
|
118
|
+
}),
|
|
119
|
+
).toBe(
|
|
120
|
+
[
|
|
121
|
+
'---',
|
|
122
|
+
'sessionName: "https_//github_com/OWNER/REPO/issues/1"',
|
|
123
|
+
'calledAt: "2026-08-14T04:22:28Z"',
|
|
124
|
+
'body: |2',
|
|
125
|
+
' The first line of the call body.',
|
|
126
|
+
'',
|
|
127
|
+
' A later line of the call body.',
|
|
128
|
+
'',
|
|
129
|
+
].join('\n'),
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('keeps a body whose first line begins with a space readable as YAML', () => {
|
|
134
|
+
const body = ' indented first line\nsecond line\n';
|
|
135
|
+
const document = ownerCallYamlDocument({
|
|
136
|
+
sessionName,
|
|
137
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
138
|
+
body,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const parsed = parseAllDocuments(document);
|
|
142
|
+
expect(parsed).toHaveLength(1);
|
|
143
|
+
expect(parsed[0].errors).toEqual([]);
|
|
144
|
+
expect(parsed[0].toJS()).toEqual({
|
|
145
|
+
sessionName,
|
|
146
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
147
|
+
body,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('keeps a body line that would otherwise open a new YAML document inside the block', () => {
|
|
152
|
+
const body = '---\nnot a document delimiter\n';
|
|
153
|
+
const parsed = parseAllDocuments(
|
|
154
|
+
ownerCallYamlDocument({
|
|
155
|
+
sessionName,
|
|
156
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
157
|
+
body,
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
expect(parsed).toHaveLength(1);
|
|
162
|
+
expect(parsed[0].toJS()).toEqual({
|
|
163
|
+
sessionName,
|
|
164
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
165
|
+
body,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('repeats the session name in the document so a reader can reject a foreign file', () => {
|
|
170
|
+
const parsed = parseAllDocuments(
|
|
171
|
+
ownerCallYamlDocument({
|
|
172
|
+
sessionName,
|
|
173
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
174
|
+
body: 'one line\n',
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
expect(parsed[0].toJS()).toEqual({
|
|
179
|
+
sessionName,
|
|
180
|
+
calledAt: '2026-08-14T04:22:28Z',
|
|
181
|
+
body: 'one line\n',
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { toTmuxSessionName } from './InTmuxByHumanSessionReconcileUseCase';
|
|
2
|
+
|
|
3
|
+
export const OWNER_CALL_FILE_DIRECTORY_NAME = 'call-to-user';
|
|
4
|
+
|
|
5
|
+
export const OWNER_CALL_FILE_PROJECT_CODE_FOR_NO_PROJECT = 'NA';
|
|
6
|
+
|
|
7
|
+
export const OWNER_CALL_FILE_EXTENSION = '.yaml';
|
|
8
|
+
|
|
9
|
+
export type OwnerCall = {
|
|
10
|
+
sessionName: string;
|
|
11
|
+
calledAt: string;
|
|
12
|
+
body: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// The key one owner call file is named by. It is the tmux session name
|
|
16
|
+
// derivation every other in-tmux caller uses, followed by the replacement of
|
|
17
|
+
// the slashes tmux keeps but a file name cannot hold. Applying it to a name
|
|
18
|
+
// that already went through it gives that same name back, so the writing side,
|
|
19
|
+
// which knows the tmux session name, and the reading side, which knows the
|
|
20
|
+
// issue url, reach the same file.
|
|
21
|
+
export const ownerCallFileSessionKey = (sessionName: string): string =>
|
|
22
|
+
toTmuxSessionName(sessionName).replace(/\//g, '_');
|
|
23
|
+
|
|
24
|
+
export const ownerCallFileRelativePath = (
|
|
25
|
+
projectCode: string | null,
|
|
26
|
+
sessionName: string,
|
|
27
|
+
): string => {
|
|
28
|
+
const directory = projectCode ?? OWNER_CALL_FILE_PROJECT_CODE_FOR_NO_PROJECT;
|
|
29
|
+
const fileName = ownerCallFileSessionKey(sessionName);
|
|
30
|
+
return `${OWNER_CALL_FILE_DIRECTORY_NAME}/${directory}/${fileName}${OWNER_CALL_FILE_EXTENSION}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type OwnerCallProjectSessionNames = {
|
|
34
|
+
projectCode: string;
|
|
35
|
+
sessionNames: string[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const ownerCallProjectCodeOfSession = (
|
|
39
|
+
projects: OwnerCallProjectSessionNames[],
|
|
40
|
+
sessionName: string,
|
|
41
|
+
): string | null => {
|
|
42
|
+
const key = ownerCallFileSessionKey(sessionName);
|
|
43
|
+
const owningProject = projects.find((project) =>
|
|
44
|
+
project.sessionNames.some(
|
|
45
|
+
(candidate) => ownerCallFileSessionKey(candidate) === key,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
return owningProject ? owningProject.projectCode : null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const CALLED_AT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
|
|
52
|
+
|
|
53
|
+
export const isOwnerCallCalledAtValid = (calledAt: string): boolean => {
|
|
54
|
+
if (!CALLED_AT_PATTERN.test(calledAt)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const parsed = new Date(calledAt);
|
|
58
|
+
return (
|
|
59
|
+
Number.isFinite(parsed.getTime()) &&
|
|
60
|
+
parsed.toISOString().replace(/\.\d{3}Z$/, 'Z') === calledAt
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const BODY_INDENTATION = ' ';
|
|
65
|
+
|
|
66
|
+
const bodyBlockLines = (body: string): string[] =>
|
|
67
|
+
body
|
|
68
|
+
.replace(/\n+$/, '')
|
|
69
|
+
.split('\n')
|
|
70
|
+
.map((line) => (line.length === 0 ? '' : `${BODY_INDENTATION}${line}`));
|
|
71
|
+
|
|
72
|
+
export const ownerCallYamlDocument = (ownerCall: OwnerCall): string =>
|
|
73
|
+
[
|
|
74
|
+
'---',
|
|
75
|
+
`sessionName: ${JSON.stringify(ownerCall.sessionName)}`,
|
|
76
|
+
`calledAt: ${JSON.stringify(ownerCall.calledAt)}`,
|
|
77
|
+
`body: |${BODY_INDENTATION.length}`,
|
|
78
|
+
...bodyBlockLines(ownerCall.body),
|
|
79
|
+
'',
|
|
80
|
+
].join('\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,UAAU,EACV,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,UAAU,EACV,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AA4LzB,eAAO,MAAM,OAAO,SAAgB,CAAC;AAqgCrC,eAAO,MAAM,uBAAuB,GAAI,OAAO,OAAO,KAAG,IAGxD,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EAAE,EACd,kBAAkB,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KACzC,OAAO,CAAC,IAAI,CAMd,CAAC"}
|
|
@@ -36,6 +36,7 @@ export type WebServerOptions = {
|
|
|
36
36
|
export declare const DASHBOARD_REQUEST_PATH = "/tdpm.txt";
|
|
37
37
|
export declare const IMAGE_PROXY_REQUEST_PATH = "/api/img";
|
|
38
38
|
export declare const resolveDashboardFilePath: (dashboardDir: string, requestPath: string) => string | null;
|
|
39
|
+
export declare const isOwnerCallFileRequestPath: (requestPath: string) => boolean;
|
|
39
40
|
export declare const resolveFlatInTmuxFilePath: (inTmuxDataDir: string, requestPath: string) => string | null;
|
|
40
41
|
export declare const resolveDashboardContent: (options: WebServerOptions, requestPath: string) => Buffer | null;
|
|
41
42
|
export declare const handleWebRequest: (options: WebServerOptions, request: http.IncomingMessage, response: http.ServerResponse) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webServer.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/webServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,uEAAuE,CAAC;AAClH,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAM9F,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EAQvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,8BAA8B,EAE9B,sBAAsB,EACtB,sBAAsB,EAOvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"webServer.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/webServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,uEAAuE,CAAC;AAClH,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAM9F,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EAQvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,8BAA8B,EAE9B,sBAAsB,EACtB,sBAAsB,EAOvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AAUtE,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAoC/C,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAGiB,CAAC;AAEtE,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAIH,CAAC;AAIlD,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,KAAG,OAmBvD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,eAAe,MAAM,EACrB,eAAe,MAAM,GAAG,IAAI,KAC3B,OAAoE,CAAC;AAExE,eAAO,MAAM,kBAAkB,GAC7B,cAAc,MAAM,GAAG,SAAS,KAC/B,MAAM,GAAG,IAoBX,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACpC,aAAa,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAC1C,aAAa,MAAM,GAAG,IAAI,KACzB,MAAM,GAAG,IAWX,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAC0C,CAAC;AAE5F,eAAO,MAAM,oBAAoB,GAAI,YAAY,GAAG,KAAG,MAOtD,CAAC;AAuCF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,sBAAsB,CAAC,EAAE,8BAA8B,GAAG,IAAI,CAAC;IAC/D,cAAc,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACnD,yBAAyB,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC7D,oBAAoB,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACnD,sBAAsB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACxD,CAAC;AAYF,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAInD,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,EACpB,aAAa,MAAM,KAClB,MAAM,GAAG,IAWX,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,aAAa,MAAM,KAAG,OAEI,CAAC;AAEtE,eAAO,MAAM,yBAAyB,GACpC,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB,MAAM,GAAG,IAoBX,CAAC;AA0ZF,eAAO,MAAM,uBAAuB,GAClC,SAAS,gBAAgB,EACzB,aAAa,MAAM,KAClB,MAAM,GAAG,IAeX,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,gBAAgB,EACzB,SAAS,IAAI,CAAC,eAAe,EAC7B,UAAU,IAAI,CAAC,cAAc,KAC5B,OAAO,CAAC,IAAI,CA8Ed,CAAC;AAcF,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,IAAI,CAAC,MAM7D,CAAC;AAEL,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG;IACrD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,CAQlB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAqD3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,6BAA4B,MAAM,EAAE,GAAG,IAAW,KACjD,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CA2xBP;CACH"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
export type CleanClosedIssueOwnerCallFilesParams = {
|
|
3
|
+
inTmuxDataOutputDir: string | null | undefined;
|
|
4
|
+
pjcode: string | null | undefined;
|
|
5
|
+
issues: Issue[];
|
|
6
|
+
};
|
|
7
|
+
export declare const cleanClosedIssueOwnerCallFiles: (params: CleanClosedIssueOwnerCallFilesParams) => void;
|
|
8
|
+
//# sourceMappingURL=ownerCallFileCleaner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownerCallFileCleaner.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/ownerCallFileCleaner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAIvD,MAAM,MAAM,oCAAoC,GAAG;IACjD,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC/C,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,QAAQ,oCAAoC,KAC3C,IAYF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OwnerCall } from '../../../domain/usecases/intmux/OwnerCallFile';
|
|
2
|
+
export type OwnerCallFileAppendParams = {
|
|
3
|
+
dataDir: string;
|
|
4
|
+
projectCode: string | null;
|
|
5
|
+
ownerCall: OwnerCall;
|
|
6
|
+
};
|
|
7
|
+
export type OwnerCallFileDeleteParams = {
|
|
8
|
+
dataDir: string;
|
|
9
|
+
projectCode: string | null;
|
|
10
|
+
sessionName: string;
|
|
11
|
+
};
|
|
12
|
+
export type OwnerCallFileDeleteInEveryProjectParams = {
|
|
13
|
+
dataDir: string;
|
|
14
|
+
sessionName: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const ownerCallFilePath: (dataDir: string, projectCode: string | null, sessionName: string) => string;
|
|
17
|
+
export declare const ownerCallFileAppend: (params: OwnerCallFileAppendParams) => void;
|
|
18
|
+
export declare const ownerCallFileDelete: (params: OwnerCallFileDeleteParams) => void;
|
|
19
|
+
export declare const ownerCallFileDeleteInEveryProject: (params: OwnerCallFileDeleteInEveryProjectParams) => void;
|
|
20
|
+
export declare const ownerCallProjectCodeInInTmuxByHumanData: (dataDir: string, sessionName: string) => string | null;
|
|
21
|
+
//# sourceMappingURL=ownerCallFileStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownerCallFileStore.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/ownerCallFileStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,SAAS,EAKV,MAAM,+CAA+C,CAAC;AAEvD,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,aAAa,MAAM,GAAG,IAAI,EAC1B,aAAa,MAAM,KAClB,MACsE,CAAC;AAE1E,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,yBAAyB,KAChC,IAQF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,yBAAyB,KAChC,IAKF,CAAC;AAaF,eAAO,MAAM,iCAAiC,GAC5C,QAAQ,uCAAuC,KAC9C,IAQF,CAAC;AA4DF,eAAO,MAAM,uCAAuC,GAClD,SAAS,MAAM,EACf,aAAa,MAAM,KAClB,MAAM,GAAG,IAIT,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const OWNER_CALL_FILE_DIRECTORY_NAME = "call-to-user";
|
|
2
|
+
export declare const OWNER_CALL_FILE_PROJECT_CODE_FOR_NO_PROJECT = "NA";
|
|
3
|
+
export declare const OWNER_CALL_FILE_EXTENSION = ".yaml";
|
|
4
|
+
export type OwnerCall = {
|
|
5
|
+
sessionName: string;
|
|
6
|
+
calledAt: string;
|
|
7
|
+
body: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const ownerCallFileSessionKey: (sessionName: string) => string;
|
|
10
|
+
export declare const ownerCallFileRelativePath: (projectCode: string | null, sessionName: string) => string;
|
|
11
|
+
export type OwnerCallProjectSessionNames = {
|
|
12
|
+
projectCode: string;
|
|
13
|
+
sessionNames: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare const ownerCallProjectCodeOfSession: (projects: OwnerCallProjectSessionNames[], sessionName: string) => string | null;
|
|
16
|
+
export declare const isOwnerCallCalledAtValid: (calledAt: string) => boolean;
|
|
17
|
+
export declare const ownerCallYamlDocument: (ownerCall: OwnerCall) => string;
|
|
18
|
+
//# sourceMappingURL=OwnerCallFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OwnerCallFile.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/intmux/OwnerCallFile.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,8BAA8B,iBAAiB,CAAC;AAE7D,eAAO,MAAM,2CAA2C,OAAO,CAAC;AAEhE,eAAO,MAAM,yBAAyB,UAAU,CAAC;AAEjD,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAQF,eAAO,MAAM,uBAAuB,GAAI,aAAa,MAAM,KAAG,MACV,CAAC;AAErD,eAAO,MAAM,yBAAyB,GACpC,aAAa,MAAM,GAAG,IAAI,EAC1B,aAAa,MAAM,KAClB,MAIF,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,UAAU,4BAA4B,EAAE,EACxC,aAAa,MAAM,KAClB,MAAM,GAAG,IAQX,CAAC;AAIF,eAAO,MAAM,wBAAwB,GAAI,UAAU,MAAM,KAAG,OAS3D,CAAC;AAUF,eAAO,MAAM,qBAAqB,GAAI,WAAW,SAAS,KAAG,MAQ/C,CAAC"}
|