adonisjs-pulse 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +119 -0
  3. package/build/configure.d.ts +2 -0
  4. package/build/configure.js +47 -0
  5. package/build/index.d.ts +5 -0
  6. package/build/index.js +5 -0
  7. package/build/providers/pulse_provider.d.ts +16 -0
  8. package/build/providers/pulse_provider.js +64 -0
  9. package/build/resources/views/components/card-header.edge +30 -0
  10. package/build/resources/views/components/card.edge +8 -0
  11. package/build/resources/views/components/no-results.edge +3 -0
  12. package/build/resources/views/components/pulse.edge +79 -0
  13. package/build/resources/views/components/scroll.edge +3 -0
  14. package/build/resources/views/components/table.edge +3 -0
  15. package/build/resources/views/components/td.edge +4 -0
  16. package/build/resources/views/components/th.edge +4 -0
  17. package/build/resources/views/components/thead.edge +3 -0
  18. package/build/resources/views/components/theme-switcher.edge +48 -0
  19. package/build/resources/views/dashboard.edge +7 -0
  20. package/build/resources/views/livewire/cache.edge +111 -0
  21. package/build/resources/views/livewire/exceptions.edge +48 -0
  22. package/build/resources/views/livewire/period-selector.edge +11 -0
  23. package/build/resources/views/livewire/servers.edge +86 -0
  24. package/build/resources/views/livewire/slow-queries.edge +50 -0
  25. package/build/resources/views/livewire/slow-requests.edge +62 -0
  26. package/build/resources/views/livewire/usage.edge +63 -0
  27. package/build/services/pulse.d.ts +3 -0
  28. package/build/services/pulse.js +6 -0
  29. package/build/src/entry.d.ts +24 -0
  30. package/build/src/entry.js +67 -0
  31. package/build/src/livewire/cache.d.ts +4 -0
  32. package/build/src/livewire/cache.js +33 -0
  33. package/build/src/livewire/card.d.ts +22 -0
  34. package/build/src/livewire/card.js +96 -0
  35. package/build/src/livewire/exceptions.d.ts +4 -0
  36. package/build/src/livewire/exceptions.js +29 -0
  37. package/build/src/livewire/period_selector.d.ts +5 -0
  38. package/build/src/livewire/period_selector.js +16 -0
  39. package/build/src/livewire/servers.d.ts +4 -0
  40. package/build/src/livewire/servers.js +36 -0
  41. package/build/src/livewire/slow_queries.d.ts +5 -0
  42. package/build/src/livewire/slow_queries.js +37 -0
  43. package/build/src/livewire/slow_requests.d.ts +5 -0
  44. package/build/src/livewire/slow_requests.js +37 -0
  45. package/build/src/pulse.d.ts +44 -0
  46. package/build/src/pulse.js +224 -0
  47. package/build/src/recorders/cache_interactions.d.ts +34 -0
  48. package/build/src/recorders/cache_interactions.js +80 -0
  49. package/build/src/recorders/exceptions.d.ts +8 -0
  50. package/build/src/recorders/exceptions.js +81 -0
  51. package/build/src/recorders/index.d.ts +7 -0
  52. package/build/src/recorders/index.js +6 -0
  53. package/build/src/recorders/recorder.d.ts +24 -0
  54. package/build/src/recorders/recorder.js +45 -0
  55. package/build/src/recorders/servers.d.ts +24 -0
  56. package/build/src/recorders/servers.js +133 -0
  57. package/build/src/recorders/slow_queries.d.ts +8 -0
  58. package/build/src/recorders/slow_queries.js +27 -0
  59. package/build/src/recorders/slow_requests.d.ts +11 -0
  60. package/build/src/recorders/slow_requests.js +80 -0
  61. package/build/src/storage/database_storage.d.ts +20 -0
  62. package/build/src/storage/database_storage.js +357 -0
  63. package/build/src/types.d.ts +91 -0
  64. package/build/src/types.js +3 -0
  65. package/build/src/value.d.ts +9 -0
  66. package/build/src/value.js +20 -0
  67. package/build/stubs/config.stub +58 -0
  68. package/build/stubs/main.d.ts +5 -0
  69. package/build/stubs/main.js +7 -0
  70. package/build/stubs/migration.stub +58 -0
  71. package/package.json +100 -0
@@ -0,0 +1,91 @@
1
+ import type { Entry } from './entry.js';
2
+ import type { Value } from './value.js';
3
+ import type { Recorder, RecorderOptions } from './recorders/recorder.js';
4
+ export type AggregationType = 'count' | 'min' | 'max' | 'sum' | 'avg';
5
+ export interface EntryAttributes {
6
+ timestamp: number;
7
+ type: string;
8
+ key: string;
9
+ value: number | null;
10
+ }
11
+ export interface ValueAttributes {
12
+ timestamp: number;
13
+ type: string;
14
+ key: string;
15
+ value: string;
16
+ }
17
+ export interface StoredEntry extends EntryAttributes {
18
+ id: number;
19
+ key_hash: string;
20
+ }
21
+ export interface StoredValue extends ValueAttributes {
22
+ id: number;
23
+ key_hash: string;
24
+ }
25
+ export interface StoredAggregate {
26
+ id: number;
27
+ bucket: number;
28
+ period: number;
29
+ type: string;
30
+ key: string;
31
+ key_hash: string;
32
+ aggregate: AggregationType;
33
+ value: number;
34
+ count: number | null;
35
+ }
36
+ export interface AggregateResult {
37
+ key: string;
38
+ count?: number;
39
+ min?: number;
40
+ max?: number;
41
+ sum?: number;
42
+ avg?: number;
43
+ }
44
+ export interface AggregateTypesResult {
45
+ key: string;
46
+ [type: string]: string | number | undefined;
47
+ }
48
+ export type LazyRecorder = () => Promise<{
49
+ default: new (options?: RecorderOptions) => Recorder;
50
+ }>;
51
+ export interface RecorderEntry extends RecorderOptions {
52
+ recorder: LazyRecorder;
53
+ }
54
+ export interface PulseConfig {
55
+ enabled: boolean;
56
+ storage: {
57
+ connection?: string;
58
+ };
59
+ ingest: {
60
+ interval: number;
61
+ lottery: {
62
+ chance: number;
63
+ outOf: number;
64
+ };
65
+ };
66
+ recorders: RecorderEntry[];
67
+ }
68
+ export interface ResolvedPulseConfig {
69
+ enabled: boolean;
70
+ ingestInterval: number;
71
+ trimLotteryChance: [number, number];
72
+ connection?: string;
73
+ retain: {
74
+ entries: number;
75
+ values: number;
76
+ aggregates: number;
77
+ };
78
+ recorders: RecorderEntry[];
79
+ }
80
+ export interface PulseStorage {
81
+ store(entries: Entry[], values: Value[]): Promise<void>;
82
+ trim(): Promise<void>;
83
+ purge(types?: string[]): Promise<void>;
84
+ values(type: string, keys?: string[]): Promise<StoredValue[]>;
85
+ aggregate(type: string, aggregates: AggregationType[], interval: number, orderBy?: AggregationType, direction?: 'asc' | 'desc', limit?: number): Promise<Map<string, AggregateResult>>;
86
+ aggregateTypes(types: string[], aggregate: AggregationType, interval: number, orderBy?: string, direction?: 'asc' | 'desc', limit?: number): Promise<AggregateTypesResult[]>;
87
+ aggregateTotal(types: string[], aggregate: AggregationType, interval: number): Promise<Record<string, number>>;
88
+ graph(types: string[], aggregate: AggregationType, interval: number): Promise<Map<number, Map<string, number>>>;
89
+ }
90
+ export type RecordCallback = () => Entry | Value | Entry[] | Value[] | null | Promise<Entry | Value | Entry[] | Value[] | null>;
91
+ export declare function defineConfig(config: PulseConfig): PulseConfig;
@@ -0,0 +1,3 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
@@ -0,0 +1,9 @@
1
+ import type { ValueAttributes } from './types.js';
2
+ export declare class Value {
3
+ timestamp: number;
4
+ type: string;
5
+ key: string;
6
+ value: string;
7
+ constructor(timestamp: number, type: string, key: string, value: string);
8
+ attributes(): ValueAttributes;
9
+ }
@@ -0,0 +1,20 @@
1
+ export class Value {
2
+ timestamp;
3
+ type;
4
+ key;
5
+ value;
6
+ constructor(timestamp, type, key, value) {
7
+ this.timestamp = timestamp;
8
+ this.type = type;
9
+ this.key = key;
10
+ this.value = value;
11
+ }
12
+ attributes() {
13
+ return {
14
+ timestamp: this.timestamp,
15
+ type: this.type,
16
+ key: this.key,
17
+ value: this.value,
18
+ };
19
+ }
20
+ }
@@ -0,0 +1,58 @@
1
+ {{{
2
+ exports({ to: app.configPath('pulse.ts') })
3
+ }}}
4
+ import env from '#start/env'
5
+ import { defineConfig } from 'adonisjs-pulse'
6
+
7
+ export default defineConfig({
8
+ enabled: env.get('PULSE_ENABLED', true),
9
+
10
+ storage: {
11
+ connection: env.get('PULSE_DB_CONNECTION'),
12
+ },
13
+
14
+ ingest: {
15
+ interval: 5000,
16
+ lottery: {
17
+ chance: 1,
18
+ outOf: 1000,
19
+ },
20
+ },
21
+
22
+ recorders: [
23
+ {
24
+ recorder: () => import('adonisjs-pulse/recorders/slow_requests'),
25
+ enabled: env.get('PULSE_SLOW_REQUESTS_ENABLED', true),
26
+ sampleRate: env.get('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
27
+ threshold: env.get('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
28
+ ignore: ['/pulse'],
29
+ },
30
+ {
31
+ recorder: () => import('adonisjs-pulse/recorders/slow_queries'),
32
+ enabled: env.get('PULSE_SLOW_QUERIES_ENABLED', true),
33
+ sampleRate: env.get('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
34
+ threshold: env.get('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
35
+ ignore: ['/pulse_/'],
36
+ },
37
+ {
38
+ recorder: () => import('adonisjs-pulse/recorders/exceptions'),
39
+ enabled: env.get('PULSE_EXCEPTIONS_ENABLED', true),
40
+ sampleRate: env.get('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
41
+ ignore: [],
42
+ },
43
+ {
44
+ recorder: () => import('adonisjs-pulse/recorders/servers'),
45
+ enabled: env.get('PULSE_SERVERS_ENABLED', true),
46
+ directories: ['/'],
47
+ },
48
+ {
49
+ recorder: () => import('adonisjs-pulse/recorders/cache_interactions'),
50
+ enabled: env.get('PULSE_CACHE_INTERACTIONS_ENABLED', true),
51
+ sampleRate: env.get('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1),
52
+ ignore: [],
53
+ groups: {
54
+ '/^job-exceptions:.*/': 'job-exceptions:*',
55
+ },
56
+ },
57
+ ],
58
+ })
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Path to the root directory where the stubs are stored. We use
3
+ * this path within commands and the configure hook
4
+ */
5
+ export declare const stubsRoot: string;
@@ -0,0 +1,7 @@
1
+ import { dirname } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ /**
4
+ * Path to the root directory where the stubs are stored. We use
5
+ * this path within commands and the configure hook
6
+ */
7
+ export const stubsRoot = dirname(fileURLToPath(import.meta.url));
@@ -0,0 +1,58 @@
1
+ {{{
2
+ exports({ to: app.migrationsPath(time + '_create_pulse_tables.ts') })
3
+ }}}
4
+ import { BaseSchema } from '@adonisjs/lucid/schema'
5
+
6
+ export default class extends BaseSchema {
7
+ async up() {
8
+ this.schema.createTable('pulse_values', (table) => {
9
+ table.increments('id')
10
+ table.integer('timestamp').unsigned()
11
+ table.string('type')
12
+ table.text('key', 'medium')
13
+ table.string('key_hash')
14
+ table.text('value', 'medium')
15
+
16
+ table.index('timestamp') // For trimming...
17
+ table.index('type') // For fast lookups and purging...
18
+ table.unique(['type', 'key_hash']) // For data integrity and upserts...
19
+ })
20
+
21
+ this.schema.createTable('pulse_entries', (table) => {
22
+ table.increments('id')
23
+ table.integer('timestamp').unsigned()
24
+ table.string('type')
25
+ table.text('key', 'medium')
26
+ table.string('key_hash')
27
+ table.bigInteger('value').nullable()
28
+
29
+ table.index('timestamp') // For trimming...
30
+ table.index('type') // For purging...
31
+ table.index('key_hash') // For mapping...
32
+ table.index(['timestamp', 'type', 'key_hash', 'value']) // For aggregate queries...
33
+ })
34
+
35
+ this.schema.createTable('pulse_aggregates', (table) => {
36
+ table.increments('id')
37
+ table.integer('bucket').unsigned()
38
+ table.mediumint('period').unsigned()
39
+ table.string('type')
40
+ table.text('key', 'medium')
41
+ table.string('key_hash')
42
+ table.string('aggregate')
43
+ table.decimal('value', 20, 2)
44
+ table.integer('count').nullable()
45
+
46
+ table.unique(['bucket', 'period', 'type', 'aggregate', 'key_hash']) // Force "on duplicate update"...
47
+ table.index(['period', 'bucket']) // For trimming...
48
+ table.index('type') // For purging...
49
+ table.index(['period', 'type', 'aggregate', 'bucket']) // For aggregate queries...
50
+ })
51
+ }
52
+
53
+ async down() {
54
+ this.schema.dropTable('pulse_values')
55
+ this.schema.dropTable('pulse_entries')
56
+ this.schema.dropTable('pulse_aggregates')
57
+ }
58
+ }
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "adonisjs-pulse",
3
+ "description": "",
4
+ "version": "0.0.1",
5
+ "engines": {
6
+ "node": ">=20.6.0"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "build/src",
11
+ "build/providers",
12
+ "build/services",
13
+ "build/stubs",
14
+ "build/resources",
15
+ "build/index.d.ts",
16
+ "build/index.js",
17
+ "build/configure.d.ts",
18
+ "build/configure.js"
19
+ ],
20
+ "exports": {
21
+ ".": "./build/index.js",
22
+ "./types": "./build/src/types.js",
23
+ "./pulse_provider": "./build/providers/pulse_provider.js",
24
+ "./services/pulse": "./build/services/pulse.js",
25
+ "./recorders/slow_requests": "./build/src/recorders/slow_requests.js",
26
+ "./recorders/slow_queries": "./build/src/recorders/slow_queries.js",
27
+ "./recorders/exceptions": "./build/src/recorders/exceptions.js",
28
+ "./recorders/servers": "./build/src/recorders/servers.js",
29
+ "./recorders/cache_interactions": "./build/src/recorders/cache_interactions.js"
30
+ },
31
+ "keywords": [],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "devDependencies": {
35
+ "@adonisjs/assembler": "^7.8.2",
36
+ "@adonisjs/core": "^6.12.0",
37
+ "@adonisjs/eslint-config": "2.0.0-beta.7",
38
+ "@adonisjs/lucid": "^21.8.2",
39
+ "@adonisjs/prettier-config": "^1.4.0",
40
+ "@adonisjs/tsconfig": "^1.3.0",
41
+ "@japa/assert": "^3.0.0",
42
+ "@japa/runner": "^3.1.4",
43
+ "@swc/core": "^1.6.3",
44
+ "@types/node": "^20.14.5",
45
+ "adonisjs-livewire": "^0.15.1",
46
+ "c8": "^10.1.2",
47
+ "copyfiles": "^2.4.1",
48
+ "del-cli": "^5.1.0",
49
+ "edge.js": "^6.4.0",
50
+ "eslint": "^9.15.0",
51
+ "np": "^10.0.6",
52
+ "prettier": "^3.3.2",
53
+ "ts-node-maintained": "^10.9.4",
54
+ "typescript": "^5.4.5"
55
+ },
56
+ "peerDependencies": {
57
+ "@adonisjs/core": "^6.2.0",
58
+ "@adonisjs/lucid": "^21.8.2",
59
+ "adonisjs-livewire": "^0.15.1",
60
+ "edge.js": "^6.4.0"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public",
64
+ "tag": "latest"
65
+ },
66
+ "np": {
67
+ "message": "chore(release): %s",
68
+ "tag": "latest",
69
+ "branch": "main",
70
+ "anyBranch": false
71
+ },
72
+ "c8": {
73
+ "reporter": [
74
+ "text",
75
+ "html"
76
+ ],
77
+ "exclude": [
78
+ "tests/**"
79
+ ]
80
+ },
81
+ "prettier": "@adonisjs/prettier-config",
82
+ "dependencies": {
83
+ "unreflect": "^0.0.7"
84
+ },
85
+ "scripts": {
86
+ "clean": "del-cli build",
87
+ "copy:templates": "copyfiles \"stubs/**/*.stub\" \"resources/**/*\" build",
88
+ "typecheck": "tsc --noEmit",
89
+ "lint": "eslint .",
90
+ "format": "prettier --write .",
91
+ "quick:test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts",
92
+ "pretest": "npm run lint",
93
+ "test": "c8 npm run quick:test",
94
+ "prebuild": "npm run lint && npm run clean",
95
+ "build": "tsc",
96
+ "postbuild": "npm run copy:templates",
97
+ "release": "np",
98
+ "version": "npm run build"
99
+ }
100
+ }