mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
An extension's hooks were stated once, on the Base the process started with, so a write to a tenant's Base fired nothing an extension had asked for. The file says what to do when a record is created; it does not say where, and the where comes with the event. So a declaration is recorded as something that can be stated on any Base, and stated on each as it is constructed — the same place realtime is bound, for the same reason. A hook still sees only the Base it fired on. This grants an extension nothing it did not have. A file on disk is the operator's, and it already ran with the process's authority; what changes is which data its hooks see. An extension written when there was one Base now runs against each, which is a behaviour change and is the thing to know. Routes and crons an extension declares stay on the Base that loaded it: a route is registered once on a router, and a schedule belongs to the process. A tenant's Base was bootstrapped and never served, so nothing it scheduled ever ran — its logs were never cleaned and its database never optimised. Co-authored-by: Hanzo Dev <[email protected]>
50 lines
1.6 KiB
Go
50 lines
1.6 KiB
Go
package core
|
|
|
|
import "sync"
|
|
|
|
// AppBindings is what every Base this process opens binds to its own hooks, and
|
|
// it is read where a Base is constructed — so a binding registered here reaches
|
|
// an org's Base as well as the process's own.
|
|
//
|
|
// It is the hook counterpart of [AppMigrations]: one list, read in one place, so
|
|
// there is a single answer to what a Base does. Anything bound instead at the
|
|
// process's front door belongs to the one Base that door opens onto.
|
|
//
|
|
// Register before the Base that should carry it is constructed. The process's
|
|
// own Base is built at startup, so a plugin that registers after that says the
|
|
// same thing twice: once on the app it was handed, and once here for every Base
|
|
// opened later.
|
|
//
|
|
// What goes here runs on every Base, which is the whole point and also the
|
|
// limit: it may hold nothing that reaches past the Base it is handed.
|
|
var AppBindings BindingsList
|
|
|
|
// BindingsList is an ordered list of functions applied to a newly constructed
|
|
// App.
|
|
//
|
|
// A plugin holds one of its own to collect what it has to say before deciding
|
|
// which Bases hear it: Register records a statement, Apply makes it on a Base.
|
|
type BindingsList struct {
|
|
mu sync.RWMutex
|
|
list []func(App)
|
|
}
|
|
|
|
// Register adds fn to what every Base binds.
|
|
func (l *BindingsList) Register(fn func(App)) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
l.list = append(l.list, fn)
|
|
}
|
|
|
|
// Apply runs each registered binding against app, in registration order.
|
|
func (l *BindingsList) Apply(app App) {
|
|
l.mu.RLock()
|
|
list := l.list
|
|
l.mu.RUnlock()
|
|
|
|
for _, bind := range list {
|
|
bind(app)
|
|
}
|
|
}
|