retold 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +38 -1
- package/README.md +92 -2
- package/docs/README.md +7 -6
- package/docs/_sidebar.md +36 -21
- package/docs/_topbar.md +2 -2
- package/docs/architecture/comprehensions.md +282 -0
- package/docs/architecture/fluid-models.md +355 -0
- package/docs/architecture/module-architecture.md +234 -0
- package/docs/{modules.md → architecture/modules.md} +25 -22
- package/docs/cover.md +2 -2
- package/docs/css/docuserve.css +6 -6
- package/docs/examples/examples.md +71 -0
- package/docs/examples/todolist/todo-list-cli-client.md +178 -0
- package/docs/examples/todolist/todo-list-console-client.md +152 -0
- package/docs/examples/todolist/todo-list-model.md +114 -0
- package/docs/examples/todolist/todo-list-server.md +128 -0
- package/docs/examples/todolist/todo-list-web-client.md +177 -0
- package/docs/examples/todolist/todo-list.md +162 -0
- package/docs/getting-started.md +8 -7
- package/docs/index.html +4 -4
- package/docs/{meadow.md → modules/meadow.md} +4 -6
- package/docs/{orator.md → modules/orator.md} +1 -0
- package/docs/{pict.md → modules/pict.md} +30 -8
- package/docs/{utility.md → modules/utility.md} +0 -9
- package/docs/retold-catalog.json +1792 -231
- package/docs/retold-keyword-index.json +136439 -64616
- package/examples/todo-list/Dockerfile +45 -0
- package/examples/todo-list/README.md +394 -0
- package/examples/todo-list/cli-client/package-lock.json +418 -0
- package/examples/todo-list/cli-client/package.json +19 -0
- package/examples/todo-list/cli-client/source/TodoCLI-CLIProgram.js +30 -0
- package/examples/todo-list/cli-client/source/TodoCLI-Run.js +3 -0
- package/examples/todo-list/cli-client/source/commands/add/TodoCLI-Command-Add.js +74 -0
- package/examples/todo-list/cli-client/source/commands/complete/TodoCLI-Command-Complete.js +84 -0
- package/examples/todo-list/cli-client/source/commands/list/TodoCLI-Command-List.js +110 -0
- package/examples/todo-list/cli-client/source/commands/remove/TodoCLI-Command-Remove.js +49 -0
- package/examples/todo-list/cli-client/source/services/TodoCLI-Service-API.js +92 -0
- package/examples/todo-list/console-client/console-client.cjs +913 -0
- package/examples/todo-list/console-client/package-lock.json +426 -0
- package/examples/todo-list/console-client/package.json +19 -0
- package/examples/todo-list/console-client/views/PictView-TUI-Header.cjs +43 -0
- package/examples/todo-list/console-client/views/PictView-TUI-Layout.cjs +58 -0
- package/examples/todo-list/console-client/views/PictView-TUI-StatusBar.cjs +41 -0
- package/examples/todo-list/console-client/views/PictView-TUI-TaskList.cjs +104 -0
- package/examples/todo-list/docker-motd.sh +36 -0
- package/examples/todo-list/docker-run.sh +2 -0
- package/examples/todo-list/docker-shell.sh +2 -0
- package/examples/todo-list/model/MeadowSchema-Task.json +152 -0
- package/examples/todo-list/model/Task-Compiled.json +25 -0
- package/examples/todo-list/model/Task.mddl +15 -0
- package/examples/todo-list/model/data/seeded_todo_events.csv +1001 -0
- package/examples/todo-list/server/database-initialization-service.cjs +273 -0
- package/examples/todo-list/server/package-lock.json +6113 -0
- package/examples/todo-list/server/package.json +19 -0
- package/examples/todo-list/server/server.cjs +138 -0
- package/examples/todo-list/web-client/css/todolist-theme.css +235 -0
- package/examples/todo-list/web-client/generate-build-config.cjs +18 -0
- package/examples/todo-list/web-client/html/index.html +18 -0
- package/examples/todo-list/web-client/package-lock.json +12030 -0
- package/examples/todo-list/web-client/package.json +43 -0
- package/examples/todo-list/web-client/source/TodoList-Application-Config.json +12 -0
- package/examples/todo-list/web-client/source/TodoList-Application.cjs +383 -0
- package/examples/todo-list/web-client/source/providers/Provider-TaskData.cjs +243 -0
- package/examples/todo-list/web-client/source/providers/Router-Config.json +32 -0
- package/examples/todo-list/web-client/source/views/View-Layout.cjs +75 -0
- package/examples/todo-list/web-client/source/views/View-TaskForm.cjs +87 -0
- package/examples/todo-list/web-client/source/views/View-TaskList.cjs +127 -0
- package/examples/todo-list/web-client/source/views/calendar/View-MonthView.cjs +293 -0
- package/examples/todo-list/web-client/source/views/calendar/View-WeekView.cjs +149 -0
- package/examples/todo-list/web-client/source/views/calendar/View-YearView.cjs +226 -0
- package/modules/Include-Retold-Module-List.sh +2 -2
- package/package.json +5 -5
- package/docs/js/pict.min.js +0 -12
- package/docs/js/pict.min.js.map +0 -1
- package/docs/pict-docuserve.min.js +0 -58
- package/docs/pict-docuserve.min.js.map +0 -1
- /package/docs/{architecture.md → architecture/architecture.md} +0 -0
- /package/docs/{fable.md → modules/fable.md} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const libCommandLineCommand = require('pict-service-commandlineutility').ServiceCommandLineCommand;
|
|
2
|
+
|
|
3
|
+
class TodoCLICommandList extends libCommandLineCommand
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pManifest, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pManifest, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.options.CommandKeyword = 'list';
|
|
10
|
+
this.options.Description = 'List tasks from the todo list';
|
|
11
|
+
|
|
12
|
+
this.options.Aliases.push('ls');
|
|
13
|
+
this.options.Aliases.push('l');
|
|
14
|
+
|
|
15
|
+
this.options.CommandOptions.push({ Name: '-s, --search [text]', Description: 'Filter tasks by name or description', Default: '' });
|
|
16
|
+
this.options.CommandOptions.push({ Name: '-c, --column [column]', Description: 'Sort column (DueDate, Name, Status, LengthInHours, IDTask)', Default: 'DueDate' });
|
|
17
|
+
this.options.CommandOptions.push({ Name: '-d, --direction [direction]', Description: 'Sort direction (ASC or DESC)', Default: 'DESC' });
|
|
18
|
+
this.options.CommandOptions.push({ Name: '-n, --limit [count]', Description: 'Maximum number of tasks to show', Default: '50' });
|
|
19
|
+
this.options.CommandOptions.push({ Name: '--status [status]', Description: 'Filter by status (Pending, Complete, In Progress)', Default: '' });
|
|
20
|
+
|
|
21
|
+
this.addCommand();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onRunAsync(fCallback)
|
|
25
|
+
{
|
|
26
|
+
let tmpAPI = this.services.TodoAPI;
|
|
27
|
+
let tmpSearch = this.CommandOptions.search || '';
|
|
28
|
+
let tmpColumn = this.CommandOptions.column || 'DueDate';
|
|
29
|
+
let tmpDirection = this.CommandOptions.direction || 'DESC';
|
|
30
|
+
let tmpLimit = parseInt(this.CommandOptions.limit, 10) || 50;
|
|
31
|
+
let tmpStatusFilter = this.CommandOptions.status || '';
|
|
32
|
+
|
|
33
|
+
let tmpPath = tmpAPI.buildFilteredPath(tmpColumn, tmpDirection, tmpSearch, tmpLimit);
|
|
34
|
+
|
|
35
|
+
this.log.info(`Fetching tasks...`);
|
|
36
|
+
|
|
37
|
+
tmpAPI.request('GET', tmpPath, null,
|
|
38
|
+
(pError, pTasks) =>
|
|
39
|
+
{
|
|
40
|
+
if (pError)
|
|
41
|
+
{
|
|
42
|
+
this.log.error(`Failed to fetch tasks: ${pError.message}`);
|
|
43
|
+
return fCallback();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!Array.isArray(pTasks))
|
|
47
|
+
{
|
|
48
|
+
this.log.error(`Unexpected response from server.`);
|
|
49
|
+
return fCallback();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Optional client-side status filter
|
|
53
|
+
let tmpTasks = pTasks;
|
|
54
|
+
if (tmpStatusFilter)
|
|
55
|
+
{
|
|
56
|
+
let tmpLower = tmpStatusFilter.toLowerCase();
|
|
57
|
+
tmpTasks = tmpTasks.filter(
|
|
58
|
+
(pTask) =>
|
|
59
|
+
{
|
|
60
|
+
return (pTask.Status || '').toLowerCase() === tmpLower;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (tmpTasks.length === 0)
|
|
65
|
+
{
|
|
66
|
+
this.log.info(`No tasks found.`);
|
|
67
|
+
return fCallback();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.log.info(`${tmpTasks.length} task(s):`);
|
|
71
|
+
this.log.info('');
|
|
72
|
+
|
|
73
|
+
// Table header
|
|
74
|
+
let tmpHeader = this._padRight('ID', 6)
|
|
75
|
+
+ this._padRight('Status', 14)
|
|
76
|
+
+ this._padRight('Due Date', 13)
|
|
77
|
+
+ this._padRight('Hours', 7)
|
|
78
|
+
+ 'Name';
|
|
79
|
+
this.log.info(tmpHeader);
|
|
80
|
+
this.log.info('-'.repeat(78));
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < tmpTasks.length; i++)
|
|
83
|
+
{
|
|
84
|
+
let tmpTask = tmpTasks[i];
|
|
85
|
+
let tmpDueDate = (tmpTask.DueDate || '-').substring(0, 10);
|
|
86
|
+
let tmpLine = this._padRight(String(tmpTask.IDTask), 6)
|
|
87
|
+
+ this._padRight(tmpTask.Status || '-', 14)
|
|
88
|
+
+ this._padRight(tmpDueDate, 13)
|
|
89
|
+
+ this._padRight(String(tmpTask.LengthInHours || 0), 7)
|
|
90
|
+
+ (tmpTask.Name || '(untitled)');
|
|
91
|
+
this.log.info(tmpLine);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.log.info('');
|
|
95
|
+
return fCallback();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_padRight(pString, pLength)
|
|
100
|
+
{
|
|
101
|
+
let tmpStr = String(pString);
|
|
102
|
+
while (tmpStr.length < pLength)
|
|
103
|
+
{
|
|
104
|
+
tmpStr += ' ';
|
|
105
|
+
}
|
|
106
|
+
return tmpStr;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = TodoCLICommandList;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const libCommandLineCommand = require('pict-service-commandlineutility').ServiceCommandLineCommand;
|
|
2
|
+
|
|
3
|
+
class TodoCLICommandRemove extends libCommandLineCommand
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pManifest, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pManifest, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.options.CommandKeyword = 'remove';
|
|
10
|
+
this.options.Description = 'Remove a task by ID';
|
|
11
|
+
|
|
12
|
+
this.options.Aliases.push('rm');
|
|
13
|
+
this.options.Aliases.push('delete');
|
|
14
|
+
this.options.Aliases.push('del');
|
|
15
|
+
|
|
16
|
+
this.options.CommandArguments.push({ Name: '<id>', Description: 'The task ID to remove' });
|
|
17
|
+
|
|
18
|
+
this.addCommand();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onRunAsync(fCallback)
|
|
22
|
+
{
|
|
23
|
+
let tmpAPI = this.services.TodoAPI;
|
|
24
|
+
let tmpIDTask = parseInt(this.ArgumentString, 10);
|
|
25
|
+
|
|
26
|
+
if (!tmpIDTask || isNaN(tmpIDTask))
|
|
27
|
+
{
|
|
28
|
+
this.log.error(`A valid task ID is required. Usage: todo remove 42`);
|
|
29
|
+
return fCallback();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.log.info(`Removing task ${tmpIDTask}...`);
|
|
33
|
+
|
|
34
|
+
tmpAPI.request('DELETE', '/1.0/Task/' + tmpIDTask, null,
|
|
35
|
+
(pError, pResult) =>
|
|
36
|
+
{
|
|
37
|
+
if (pError)
|
|
38
|
+
{
|
|
39
|
+
this.log.error(`Failed to remove task: ${pError.message}`);
|
|
40
|
+
return fCallback();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.log.info(`Task ${tmpIDTask} removed.`);
|
|
44
|
+
return fCallback();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = TodoCLICommandRemove;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
const libHttp = require('http');
|
|
3
|
+
|
|
4
|
+
class TodoAPIService extends libFableServiceProviderBase
|
|
5
|
+
{
|
|
6
|
+
constructor(pFable, pManifest, pServiceHash)
|
|
7
|
+
{
|
|
8
|
+
super(pFable, pManifest, pServiceHash);
|
|
9
|
+
|
|
10
|
+
this.serviceType = 'TodoAPI';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Make an HTTP request to the Todo API.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} pMethod - HTTP method (GET, POST, PUT, DELETE)
|
|
17
|
+
* @param {string} pPath - URL path (e.g. /1.0/Tasks)
|
|
18
|
+
* @param {Object|null} pBody - Request body (for POST/PUT)
|
|
19
|
+
* @param {Function} fCallback - Callback(pError, pParsedResponse)
|
|
20
|
+
*/
|
|
21
|
+
request(pMethod, pPath, pBody, fCallback)
|
|
22
|
+
{
|
|
23
|
+
let tmpBaseURL = this.fable.settings.ApiBaseURL || 'http://localhost:8086';
|
|
24
|
+
let tmpURL = new URL(pPath, tmpBaseURL);
|
|
25
|
+
|
|
26
|
+
let tmpOptions =
|
|
27
|
+
{
|
|
28
|
+
method: pMethod,
|
|
29
|
+
hostname: tmpURL.hostname,
|
|
30
|
+
port: tmpURL.port,
|
|
31
|
+
path: tmpURL.pathname + tmpURL.search,
|
|
32
|
+
headers: { 'Content-Type': 'application/json' }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
let tmpReq = libHttp.request(tmpOptions,
|
|
36
|
+
(pResponse) =>
|
|
37
|
+
{
|
|
38
|
+
let tmpData = '';
|
|
39
|
+
pResponse.on('data', (pChunk) => { tmpData += pChunk; });
|
|
40
|
+
pResponse.on('end', () =>
|
|
41
|
+
{
|
|
42
|
+
try
|
|
43
|
+
{
|
|
44
|
+
let tmpParsed = JSON.parse(tmpData);
|
|
45
|
+
return fCallback(null, tmpParsed);
|
|
46
|
+
}
|
|
47
|
+
catch (pParseError)
|
|
48
|
+
{
|
|
49
|
+
return fCallback(pParseError);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
tmpReq.on('error', (pError) => { return fCallback(pError); });
|
|
55
|
+
|
|
56
|
+
if (pBody)
|
|
57
|
+
{
|
|
58
|
+
tmpReq.write(JSON.stringify(pBody));
|
|
59
|
+
}
|
|
60
|
+
tmpReq.end();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build a FilteredTo path with optional sort and search.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} pSortColumn - Column to sort by (default 'DueDate')
|
|
67
|
+
* @param {string} pSortDirection - ASC or DESC (default 'DESC')
|
|
68
|
+
* @param {string} pSearchText - Optional LIKE search across Name and Description
|
|
69
|
+
* @param {number} pLimit - Max records to return (default 50)
|
|
70
|
+
* @returns {string} The URL path
|
|
71
|
+
*/
|
|
72
|
+
buildFilteredPath(pSortColumn, pSortDirection, pSearchText, pLimit)
|
|
73
|
+
{
|
|
74
|
+
let tmpSortColumn = pSortColumn || 'DueDate';
|
|
75
|
+
let tmpSortDirection = pSortDirection || 'DESC';
|
|
76
|
+
let tmpLimit = pLimit || 50;
|
|
77
|
+
|
|
78
|
+
let tmpFilter = 'FSF~' + tmpSortColumn + '~' + tmpSortDirection + '~0';
|
|
79
|
+
|
|
80
|
+
if (pSearchText)
|
|
81
|
+
{
|
|
82
|
+
let tmpSearchEncoded = encodeURIComponent('%' + pSearchText + '%');
|
|
83
|
+
tmpFilter = 'FBV~Name~LK~' + tmpSearchEncoded
|
|
84
|
+
+ '~FBVOR~Description~LK~' + tmpSearchEncoded
|
|
85
|
+
+ '~' + tmpFilter;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return '/1.0/Tasks/FilteredTo/' + tmpFilter + '/0/' + tmpLimit;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = TodoAPIService;
|