stonyx 0.2.3-alpha.6 → 0.2.3-alpha.8
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/README.md +4 -12
- package/docs/cli.md +0 -10
- package/docs/conventions/cron-conventions.md +195 -0
- package/docs/conventions/framework-modules.md +13 -0
- package/docs/conventions/index.md +1 -0
- package/docs/conventions/orm-conventions.md +32 -0
- package/docs/modules.md +1 -5
- package/package.json +2 -2
- package/src/cli.js +1 -1
package/README.md
CHANGED
|
@@ -5,23 +5,19 @@
|
|
|
5
5
|
- 100% JavaScript (ES Modules)
|
|
6
6
|
- Drop-in module system — no boilerplate
|
|
7
7
|
- Automatic async module loading and initialization
|
|
8
|
-
- Built-in CLI for
|
|
8
|
+
- Built-in CLI for bootstrapping and testing
|
|
9
9
|
|
|
10
10
|
## Quick Start
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm install
|
|
14
|
-
stonyx new my-app
|
|
15
|
-
cd my-app
|
|
16
|
-
stonyx serve
|
|
13
|
+
npm install stonyx
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
The
|
|
16
|
+
The CLI handles everything — no manual `new Stonyx()` calls needed:
|
|
20
17
|
|
|
21
18
|
```bash
|
|
22
19
|
stonyx serve # Bootstrap + run app.js
|
|
23
20
|
stonyx test # Bootstrap + run tests
|
|
24
|
-
stonyx help # Show all available commands
|
|
25
21
|
```
|
|
26
22
|
|
|
27
23
|
Stonyx reads `config/environment.js`, initializes all `@stonyx/*` modules from your `devDependencies`, and runs your application.
|
|
@@ -44,12 +40,8 @@ Stonyx reads `config/environment.js`, initializes all `@stonyx/*` modules from y
|
|
|
44
40
|
| Module | Description |
|
|
45
41
|
|--------|-------------|
|
|
46
42
|
| [@stonyx/cron](https://github.com/abofs/stonyx-cron) | Lightweight async job scheduling |
|
|
47
|
-
| [@stonyx/discord](https://github.com/abofs/stonyx-discord) | Discord bot with command and event handler auto-discovery |
|
|
48
|
-
| [@stonyx/events](https://github.com/abofs/stonyx-events) | Pub/sub event system |
|
|
49
|
-
| [@stonyx/oauth](https://github.com/abofs/stonyx-oauth) | OAuth provider integration |
|
|
50
|
-
| [@stonyx/orm](https://github.com/abofs/stonyx-orm) | ORM with models, relationships, and serializers |
|
|
51
43
|
| [@stonyx/rest-server](https://github.com/abofs/stonyx-rest-server) | Dynamic REST server with auto-route registration |
|
|
52
|
-
| [@stonyx/
|
|
44
|
+
| [@stonyx/orm](https://github.com/abofs/stonyx-orm) | ORM with models, relationships, and serializers |
|
|
53
45
|
|
|
54
46
|
## License
|
|
55
47
|
|
package/docs/cli.md
CHANGED
|
@@ -12,20 +12,10 @@ stonyx <command> [...args]
|
|
|
12
12
|
|
|
13
13
|
| Command | Alias | Description |
|
|
14
14
|
|---------|-------|-------------|
|
|
15
|
-
| `new` | `n` | Scaffold a new Stonyx project |
|
|
16
15
|
| `serve` | `s` | Bootstrap Stonyx and run the app |
|
|
17
16
|
| `test` | `t` | Bootstrap Stonyx in test mode and run tests |
|
|
18
17
|
| `help` | `h` | Show available commands |
|
|
19
18
|
|
|
20
|
-
### new
|
|
21
|
-
|
|
22
|
-
Scaffolds a new Stonyx project in the current directory. Prompts for a project name and which modules to include, then generates the project structure and runs `pnpm install`.
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
stonyx new # Prompts for project name
|
|
26
|
-
stonyx new my-app # Creates my-app/ in the current directory
|
|
27
|
-
```
|
|
28
|
-
|
|
29
19
|
### serve
|
|
30
20
|
|
|
31
21
|
Bootstraps Stonyx (loads config, initializes modules, runs lifecycle hooks), then imports your application entry point.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Cron Conventions
|
|
2
|
+
|
|
3
|
+
Scheduling conventions for `@stonyx/cron`. Covers the legacy interval API and the advanced scheduling system.
|
|
4
|
+
|
|
5
|
+
## Legacy API (Simple Intervals)
|
|
6
|
+
|
|
7
|
+
For basic recurring callbacks, use the `Cron` class directly:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import Cron from '@stonyx/cron';
|
|
11
|
+
|
|
12
|
+
const cron = new Cron();
|
|
13
|
+
|
|
14
|
+
// register(key, callback, intervalSeconds, runOnInit?)
|
|
15
|
+
cron.register('health-check', async () => {
|
|
16
|
+
await checkHealth();
|
|
17
|
+
}, 300, true);
|
|
18
|
+
|
|
19
|
+
// Unregister when done
|
|
20
|
+
cron.unregister('health-check');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`Cron` is a singleton — only one instance per process.
|
|
24
|
+
|
|
25
|
+
## Advanced Scheduling (CronService)
|
|
26
|
+
|
|
27
|
+
For jobs with cron expressions, one-shot scheduling, AI input normalization, and run history:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import CronService from '@stonyx/cron/service';
|
|
31
|
+
|
|
32
|
+
const service = new CronService();
|
|
33
|
+
|
|
34
|
+
service.onJobDue = async (job) => {
|
|
35
|
+
// Execute the job's work
|
|
36
|
+
return { status: 'ok', summary: 'completed' };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
await service.start();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Schedule Kinds
|
|
43
|
+
|
|
44
|
+
Three schedule types, specified in `schedule.kind`:
|
|
45
|
+
|
|
46
|
+
| Kind | Purpose | Required fields |
|
|
47
|
+
|------|---------|----------------|
|
|
48
|
+
| `every` | Recurring interval | `everyMs` (milliseconds) |
|
|
49
|
+
| `cron` | Cron expression | `expr` (5-field), optional `tz` |
|
|
50
|
+
| `at` | One-shot | `at` (ISO-8601 string) |
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
// Recurring every 60 seconds
|
|
54
|
+
await service.add({
|
|
55
|
+
name: 'Diagnostics',
|
|
56
|
+
schedule: { kind: 'every', everyMs: 60_000 },
|
|
57
|
+
payload: { kind: 'agentTurn', message: 'run diagnostics' },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Daily at 9am Eastern
|
|
61
|
+
await service.add({
|
|
62
|
+
name: 'Morning Report',
|
|
63
|
+
schedule: { kind: 'cron', expr: '0 9 * * *', tz: 'America/New_York' },
|
|
64
|
+
payload: { kind: 'agentTurn', message: 'generate morning report' },
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// One-shot reminder (auto-deletes after run)
|
|
68
|
+
await service.add({
|
|
69
|
+
name: 'Reminder',
|
|
70
|
+
schedule: { kind: 'at', at: '2026-07-01T12:00:00Z' },
|
|
71
|
+
payload: { kind: 'agentTurn', message: 'follow up on PR' },
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Payload Kinds
|
|
76
|
+
|
|
77
|
+
| Kind | Target | Key field |
|
|
78
|
+
|------|--------|-----------|
|
|
79
|
+
| `agentTurn` | Isolated agent session | `message` |
|
|
80
|
+
| `systemEvent` | Main session | `text` |
|
|
81
|
+
|
|
82
|
+
`sessionTarget` is inferred automatically: `agentTurn` → `isolated`, `systemEvent` → `main`.
|
|
83
|
+
|
|
84
|
+
### CRUD
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const job = await service.add({ ... }); // Create
|
|
88
|
+
const found = service.get(job.id); // Read
|
|
89
|
+
const updated = await service.update(job.id, { name: 'Renamed' }); // Update
|
|
90
|
+
await service.remove(job.id); // Delete
|
|
91
|
+
|
|
92
|
+
// List (excludes disabled by default)
|
|
93
|
+
const jobs = service.list();
|
|
94
|
+
const all = service.list({ includeDisabled: true });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Manual Execution
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
// Force-run regardless of schedule
|
|
101
|
+
await service.run(job.id, 'force');
|
|
102
|
+
|
|
103
|
+
// Only run if due
|
|
104
|
+
await service.run(job.id, 'due');
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Run History
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
const history = service.runs(job.id);
|
|
111
|
+
// [{ status, error?, summary?, runAtMs, durationMs, nextRunAtMs, ts }]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### AI Input Normalization
|
|
115
|
+
|
|
116
|
+
CronService accepts loose input from AI tool calls and normalizes it:
|
|
117
|
+
|
|
118
|
+
- Missing `schedule.kind` is inferred from fields (`everyMs` → `every`, `expr` → `cron`, `at` → `at`)
|
|
119
|
+
- Bare `message` or `text` at the top level is wrapped into a `payload`
|
|
120
|
+
- `deleteAfterRun` is auto-set for one-shot (`at`) jobs
|
|
121
|
+
- `delivery: { mode: 'announce' }` is auto-set for isolated `agentTurn` jobs
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
// AI might send this flat structure
|
|
125
|
+
await service.add({
|
|
126
|
+
name: 'Weather Check',
|
|
127
|
+
schedule: { everyMs: 120000 },
|
|
128
|
+
message: 'check the weather',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Normalized to:
|
|
132
|
+
// {
|
|
133
|
+
// schedule: { kind: 'every', everyMs: 120000 },
|
|
134
|
+
// payload: { kind: 'agentTurn', message: 'check the weather' },
|
|
135
|
+
// sessionTarget: 'isolated',
|
|
136
|
+
// delivery: { mode: 'announce' },
|
|
137
|
+
// }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## ORM Data Model
|
|
141
|
+
|
|
142
|
+
When persisting cron data with `@stonyx/orm`, use the following model structure. Reference implementations are in `@stonyx/cron`'s `test/sample/`.
|
|
143
|
+
|
|
144
|
+
### Models
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
models/
|
|
148
|
+
cron-job.js # name, enabled, deleteAfterRun, sessionTarget, wakeMode, timestamps
|
|
149
|
+
cron-job/
|
|
150
|
+
schedule.js # kind, at, everyMs, anchorMs, expr, tz
|
|
151
|
+
payload.js # kind, message, text
|
|
152
|
+
state.js # nextRunAtMs, lastRunAtMs, lastStatus, consecutiveErrors, etc.
|
|
153
|
+
delivery.js # mode
|
|
154
|
+
cron-run.js # jobId, status, error, summary, runAtMs, durationMs, ts
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Property ordering:** `attr()` → `belongsTo()` (on parent model)
|
|
158
|
+
|
|
159
|
+
The parent `cron-job` model uses `belongsTo` for schedule, payload, state, and delivery sub-models. This follows the property flattening rule — no passthrough objects.
|
|
160
|
+
|
|
161
|
+
### DB Schema
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
import { Model, hasMany } from '@stonyx/orm';
|
|
165
|
+
|
|
166
|
+
export default class DBModel extends Model {
|
|
167
|
+
cronJobs = hasMany('cron-job');
|
|
168
|
+
cronJobSchedules = hasMany('cron-job/schedule');
|
|
169
|
+
cronJobPayloads = hasMany('cron-job/payload');
|
|
170
|
+
cronJobStates = hasMany('cron-job/state');
|
|
171
|
+
cronJobDeliveries = hasMany('cron-job/delivery');
|
|
172
|
+
cronRuns = hasMany('cron-run');
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Serializers
|
|
177
|
+
|
|
178
|
+
Identity maps for all cron models — field names match the model attributes directly.
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
// config/environment.js
|
|
184
|
+
export default {
|
|
185
|
+
cron: {
|
|
186
|
+
log: true, // enable cron logging (uses stonyx/log)
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## When to Use Which
|
|
192
|
+
|
|
193
|
+
- **Simple recurring callback** → `Cron.register(key, callback, interval)`
|
|
194
|
+
- **Scheduled jobs with history, CRUD, AI input** → `CronService`
|
|
195
|
+
- **Never use raw `setInterval` or `setTimeout`** for recurring work
|
|
@@ -86,9 +86,22 @@ All pub/sub event handling. Never create custom event emitters.
|
|
|
86
86
|
|
|
87
87
|
All scheduled and interval tasks. Never use raw `setInterval` or `setTimeout` for recurring work.
|
|
88
88
|
|
|
89
|
+
**Legacy API** — simple recurring callbacks:
|
|
90
|
+
|
|
89
91
|
- `register(key, callback, interval, runOnInit?)` — schedule a recurring job
|
|
90
92
|
- `unregister(key)` — cancel a job
|
|
91
93
|
|
|
94
|
+
**Advanced API** (`@stonyx/cron/service`) — full scheduling with CRUD, run history, and AI normalization:
|
|
95
|
+
|
|
96
|
+
- `add(input)` / `get(id)` / `update(id, patch)` / `remove(id)` / `list(opts?)` — CRUD
|
|
97
|
+
- `run(id, mode?)` — manual execution (`'force'` or `'due'`)
|
|
98
|
+
- `runs(id, limit?)` — run history
|
|
99
|
+
- `onJobDue` — callback for job execution
|
|
100
|
+
|
|
101
|
+
Three schedule kinds: `every` (interval), `cron` (expression), `at` (one-shot).
|
|
102
|
+
|
|
103
|
+
See [Cron Conventions](./cron-conventions.md) for full details.
|
|
104
|
+
|
|
92
105
|
Configurable via `config/environment.js`:
|
|
93
106
|
|
|
94
107
|
```js
|
|
@@ -18,6 +18,7 @@ Universal rules that apply to every Stonyx project. Section-specific conventions
|
|
|
18
18
|
|
|
19
19
|
- [Project Structure](./project-structure.md) — directory layout, file organization, config conventions
|
|
20
20
|
- [Framework Modules](./framework-modules.md) — when to use which `@stonyx/*` module
|
|
21
|
+
- [Cron Conventions](./cron-conventions.md) — scheduling, job model, CronService API, ORM data model
|
|
21
22
|
- [ORM Conventions](./orm-conventions.md) — models, serializers, access control, transforms, hooks
|
|
22
23
|
- [REST Conventions](./rest-conventions.md) — REST server request classes and handlers
|
|
23
24
|
- [Discord Conventions](./discord-conventions.md) — Discord bot commands and event handlers
|
|
@@ -156,3 +156,35 @@ export default class DBModel extends Model {
|
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
Located at `config/db-schema.js`, referenced from `config/environment.js`.
|
|
159
|
+
|
|
160
|
+
## Store
|
|
161
|
+
|
|
162
|
+
The store is the in-memory data layer. Use it for internal app logic (not REST API consumers).
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
import Orm, { store, createRecord, updateRecord } from '@stonyx/orm';
|
|
166
|
+
|
|
167
|
+
// Read — sync, from memory (requires memory: true on model, which is the default)
|
|
168
|
+
const record = store.get('animal', 1);
|
|
169
|
+
const allAnimals = store.data.get('animals');
|
|
170
|
+
|
|
171
|
+
// Read — async, queries DB if memory: false
|
|
172
|
+
const record = await store.find('animal', 1);
|
|
173
|
+
const all = await store.findAll('animal');
|
|
174
|
+
|
|
175
|
+
// Create — adds to store and returns the record
|
|
176
|
+
const animal = createRecord('animal', { type: 'dog', age: 3, owner: 'angela' });
|
|
177
|
+
|
|
178
|
+
// Update — patches fields on an existing record
|
|
179
|
+
updateRecord(animal, { age: 4 });
|
|
180
|
+
|
|
181
|
+
// Delete
|
|
182
|
+
store.remove('animal', 1);
|
|
183
|
+
|
|
184
|
+
// Persist to disk — required when not using REST-triggered autosave
|
|
185
|
+
await Orm.db.save();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**When to use `Orm.db.save()`**: The `autosave: 'onUpdate'` config only triggers on REST POST/PATCH/DELETE. When modifying data directly via `createRecord` / `updateRecord` in app code (not through REST), call `Orm.db.save()` explicitly to persist.
|
|
189
|
+
|
|
190
|
+
**Store vs REST**: Use `store` for internal app logic (session tracking, state management). Use REST request handlers for external API consumers. Both operate on the same in-memory data.
|
package/docs/modules.md
CHANGED
|
@@ -72,11 +72,7 @@ await waitForModule('rest-server'); // Waits for @stonyx/rest-server
|
|
|
72
72
|
| Module | Description |
|
|
73
73
|
|--------|-------------|
|
|
74
74
|
| [@stonyx/cron](https://github.com/abofs/stonyx-cron) | Lightweight async job scheduling with min-heap |
|
|
75
|
-
| [@stonyx/discord](https://github.com/abofs/stonyx-discord) | Discord bot with command and event handler auto-discovery |
|
|
76
|
-
| [@stonyx/events](https://github.com/abofs/stonyx-events) | Pub/sub event system |
|
|
77
|
-
| [@stonyx/oauth](https://github.com/abofs/stonyx-oauth) | OAuth provider integration |
|
|
78
|
-
| [@stonyx/orm](https://github.com/abofs/stonyx-orm) | ORM with models, relationships, serializers, and optional REST integration |
|
|
79
75
|
| [@stonyx/rest-server](https://github.com/abofs/stonyx-rest-server) | Dynamic REST server with auto-route registration |
|
|
80
|
-
| [@stonyx/
|
|
76
|
+
| [@stonyx/orm](https://github.com/abofs/stonyx-orm) | ORM with models, relationships, serializers, and optional REST integration |
|
|
81
77
|
|
|
82
78
|
See each module's repository for its specific documentation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stonyx",
|
|
3
|
-
"version": "0.2.3-alpha.
|
|
3
|
+
"version": "0.2.3-alpha.8",
|
|
4
4
|
"description": "Base stonyx framework module",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"type": "module",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"url": "https://github.com/abofs/stonyx/issues"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@stonyx/utils": "0.2.3-beta.5",
|
|
34
33
|
"node-chronicle": "^0.2.0"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
36
|
+
"@stonyx/utils": "0.2.3-beta.7",
|
|
37
37
|
"qunit": "^2.24.1",
|
|
38
38
|
"sinon": "^21.0.0"
|
|
39
39
|
},
|
package/src/cli.js
CHANGED