Update (and automate) events docs (#1164)

* documentation: new documentation for emit features

* Update events documentation and doc_helper.js script

* Update package.json

Co-authored-by: Eric Dubé <eric.alex.dube@gmail.com>

* added manual_overrides.json.js file

* Update doc_helper.js to resolve paths relative to project root and update events documentation

* created a new events.md generated file, differences can be seen.

* added and populated events.md file. Can now compare new and old versions

---------

Co-authored-by: Eric Dubé <eric.alex.dube@gmail.com>
This commit is contained in:
ajaykrishnaswamy
2025-03-16 12:41:14 -04:00
committed by GitHub
parent 947dca96a2
commit 782e858b3c
6 changed files with 2053 additions and 19 deletions

View File

@@ -1,4 +1,170 @@
export default [
{
id: 'core.ai.prompt.check-usage',
description: `
This event is emitted for ai prompt check usage operations.
`,
properties: {
allow: {
type: 'boolean',
mutability: 'mutable',
summary: 'whether the operation is allowed',
notes: [],
},
intended_service: {
type: 'any',
mutability: 'mutable',
summary: 'intended service',
notes: [],
},
parameters: {
type: 'any',
mutability: 'mutable',
summary: 'parameters',
notes: [],
}
},
},
{
id: 'core.ai.prompt.complete',
description: `
This event is emitted for ai prompt complete operations.
`,
properties: {
username: {
type: 'string',
mutability: 'mutable',
summary: 'username',
notes: [],
},
intended_service: {
type: 'any',
mutability: 'mutable',
summary: 'intended service',
notes: [],
},
parameters: {
type: 'any',
mutability: 'mutable',
summary: 'parameters',
notes: [],
},
result: {
type: 'any',
mutability: 'mutable',
summary: 'result',
notes: [],
},
model_used: {
type: 'any',
mutability: 'mutable',
summary: 'model used',
notes: [],
},
service_used: {
type: 'any',
mutability: 'mutable',
summary: 'service used',
notes: [],
}
},
},
{
id: 'core.ai.prompt.report-usage',
description: `
This event is emitted for ai prompt report usage operations.
`,
},
{
id: 'core.ai.prompt.validate',
description: `
This event is emitted when a validate is being validated.
The event can be used to block certain validates from being validated.
`,
properties: {
allow: {
type: 'boolean',
mutability: 'mutable',
summary: 'whether the operation is allowed',
notes: [
'If set to false, the ai will be considered invalid.',
],
},
intended_service: {
type: 'any',
mutability: 'mutable',
summary: 'intended service',
notes: [],
},
parameters: {
type: 'any',
mutability: 'mutable',
summary: 'parameters',
notes: [],
}
},
},
{
id: 'core.app.new-icon',
description: `
This event is emitted for app new icon operations.
`,
properties: {
app_uid: {
type: 'string',
mutability: 'no-effect',
summary: 'app uid',
notes: [],
},
data_url: {
type: 'any',
mutability: 'no-effect',
summary: 'data url',
notes: [],
}
},
},
{
id: 'core.app.rename',
description: `
This event is emitted for app rename operations.
`,
properties: {
app_uid: {
type: 'string',
mutability: 'no-effect',
summary: 'app uid',
notes: [],
},
data_url: {
type: 'any',
mutability: 'no-effect',
summary: 'data url',
notes: [],
}
},
},
{
id: 'core.apps.invalidate',
description: `
This event is emitted when a invalidate is being validated.
The event can be used to block certain invalidates from being validated.
`,
properties: {
options: {
type: 'any',
mutability: 'no-effect',
summary: 'options',
notes: [],
},
apps: {
type: 'any',
mutability: 'no-effect',
summary: 'apps',
notes: [],
}
},
},
{
id: 'core.email.validate',
description: `
@@ -11,7 +177,7 @@ export default [
mutability: 'no-effect',
summary: 'the email being validated',
notes: [
'The email may have already been cleaned.',
'The email may have already been cleaned.'
]
},
allow: {
@@ -19,11 +185,386 @@ export default [
mutability: 'mutable',
summary: 'whether the email is allowed',
notes: [
'If set to false, the email will be considered invalid.',
'If set to false, the email will be considered invalid.'
]
},
}
},
},
{
id: 'core.fs.create.directory',
description: `
This event is emitted when a directory is created.
`,
properties: {
node: {
type: 'FSNodeContext',
mutability: 'no-effect',
summary: 'the directory that was created'
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context'
}
},
},
{
id: 'core.fs.create.file',
description: `
This event is emitted when a file is created.
`,
properties: {
node: {
type: 'FSNodeContext',
mutability: 'no-effect',
summary: 'the file that was affected',
notes: [],
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context',
notes: [],
}
},
},
{
id: 'core.fs.create.shortcut',
description: `
This event is emitted when a shortcut is created.
`,
},
{
id: 'core.fs.create.symlink',
description: `
This event is emitted when a symlink is created.
`,
},
{
id: 'core.fs.move.file',
description: `
This event is emitted for fs move file operations.
`,
properties: {
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context',
notes: [],
},
moved: {
type: 'any',
mutability: 'no-effect',
summary: 'moved',
notes: [],
},
old_path: {
type: 'string',
mutability: 'no-effect',
summary: 'path to the affected resource',
notes: [],
}
},
},
{
id: 'core.fs.pending.file',
description: `
This event is emitted for fs pending file operations.
`,
},
{
id: 'core.fs.storage.progress.copy',
description: `
This event reports progress of a copy operation.
`,
properties: {
upload_tracker: {
type: 'ProgressTracker',
mutability: 'no-effect',
summary: 'tracks progress of the operation',
notes: [],
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context',
notes: [],
},
meta: {
type: 'object',
mutability: 'no-effect',
summary: 'additional metadata for the operation',
notes: [],
},
item_path: {
type: 'string',
mutability: 'no-effect',
summary: 'path to the affected resource',
notes: [],
}
},
},
{
id: 'core.fs.storage.upload-progress',
description: `
This event reports progress of a upload-progress operation.
`,
},
{
id: 'core.fs.write.file',
description: `
This event is emitted when a file is updated.
`,
properties: {
node: {
type: 'FSNodeContext',
mutability: 'no-effect',
summary: 'the file that was affected',
notes: [],
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context',
notes: [],
}
},
},
{
id: 'core.ip.validate',
description: `
This event is emitted when a validate is being validated.
The event can be used to block certain validates from being validated.
`,
properties: {
allow: {
type: 'boolean',
mutability: 'mutable',
summary: 'whether the operation is allowed',
notes: [
'If set to false, the ip will be considered invalid.',
],
},
ip: {
type: 'any',
mutability: 'mutable',
summary: 'ip',
notes: [],
}
},
},
{
id: 'core.outer.fs.write-hash',
description: `
This event is emitted when a write-hash is updated.
`,
properties: {
hash: {
type: 'any',
mutability: 'no-effect',
summary: 'hash',
notes: [],
},
uuid: {
type: 'string',
mutability: 'no-effect',
summary: 'uuid',
notes: [],
}
},
},
{
id: 'core.outer.gui.item.added',
description: `
This event is emitted for outer gui item added operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.item.moved',
description: `
This event is emitted for outer gui item moved operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.item.pending',
description: `
This event is emitted for outer gui item pending operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.item.updated',
description: `
This event is emitted when a updated is updated.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.notif.ack',
description: `
This event is emitted for outer gui notif ack operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.notif.message',
description: `
This event is emitted for outer gui notif message operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
},
notification: {
type: 'any',
mutability: 'no-effect',
summary: 'notification',
notes: [],
}
},
},
{
id: 'core.outer.gui.notif.persisted',
description: `
This event is emitted for outer gui notif persisted operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.notif.unreads',
description: `
This event is emitted for outer gui notif unreads operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.outer.gui.submission.done',
description: `
This event is emitted for outer gui submission done operations.
`,
properties: {
user_id_list: {
type: 'string',
mutability: 'no-effect',
summary: 'user id list',
notes: [],
},
response: {
type: 'any',
mutability: 'no-effect',
summary: 'response',
notes: [],
}
},
},
{
id: 'core.puter-exec.submission.done',
description: `
This event is emitted for puter-exec submission done operations.
`,
},
{
id: 'core.request.measured',
description: `
@@ -39,30 +580,174 @@ export default [
const actor = data.actor; // instance of Actor
console.log('\\x1B[36;1m === MEASUREMENT ===\\x1B[0m\\n', {
console.log('\x1B[36;1m === MEASUREMENT ===\x1B[0m\n', {
actor: data.actor.uid,
measurements: data.measurements
});
});
`
}
},
},
{
id: 'core.fs.create.directory',
id: 'core.sns',
description: `
This event is emitted when a directory is created.
This event is emitted for sns operations.
`,
properties: {
node: {
type: 'FSNodeContext',
message: {
type: 'any',
mutability: 'no-effect',
summary: 'the directory that was created',
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context'
},
}
summary: 'message',
notes: [],
}
},
},
{
id: 'core.template-service.hello',
description: `
This event is emitted for template-service hello operations.
`,
properties: {
message: {
type: 'any',
mutability: 'no-effect',
summary: 'message',
notes: [],
}
},
},
{
id: 'core.usages.query',
description: `
This event is emitted for usages query operations.
`,
properties: {
actor: {
type: 'any',
mutability: 'no-effect',
summary: 'actor',
notes: [],
},
usages: {
type: 'any',
mutability: 'no-effect',
summary: 'usages',
notes: [],
}
},
},
{
id: 'core.user.email-changed',
description: `
This event is emitted for user email changed operations.
`,
properties: {
user_id: {
type: 'string',
mutability: 'no-effect',
summary: 'user id',
notes: [],
},
new_email: {
type: 'any',
mutability: 'no-effect',
summary: 'new email',
notes: [],
}
},
},
{
id: 'core.user.email-confirmed',
description: `
This event is emitted for user email confirmed operations.
`,
properties: {
user_uid: {
type: 'string',
mutability: 'no-effect',
summary: 'user uid',
notes: [],
},
email: {
type: 'any',
mutability: 'no-effect',
summary: 'email',
notes: [],
}
},
},
{
id: 'core.user.save_account',
description: `
This event is emitted for user save_account operations.
`,
properties: {
user: {
type: 'User',
mutability: 'no-effect',
summary: 'user associated with the operation',
notes: [],
}
},
},
{
id: 'core.web.socket.connected',
description: `
This event is emitted for web socket connected operations.
`,
properties: {
socket: {
type: 'any',
mutability: 'mutable',
summary: 'socket',
notes: [],
},
user: {
type: 'User',
mutability: 'mutable',
summary: 'user associated with the operation',
notes: [],
}
},
},
{
id: 'core.web.socket.user-connected',
description: `
This event is emitted for web socket user connected operations.
`,
properties: {
socket: {
type: 'any',
mutability: 'mutable',
summary: 'socket',
notes: [],
},
user: {
type: 'User',
mutability: 'mutable',
summary: 'user associated with the operation',
notes: [],
}
},
},
{
id: 'core.wisp.get-policy',
description: `
This event is emitted for wisp get policy operations.
`,
properties: {
allow: {
type: 'boolean',
mutability: 'mutable',
summary: 'whether the operation is allowed',
notes: [],
},
policy: {
type: 'Policy',
mutability: 'mutable',
summary: 'policy information for the operation',
notes: [],
}
},
}
];

View File

@@ -19,7 +19,6 @@ whether the email is allowed
- **Notes**:
- If set to false, the email will be considered invalid.
### `core.request.measured`
This event is emitted when a requests incoming and outgoing bytes
@@ -57,4 +56,654 @@ current context
- **Type**: Context
- **Mutability**: no-effect
### `core.ai.prompt.check-usage`
This event is emitted for ai prompt check usage operations.
#### Property `allow`
whether the operation is allowed
- **Type**: boolean
- **Mutability**: mutable
- **Notes**:
#### Property `intended_service`
intended service
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `parameters`
parameters
- **Type**: any
- **Mutability**: mutable
- **Notes**:
### `core.ai.prompt.complete`
This event is emitted for ai prompt complete operations.
#### Property `username`
username
- **Type**: string
- **Mutability**: mutable
- **Notes**:
#### Property `intended_service`
intended service
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `parameters`
parameters
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `result`
result
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `model_used`
model used
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `service_used`
service used
- **Type**: any
- **Mutability**: mutable
- **Notes**:
### `core.ai.prompt.report-usage`
This event is emitted for ai prompt report usage operations.
### `core.ai.prompt.validate`
This event is emitted when a validate is being validated.
The event can be used to block certain validates from being validated.
#### Property `allow`
whether the operation is allowed
- **Type**: boolean
- **Mutability**: mutable
- **Notes**:
- If set to false, the ai will be considered invalid.
#### Property `intended_service`
intended service
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `parameters`
parameters
- **Type**: any
- **Mutability**: mutable
- **Notes**:
### `core.app.new-icon`
This event is emitted for app new icon operations.
#### Property `app_uid`
app uid
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `data_url`
data url
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.app.rename`
This event is emitted for app rename operations.
#### Property `app_uid`
app uid
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `data_url`
data url
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.apps.invalidate`
This event is emitted when a invalidate is being validated.
The event can be used to block certain invalidates from being validated.
#### Property `options`
options
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
#### Property `apps`
apps
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.fs.create.file`
This event is emitted when a file is created.
#### Property `node`
the file that was affected
- **Type**: FSNodeContext
- **Mutability**: no-effect
- **Notes**:
#### Property `context`
current context
- **Type**: Context
- **Mutability**: no-effect
- **Notes**:
### `core.fs.create.shortcut`
This event is emitted when a shortcut is created.
### `core.fs.create.symlink`
This event is emitted when a symlink is created.
### `core.fs.move.file`
This event is emitted for fs move file operations.
#### Property `context`
current context
- **Type**: Context
- **Mutability**: no-effect
- **Notes**:
#### Property `moved`
moved
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
#### Property `old_path`
path to the affected resource
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
### `core.fs.pending.file`
This event is emitted for fs pending file operations.
### `core.fs.storage.progress.copy`
This event reports progress of a copy operation.
#### Property `upload_tracker`
tracks progress of the operation
- **Type**: ProgressTracker
- **Mutability**: no-effect
- **Notes**:
#### Property `context`
current context
- **Type**: Context
- **Mutability**: no-effect
- **Notes**:
#### Property `meta`
additional metadata for the operation
- **Type**: object
- **Mutability**: no-effect
- **Notes**:
#### Property `item_path`
path to the affected resource
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
### `core.fs.storage.upload-progress`
This event reports progress of a upload-progress operation.
### `core.fs.write.file`
This event is emitted when a file is updated.
#### Property `node`
the file that was affected
- **Type**: FSNodeContext
- **Mutability**: no-effect
- **Notes**:
#### Property `context`
current context
- **Type**: Context
- **Mutability**: no-effect
- **Notes**:
### `core.ip.validate`
This event is emitted when a validate is being validated.
The event can be used to block certain validates from being validated.
#### Property `allow`
whether the operation is allowed
- **Type**: boolean
- **Mutability**: mutable
- **Notes**:
- If set to false, the ip will be considered invalid.
#### Property `ip`
ip
- **Type**: any
- **Mutability**: mutable
- **Notes**:
### `core.outer.fs.write-hash`
This event is emitted when a write-hash is updated.
#### Property `hash`
hash
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
#### Property `uuid`
uuid
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.item.added`
This event is emitted for outer gui item added operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.item.moved`
This event is emitted for outer gui item moved operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.item.pending`
This event is emitted for outer gui item pending operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.item.updated`
This event is emitted when a updated is updated.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.notif.ack`
This event is emitted for outer gui notif ack operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.notif.message`
This event is emitted for outer gui notif message operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
#### Property `notification`
notification
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.notif.persisted`
This event is emitted for outer gui notif persisted operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.notif.unreads`
This event is emitted for outer gui notif unreads operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.outer.gui.submission.done`
This event is emitted for outer gui submission done operations.
#### Property `user_id_list`
user id list
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `response`
response
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.puter-exec.submission.done`
This event is emitted for puter-exec submission done operations.
### `core.sns`
This event is emitted for sns operations.
#### Property `message`
message
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.template-service.hello`
This event is emitted for template-service hello operations.
#### Property `message`
message
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.usages.query`
This event is emitted for usages query operations.
#### Property `actor`
actor
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
#### Property `usages`
usages
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.user.email-changed`
This event is emitted for user email changed operations.
#### Property `user_id`
user id
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `new_email`
new email
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.user.email-confirmed`
This event is emitted for user email confirmed operations.
#### Property `user_uid`
user uid
- **Type**: string
- **Mutability**: no-effect
- **Notes**:
#### Property `email`
email
- **Type**: any
- **Mutability**: no-effect
- **Notes**:
### `core.user.save_account`
This event is emitted for user save_account operations.
#### Property `user`
user associated with the operation
- **Type**: User
- **Mutability**: no-effect
- **Notes**:
### `core.web.socket.connected`
This event is emitted for web socket connected operations.
#### Property `socket`
socket
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `user`
user associated with the operation
- **Type**: User
- **Mutability**: mutable
- **Notes**:
### `core.web.socket.user-connected`
This event is emitted for web socket user connected operations.
#### Property `socket`
socket
- **Type**: any
- **Mutability**: mutable
- **Notes**:
#### Property `user`
user associated with the operation
- **Type**: User
- **Mutability**: mutable
- **Notes**:
### `core.wisp.get-policy`
This event is emitted for wisp get policy operations.
#### Property `allow`
whether the operation is allowed
- **Type**: boolean
- **Mutability**: mutable
- **Notes**:
#### Property `policy`
policy information for the operation
- **Type**: Policy
- **Mutability**: mutable
- **Notes**:

View File

@@ -0,0 +1,68 @@
export default [
{
id: 'core.email.validate',
description: `
This event is emitted when an email is being validated.
The event can be used to block certain emails from being validated.
`,
properties: {
email: {
type: 'string',
mutability: 'no-effect',
summary: 'the email being validated',
notes: [
'The email may have already been cleaned.',
]
},
allow: {
type: 'boolean',
mutability: 'mutable',
summary: 'whether the email is allowed',
notes: [
'If set to false, the email will be considered invalid.',
]
},
},
},
{
id: 'core.request.measured',
description: `
This event is emitted when a requests incoming and outgoing bytes
have been measured.
`,
example: {
language: 'javascript',
code: /*javascript*/`
extension.on('core.request.measured', data => {
const measurements = data.measurements;
// measurements = { sz_incoming: integer, sz_outgoing: integer }
const actor = data.actor; // instance of Actor
console.log('\\x1B[36;1m === MEASUREMENT ===\\x1B[0m\\n', {
actor: data.actor.uid,
measurements: data.measurements
});
});
`
}
},
{
id: 'core.fs.create.directory',
description: `
This event is emitted when a directory is created.
`,
properties: {
node: {
type: 'FSNodeContext',
mutability: 'no-effect',
summary: 'the directory that was created',
},
context: {
type: 'Context',
mutability: 'no-effect',
summary: 'current context'
},
}
},
];

158
extensions/testex.js Normal file
View File

@@ -0,0 +1,158 @@
// Test extension for event listeners
extension.on('core.ai.prompt.check-usage', event => {
console.log('GOT AI.PROMPT.CHECK-USAGE EVENT', event);
});
extension.on('core.ai.prompt.complete', event => {
console.log('GOT AI.PROMPT.COMPLETE EVENT', event);
});
extension.on('core.ai.prompt.report-usage', event => {
console.log('GOT AI.PROMPT.REPORT-USAGE EVENT', event);
});
extension.on('core.ai.prompt.validate', event => {
console.log('GOT AI.PROMPT.VALIDATE EVENT', event);
});
extension.on('core.app.new-icon', event => {
console.log('GOT APP.NEW-ICON EVENT', event);
});
extension.on('core.app.rename', event => {
console.log('GOT APP.RENAME EVENT', event);
});
extension.on('core.apps.invalidate', event => {
console.log('GOT APPS.INVALIDATE EVENT', event);
});
extension.on('core.email.validate', event => {
console.log('GOT EMAIL.VALIDATE EVENT', event);
});
extension.on('core.fs.create.directory', event => {
console.log('GOT FS.CREATE.DIRECTORY EVENT', event);
});
extension.on('core.fs.create.file', event => {
console.log('GOT FS.CREATE.FILE EVENT', event);
});
extension.on('core.fs.create.shortcut', event => {
console.log('GOT FS.CREATE.SHORTCUT EVENT', event);
});
extension.on('core.fs.create.symlink', event => {
console.log('GOT FS.CREATE.SYMLINK EVENT', event);
});
extension.on('core.fs.move.file', event => {
console.log('GOT FS.MOVE.FILE EVENT', event);
});
extension.on('core.fs.pending.file', event => {
console.log('GOT FS.PENDING.FILE EVENT', event);
});
extension.on('core.fs.storage.progress.copy', event => {
console.log('GOT FS.STORAGE.PROGRESS.COPY EVENT', event);
});
extension.on('core.fs.storage.upload-progress', event => {
console.log('GOT FS.STORAGE.UPLOAD-PROGRESS EVENT', event);
});
extension.on('core.fs.write.file', event => {
console.log('GOT FS.WRITE.FILE EVENT', event);
});
extension.on('core.ip.validate', event => {
console.log('GOT IP.VALIDATE EVENT', event);
});
extension.on('core.outer.fs.write-hash', event => {
console.log('GOT OUTER.FS.WRITE-HASH EVENT', event);
});
extension.on('core.outer.gui.item.added', event => {
console.log('GOT OUTER.GUI.ITEM.ADDED EVENT', event);
});
extension.on('core.outer.gui.item.moved', event => {
console.log('GOT OUTER.GUI.ITEM.MOVED EVENT', event);
});
extension.on('core.outer.gui.item.pending', event => {
console.log('GOT OUTER.GUI.ITEM.PENDING EVENT', event);
});
extension.on('core.outer.gui.item.updated', event => {
console.log('GOT OUTER.GUI.ITEM.UPDATED EVENT', event);
});
extension.on('core.outer.gui.notif.ack', event => {
console.log('GOT OUTER.GUI.NOTIF.ACK EVENT', event);
});
extension.on('core.outer.gui.notif.message', event => {
console.log('GOT OUTER.GUI.NOTIF.MESSAGE EVENT', event);
});
extension.on('core.outer.gui.notif.persisted', event => {
console.log('GOT OUTER.GUI.NOTIF.PERSISTED EVENT', event);
});
extension.on('core.outer.gui.notif.unreads', event => {
console.log('GOT OUTER.GUI.NOTIF.UNREADS EVENT', event);
});
extension.on('core.outer.gui.submission.done', event => {
console.log('GOT OUTER.GUI.SUBMISSION.DONE EVENT', event);
});
extension.on('core.puter-exec.submission.done', event => {
console.log('GOT PUTER-EXEC.SUBMISSION.DONE EVENT', event);
});
extension.on('core.request.measured', event => {
console.log('GOT REQUEST.MEASURED EVENT', event);
});
extension.on('core.sns', event => {
console.log('GOT SNS EVENT', event);
});
extension.on('core.template-service.hello', event => {
console.log('GOT TEMPLATE-SERVICE.HELLO EVENT', event);
});
extension.on('core.usages.query', event => {
console.log('GOT USAGES.QUERY EVENT', event);
});
extension.on('core.user.email-changed', event => {
console.log('GOT USER.EMAIL-CHANGED EVENT', event);
});
extension.on('core.user.email-confirmed', event => {
console.log('GOT USER.EMAIL-CONFIRMED EVENT', event);
});
extension.on('core.user.save_account', event => {
console.log('GOT USER.SAVE_ACCOUNT EVENT', event);
});
extension.on('core.web.socket.connected', event => {
console.log('GOT WEB.SOCKET.CONNECTED EVENT', event);
});
extension.on('core.web.socket.user-connected', event => {
console.log('GOT WEB.SOCKET.USER-CONNECTED EVENT', event);
});
extension.on('core.wisp.get-policy', event => {
console.log('GOT WISP.GET-POLICY EVENT', event);
});

View File

@@ -47,7 +47,6 @@
]
},
"dependencies": {
"@heyputer/putility": "^1.0.2",
"dedent": "^1.5.3",
"javascript-time-ago": "^2.5.11",
"json-colorizer": "^3.0.1",

475
tools/doc_helper.js Normal file
View File

@@ -0,0 +1,475 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import manualOverrides from '../doc/contributors/extensions/manual_overrides.json.js';
// Get the directory name in ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Create a map of manual overrides for quick lookup
const manualOverridesMap = new Map();
manualOverrides.forEach(override => {
manualOverridesMap.set(override.id, override);
});
// Array to collect all warnings
const warnings = [];
// Add a function to detect and collect duplicate events
function checkForDuplicateEvent(eventId, filePath, seenEvents) {
if (seenEvents.has(eventId)) {
const existing = seenEvents.get(eventId);
if (existing.fromManualOverride) {
warnings.push(`Event ${eventId} found in ${filePath} but already defined in manual overrides. Using manual override.`);
} else {
warnings.push(`Duplicate event ${eventId} found in ${filePath}. First seen in ${existing.filename}.`);
}
return true;
}
return false;
}
function extractEventsFromFile(filePath, seenEvents, debugMode) {
const content = fs.readFileSync(filePath, 'utf-8');
// Use a more general regex to capture all event emissions
// This captures the event name and whatever is passed as the second argument
const regex = /svc_event\.emit\(['"]([^'"]+)['"]\s*,\s*([^)]+)\)/g;
let match;
while ((match = regex.exec(content)) !== null) {
const eventName = match[1];
const eventId = `core.${eventName}`;
const eventArg = match[2].trim();
// Check if this file contains code that might affect event.allow
const hasAllowEffect = content.includes('event.allow') ||
content.includes('.allow =') ||
content.includes('.allow=');
// Check for duplicate events and collect warnings
if (checkForDuplicateEvent(eventId, filePath, seenEvents)) {
continue; // Skip this event if it's a duplicate
}
// Check if this event has a manual override
if (manualOverridesMap.has(eventId)) {
// Use the manual override instead of generating a new definition
const override = manualOverridesMap.get(eventId);
// Mark this as coming from manual override for later reference
override.fromManualOverride = true;
seenEvents.set(eventId, override);
continue;
}
// Generate description based on event name
let description = generateDescription(eventName);
let propertyDetails = {};
// Case 1: Inline object - extract properties directly
if (eventArg.startsWith('{')) {
// Extract properties from inline object
const propertiesMatch = eventArg.match(/{([^}]*)}/);
if (propertiesMatch) {
const propertiesText = propertiesMatch[1];
extractProperties(propertiesText, propertyDetails, hasAllowEffect, eventName);
}
}
// Case 2: Variable reference - find variable definition
else {
const varName = eventArg.trim();
// Look for variable definition patterns like: const event = { prop1: value1 };
const varDefRegex = new RegExp(`(?:const|let|var)\\s+${varName}\\s*=\\s*{([^}]*)}`, 'g');
let varMatch;
if ((varMatch = varDefRegex.exec(content)) !== null) {
const propertiesText = varMatch[1];
extractProperties(propertiesText, propertyDetails, hasAllowEffect, eventName);
}
}
// Add the event to our collection
seenEvents.set(eventId, {
id: eventId,
event: eventName,
filename: path.basename(filePath),
description: description,
properties: propertyDetails,
fromManualOverride: false
});
}
}
// Helper function to extract properties from a properties text string
function extractProperties(propertiesText, propertyDetails, hasAllowEffect, eventName) {
const properties = propertiesText
.split(/\s*,\s*/)
.map(prop => prop.split(':')[0].trim())
.filter(prop => prop);
// Generate property details
properties.forEach(prop => {
propertyDetails[prop] = {
type: guessType(prop),
mutability: hasAllowEffect ? 'effect' : 'no-effect',
summary: guessSummary(prop, eventName)
};
});
}
function generateDescription(eventName) {
const parts = eventName.split('.');
if (parts.length >= 2) {
const system = parts[0];
const action = parts.slice(1).join('.');
if (action.includes('create')) {
return `This event is emitted when a ${parts[parts.length - 1]} is created.`;
} else if (action.includes('update') || action.includes('write')) {
return `This event is emitted when a ${parts[parts.length - 1]} is updated.`;
} else if (action.includes('delete') || action.includes('remove')) {
return `This event is emitted when a ${parts[parts.length - 1]} is deleted.`;
} else if (action.includes('progress')) {
return `This event reports progress of a ${parts[parts.length - 1]} operation.`;
} else if (action.includes('validate')) {
return `This event is emitted when a ${parts[parts.length - 1]} is being validated.\nThe event can be used to block certain ${parts[parts.length - 1]}s from being validated.`;
} else {
return `This event is emitted for ${system} ${action.replace(/[-\.]/g, ' ')} operations.`;
}
}
return `This event is emitted for ${eventName} operations.`;
}
function guessType(propertyName) {
// Guess the type based on property name
if (propertyName === 'node') return 'FSNodeContext';
if (propertyName === 'context') return 'Context';
if (propertyName === 'user') return 'User';
if (propertyName.includes('path')) return 'string';
if (propertyName.includes('id')) return 'string';
if (propertyName.includes('name')) return 'string';
if (propertyName.includes('progress')) return 'number';
if (propertyName.includes('tracker')) return 'ProgressTracker';
if (propertyName.includes('meta')) return 'object';
if (propertyName.includes('policy')) return 'Policy';
if (propertyName.includes('allow')) return 'boolean';
return 'any';
}
function guessSummary(propertyName, eventName) {
// Generate summary based on property name and event context
if (propertyName === 'node') {
const entityType = eventName.split('.').pop();
return `the ${entityType} that was affected`;
}
if (propertyName === 'context') return 'current context';
if (propertyName === 'user') return 'user associated with the operation';
if (propertyName.includes('path')) return 'path to the affected resource';
if (propertyName.includes('tracker')) return 'tracks progress of the operation';
if (propertyName.includes('meta')) return 'additional metadata for the operation';
if (propertyName.includes('policy')) return 'policy information for the operation';
if (propertyName.includes('allow')) return 'whether the operation is allowed';
// Default summary based on property name
return propertyName.replace(/_/g, ' ');
}
function scanDirectory(directory, seenEvents, debugMode) {
const files = fs.readdirSync(directory);
for (const file of files) {
const filePath = path.join(directory, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
scanDirectory(filePath, seenEvents, debugMode);
} else if (file.endsWith('.js')) {
try {
extractEventsFromFile(filePath, seenEvents, debugMode);
} catch (error) {
warnings.push(`Error processing file ${filePath}: ${error.message}`);
}
}
}
}
function generateTestExtension(events) {
let code = `// Test extension for event listeners\n\n`;
events.forEach(event => {
const eventId = event.id;
const eventName = event.event ? event.event.toUpperCase() : eventId.split('.').slice(1).join('.').toUpperCase();
code += `extension.on('${eventId}', event => {\n`;
code += ` console.log('GOT ${eventName} EVENT', event);\n`;
code += `});\n\n`;
});
return code;
}
function main() {
const args = process.argv.slice(2);
if (args.length < 1) {
console.error('Usage: node doc_helper.js <directory> [output_file] [--generate-test] [--test-dir=<directory>] [--debug]');
process.exit(1);
}
// Resolve directory path relative to project root
const directory = path.resolve(path.join(path.dirname(__dirname), args[0]));
let outputFile = null;
let generateTest = false;
let testOutputDir = "./extensions/";
let debugMode = false;
// Parse arguments
for (let i = 1; i < args.length; i++) {
if (args[i] === '--generate-test') {
generateTest = true;
} else if (args[i].startsWith('--test-dir=')) {
testOutputDir = args[i].substring('--test-dir='.length);
} else if (args[i] === '--debug') {
debugMode = true;
} else if (!args[i].startsWith('--')) {
// Only treat non-flag arguments as output file
outputFile = path.resolve(path.join(path.dirname(__dirname), args[i]));
}
}
// Resolve test output directory relative to project root if it's not an absolute path
if (!path.isAbsolute(testOutputDir)) {
testOutputDir = path.resolve(path.join(path.dirname(__dirname), testOutputDir));
}
const seenEvents = new Map();
// First, add all manual overrides to the seenEvents map
manualOverrides.forEach(override => {
// Mark this as coming from manual override for later reference
override.fromManualOverride = true;
seenEvents.set(override.id, override);
});
// Then scan the directory for additional events
scanDirectory(directory, seenEvents, debugMode);
// Check for any manual overrides that weren't used
manualOverrides.forEach(override => {
const event = seenEvents.get(override.id);
if (!event || !event.fromManualOverride) {
warnings.push(`Manual override for ${override.id} exists but no matching event was found in the codebase.`);
}
});
const result = Array.from(seenEvents.values());
// Sort events alphabetically by ID
result.sort((a, b) => a.id.localeCompare(b.id));
// Format the output to match events.json.js
const formattedOutput = formatEventsOutput(result);
// Output the result
if (outputFile) {
fs.writeFileSync(outputFile, formattedOutput);
console.log(`Event metadata written to ${outputFile}`);
} else {
console.log(formattedOutput);
}
// Generate test extension file if requested
if (generateTest) {
const testCode = generateTestExtension(result);
// Ensure the output directory exists
if (!fs.existsSync(testOutputDir)) {
fs.mkdirSync(testOutputDir, { recursive: true });
}
const testFilePath = path.join(testOutputDir, 'testex.js');
fs.writeFileSync(testFilePath, testCode);
console.log(`Test extension file generated: ${testFilePath}`);
}
// Print warnings in the requested format
if (warnings.length > 0) {
// Collect duplicate events
const duplicateEvents = new Set();
const overrideEvents = new Set();
const otherWarnings = [];
warnings.forEach(warning => {
if (warning.includes("Duplicate event")) {
// Extract event ID from the warning message
const match = warning.match(/Duplicate event (core\.[^ ]+)/);
if (match && match[1]) {
duplicateEvents.add(match[1]);
}
} else if (warning.includes("already defined in manual overrides")) {
// Extract event ID from the warning message
const match = warning.match(/Event (core\.[^ ]+) found/);
if (match && match[1]) {
overrideEvents.add(match[1]);
}
} else {
otherWarnings.push(warning);
}
});
// Output in the requested format
console.log(`\nduplicate events: ${Array.from(duplicateEvents).join(', ')}`);
console.log(`Override events: ${Array.from(overrideEvents).join(', ')}`);
// If there are any other warnings, print them too
if (otherWarnings.length > 0) {
console.log("\nOther warnings:");
otherWarnings.forEach(warning => {
console.log(`- ${warning}`);
});
}
}
}
/**
* Format the events data to match the events.json.js format
*/
function formatEventsOutput(events) {
let output = 'export default [\n';
events.forEach((event, index) => {
// Check if this is a manual override
if (event.fromManualOverride) {
// This is a manual override, output it exactly as defined
output += ' {\n';
output += ` id: '${event.id}',\n`;
output += ` description: \``;
// Format the description with proper indentation, preserving original formatting
// Don't add extra newlines before or after the description
output += event.description;
output += `\`,\n`;
// Add properties if they exist, preserving exact format
if (event.properties && Object.keys(event.properties).length > 0) {
output += ' properties: {\n';
Object.entries(event.properties).forEach(([propName, propDetails], propIndex) => {
output += ` ${propName}: {\n`;
output += ` type: '${propDetails.type}',\n`;
output += ` mutability: '${propDetails.mutability}',\n`;
output += ` summary: '${propDetails.summary}'`;
// Add notes array if it exists
if (propDetails.notes && propDetails.notes.length > 0) {
output += `,\n notes: [\n`;
propDetails.notes.forEach((note, noteIndex) => {
output += ` '${note}'`;
if (noteIndex < propDetails.notes.length - 1) {
output += ',';
}
output += '\n';
});
output += ` ]`;
}
output += '\n }';
// Add comma if not the last property
if (propIndex < Object.keys(event.properties).length - 1) {
output += ',';
}
output += '\n';
});
output += ' },\n';
}
// Add example if it exists
if (event.example) {
output += ' example: {\n';
output += ` language: '${event.example.language}',\n`;
output += ` code: /*${event.example.language}*/\``;
// Preserve the exact formatting of the example code
// Don't add extra newlines and preserve escape sequences exactly as they are
output += event.example.code;
output += `\`\n`;
output += ' },\n';
}
output += ' }';
} else {
// This is an auto-generated event
output += ' {\n';
output += ` id: '${event.id}',\n`;
output += ` description: \`\n`;
// Format the description with proper indentation
const descriptionLines = event.description.split('\n');
descriptionLines.forEach(line => {
output += ` ${line}\n`;
});
output += ` \`,\n`;
// Add properties if they exist
if (Object.keys(event.properties).length > 0) {
output += ' properties: {\n';
Object.entries(event.properties).forEach(([propName, propDetails], propIndex) => {
output += ` ${propName}: {\n`;
output += ` type: '${propDetails.type}',\n`;
output += ` mutability: '${propDetails.mutability === 'effect' ? 'mutable' : 'no-effect'}',\n`;
output += ` summary: '${propDetails.summary}',\n`;
// Add notes array with appropriate content
if (propName === 'allow' && event.event.includes('validate')) {
output += ` notes: [\n`;
output += ` 'If set to false, the ${event.event.split('.')[0]} will be considered invalid.',\n`;
output += ` ],\n`;
} else if (propName === 'email' && event.event.includes('validate')) {
output += ` notes: [\n`;
output += ` 'The email may have already been cleaned.',\n`;
output += ` ],\n`;
} else {
output += ` notes: [],\n`;
}
output += ' }';
// Add comma if not the last property
if (propIndex < Object.keys(event.properties).length - 1) {
output += ',';
}
output += '\n';
});
output += ' },\n';
}
output += ' }';
}
// Add comma if not the last event
if (index < events.length - 1) {
output += ',';
}
output += '\n';
});
output += '];\n';
return output;
}
main();
// Updated Sun Mar 9 23:52:51 EDT 2025