Skip to content

Commit 0dce865

Browse files
committed
Guard implementation for machines
1 parent cddce59 commit 0dce865

36 files changed

Lines changed: 1947 additions & 96 deletions

File tree

dist/angular-ts.esm.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Version: 0.30.0 - June 4, 2026 05:08:51 */
1+
/* Version: 0.30.0 - June 5, 2026 02:15:08 */
22
/**
33
* Canonical token names for the built-in injectables exposed by the core `ng`
44
* module.
@@ -4115,7 +4115,7 @@ const ASTType = {
41154115
function isScopeEventStopped(event) {
41164116
return event.stopped;
41174117
}
4118-
const _SCOPE_PROXY_BIND = Symbol("ngScopeProxyBind");
4118+
const SCOPE_PROXY_BIND = Symbol("ngProxyBind");
41194119
let uid = 0;
41204120
/**
41214121
* Returns the next generated scope/listener id.
@@ -4740,7 +4740,7 @@ function getCachedScopeProxy(target, handler) {
47404740
if (!proxy) {
47414741
proxy = new Proxy(target, handler);
47424742
proxiesByHandler.set(handler, proxy);
4743-
const bind = target[_SCOPE_PROXY_BIND];
4743+
const bind = target[SCOPE_PROXY_BIND];
47444744
if (isFunction(bind)) {
47454745
bind.call(target, handler, proxy);
47464746
}
@@ -11083,9 +11083,12 @@ function createMachine(scopeOrConfig, maybeConfig) {
1108311083
let transitionStarted = false;
1108411084
try {
1108511085
const activeMachine = getActiveMachine();
11086+
if (!canRunTransition(transition, payload, activeMachine)) {
11087+
return false;
11088+
}
1108611089
const from = machineTarget.current;
1108711090
transitionStarted = true;
11088-
const nextMode = transition(activeMachine.data, payload, activeMachine);
11091+
const nextMode = transition._target(activeMachine.data, payload, activeMachine);
1108911092
const to = isNonEmptyString(nextMode) ? nextMode : from;
1109011093
const context = {
1109111094
type,
@@ -11118,11 +11121,13 @@ function createMachine(scopeOrConfig, maybeConfig) {
1111811121
}
1111911122
});
1112011123
},
11121-
can(type) {
11124+
can(type, payload) {
1112211125
if (!isString(type)) {
1112311126
return false;
1112411127
}
11125-
return !!getTransition(machineTarget.current, config, type);
11128+
const transition = getTransition(machineTarget.current, config, type);
11129+
return (!!transition &&
11130+
canRunTransition(transition, payload, getActiveMachine()));
1112611131
},
1112711132
matches(mode) {
1112811133
return machineTarget.current === mode;
@@ -11144,7 +11149,7 @@ function createMachine(scopeOrConfig, maybeConfig) {
1114411149
});
1114511150
},
1114611151
};
11147-
Object.defineProperty(machineTarget, _SCOPE_PROXY_BIND, {
11152+
Object.defineProperty(machineTarget, SCOPE_PROXY_BIND, {
1114811153
value(handler, proxy) {
1114911154
let binding = bindings.get(handler.$id);
1115011155
if (!binding) {
@@ -11298,9 +11303,30 @@ function getTransition(mode, config, type) {
1129811303
return undefined;
1129911304
}
1130011305
const transition = transitions[type];
11301-
return isFunction(transition)
11302-
? transition
11303-
: undefined;
11306+
return resolveMachineTransition(transition);
11307+
}
11308+
function resolveMachineTransition(transition) {
11309+
if (isFunction(transition)) {
11310+
return {
11311+
_target: transition,
11312+
};
11313+
}
11314+
if (!isPlainObject(transition) || !hasOwn(transition, "target")) {
11315+
return undefined;
11316+
}
11317+
const descriptor = transition;
11318+
if (!isFunction(descriptor.target)) {
11319+
return undefined;
11320+
}
11321+
return {
11322+
_guard: isFunction(descriptor.guard) ? descriptor.guard : undefined,
11323+
_target: descriptor.target,
11324+
};
11325+
}
11326+
function canRunTransition(transition, payload, machine) {
11327+
return transition._guard
11328+
? transition._guard(machine.data, payload, machine)
11329+
: true;
1130411330
}
1130511331
function getModeHook(hooks, mode) {
1130611332
if (!hooks || !hasOwn(hooks, mode)) {
@@ -11594,7 +11620,7 @@ function createWorkflowFactory($machine) {
1159411620
scheduleWorkflowBindings();
1159511621
},
1159611622
};
11597-
Object.defineProperty(workflowTarget, _SCOPE_PROXY_BIND, {
11623+
Object.defineProperty(workflowTarget, SCOPE_PROXY_BIND, {
1159811624
value(handler, proxy) {
1159911625
let binding = bindings.get(handler.$id);
1160011626
if (!binding) {

dist/angular-ts.umd.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Version: 0.30.0 - June 4, 2026 05:08:47 */
1+
/* Version: 0.30.0 - June 5, 2026 02:15:04 */
22
(function (global, factory) {
33
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
44
typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -4121,7 +4121,7 @@
41214121
function isScopeEventStopped(event) {
41224122
return event.stopped;
41234123
}
4124-
const _SCOPE_PROXY_BIND = Symbol("ngScopeProxyBind");
4124+
const SCOPE_PROXY_BIND = Symbol("ngProxyBind");
41254125
let uid = 0;
41264126
/**
41274127
* Returns the next generated scope/listener id.
@@ -4746,7 +4746,7 @@
47464746
if (!proxy) {
47474747
proxy = new Proxy(target, handler);
47484748
proxiesByHandler.set(handler, proxy);
4749-
const bind = target[_SCOPE_PROXY_BIND];
4749+
const bind = target[SCOPE_PROXY_BIND];
47504750
if (isFunction(bind)) {
47514751
bind.call(target, handler, proxy);
47524752
}
@@ -11089,9 +11089,12 @@
1108911089
let transitionStarted = false;
1109011090
try {
1109111091
const activeMachine = getActiveMachine();
11092+
if (!canRunTransition(transition, payload, activeMachine)) {
11093+
return false;
11094+
}
1109211095
const from = machineTarget.current;
1109311096
transitionStarted = true;
11094-
const nextMode = transition(activeMachine.data, payload, activeMachine);
11097+
const nextMode = transition._target(activeMachine.data, payload, activeMachine);
1109511098
const to = isNonEmptyString(nextMode) ? nextMode : from;
1109611099
const context = {
1109711100
type,
@@ -11124,11 +11127,13 @@
1112411127
}
1112511128
});
1112611129
},
11127-
can(type) {
11130+
can(type, payload) {
1112811131
if (!isString(type)) {
1112911132
return false;
1113011133
}
11131-
return !!getTransition(machineTarget.current, config, type);
11134+
const transition = getTransition(machineTarget.current, config, type);
11135+
return (!!transition &&
11136+
canRunTransition(transition, payload, getActiveMachine()));
1113211137
},
1113311138
matches(mode) {
1113411139
return machineTarget.current === mode;
@@ -11150,7 +11155,7 @@
1115011155
});
1115111156
},
1115211157
};
11153-
Object.defineProperty(machineTarget, _SCOPE_PROXY_BIND, {
11158+
Object.defineProperty(machineTarget, SCOPE_PROXY_BIND, {
1115411159
value(handler, proxy) {
1115511160
let binding = bindings.get(handler.$id);
1115611161
if (!binding) {
@@ -11304,9 +11309,30 @@
1130411309
return undefined;
1130511310
}
1130611311
const transition = transitions[type];
11307-
return isFunction(transition)
11308-
? transition
11309-
: undefined;
11312+
return resolveMachineTransition(transition);
11313+
}
11314+
function resolveMachineTransition(transition) {
11315+
if (isFunction(transition)) {
11316+
return {
11317+
_target: transition,
11318+
};
11319+
}
11320+
if (!isPlainObject(transition) || !hasOwn(transition, "target")) {
11321+
return undefined;
11322+
}
11323+
const descriptor = transition;
11324+
if (!isFunction(descriptor.target)) {
11325+
return undefined;
11326+
}
11327+
return {
11328+
_guard: isFunction(descriptor.guard) ? descriptor.guard : undefined,
11329+
_target: descriptor.target,
11330+
};
11331+
}
11332+
function canRunTransition(transition, payload, machine) {
11333+
return transition._guard
11334+
? transition._guard(machine.data, payload, machine)
11335+
: true;
1131011336
}
1131111337
function getModeHook(hooks, mode) {
1131211338
if (!hooks || !hasOwn(hooks, mode)) {
@@ -11600,7 +11626,7 @@
1160011626
scheduleWorkflowBindings();
1160111627
},
1160211628
};
11603-
Object.defineProperty(workflowTarget, _SCOPE_PROXY_BIND, {
11629+
Object.defineProperty(workflowTarget, SCOPE_PROXY_BIND, {
1160411630
value(handler, proxy) {
1160511631
let binding = bindings.get(handler.$id);
1160611632
if (!binding) {

dist/angular-ts.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/angular-ts.umd.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core/scope/scope.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ASTType } from '../parse/ast-type.js';
55
function isScopeEventStopped(event) {
66
return event.stopped;
77
}
8-
const _SCOPE_PROXY_BIND = Symbol("ngScopeProxyBind");
8+
const SCOPE_PROXY_BIND = Symbol("ngProxyBind");
99
let uid = 0;
1010
/**
1111
* Returns the next generated scope/listener id.
@@ -630,7 +630,7 @@ function getCachedScopeProxy(target, handler) {
630630
if (!proxy) {
631631
proxy = new Proxy(target, handler);
632632
proxiesByHandler.set(handler, proxy);
633-
const bind = target[_SCOPE_PROXY_BIND];
633+
const bind = target[SCOPE_PROXY_BIND];
634634
if (isFunction(bind)) {
635635
bind.call(target, handler, proxy);
636636
}
@@ -2766,4 +2766,4 @@ function collectChildIds(child) {
27662766
return ids;
27672767
}
27682768

2769-
export { RootScopeProvider, Scope, _SCOPE_PROXY_BIND, createScope, getArrayMutationMeta, isNonScope };
2769+
export { RootScopeProvider, SCOPE_PROXY_BIND, Scope, createScope, getArrayMutationMeta, isNonScope };

dist/externs/angular.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5387,10 +5387,12 @@ ng.Machine.prototype.send = function(type, var_args) {};
53875387

53885388
/**
53895389
* Public Machine.can member exposed by the AngularTS namespace contract.
5390-
* @param {?} type
5390+
* @template TType
5391+
* @param {TType} type
5392+
* @param {(?|undefined)} payload
53915393
* @return {boolean}
53925394
*/
5393-
ng.Machine.prototype.can = function(type) {};
5395+
ng.Machine.prototype.can = function(type, payload) {};
53945396

53955397
/**
53965398
* Public Machine.matches member exposed by the AngularTS namespace contract.
@@ -5443,6 +5445,12 @@ ng.MachineConfig.prototype.transitions;
54435445
*/
54445446
ng.MachineConfig.prototype.hooks;
54455447

5448+
/**
5449+
* Public AngularTS MachineGuard contract exposed through the global ng namespace for Closure-annotated applications.
5450+
* @typedef {function(?, ?, !ng.Machine<?, ?>): boolean}
5451+
*/
5452+
ng.MachineGuard;
5453+
54465454
/**
54475455
* Public AngularTS MachineHooks contract exposed through the global ng namespace for Closure-annotated applications.
54485456
* @template TData, TEvents
@@ -5506,6 +5514,34 @@ ng.MachineSnapshot.prototype.data;
55065514
*/
55075515
ng.MachineTransition;
55085516

5517+
/**
5518+
* Public AngularTS MachineTransitionDefinition contract exposed through the global ng namespace for Closure-annotated applications.
5519+
* @typedef {(!ng.MachineTransitionDescriptor<?, ?, ?>|function(?, ?, !ng.Machine<?, ?>): (boolean|string|undefined))}
5520+
*/
5521+
ng.MachineTransitionDefinition;
5522+
5523+
/**
5524+
* Public AngularTS MachineTransitionDescriptor contract exposed through the global ng namespace for Closure-annotated applications.
5525+
* @template TData, TPayload, TEvents
5526+
* @record
5527+
*/
5528+
ng.MachineTransitionDescriptor = function() {};
5529+
5530+
/**
5531+
* Public MachineTransitionDescriptor.guard member exposed by the AngularTS namespace contract.
5532+
* @type {(function(TData, TPayload, !ng.Machine<TData, TEvents>): boolean|undefined)}
5533+
*/
5534+
ng.MachineTransitionDescriptor.prototype.guard;
5535+
5536+
/**
5537+
* Public MachineTransitionDescriptor.target member exposed by the AngularTS namespace contract.
5538+
* @param {TData} data
5539+
* @param {TPayload} payload
5540+
* @param {!ng.Machine<TData, TEvents>} machine
5541+
* @return {(boolean|string|undefined)}
5542+
*/
5543+
ng.MachineTransitionDescriptor.prototype.target = function(data, payload, machine) {};
5544+
55095545
/**
55105546
* Public AngularTS MachineTransitionContext contract exposed through the global ng namespace for Closure-annotated applications.
55115547
* @template TData, TEvents, TPayload

dist/services/machine/machine.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createScope, _SCOPE_PROXY_BIND } from '../../core/scope/scope.js';
1+
import { createScope, SCOPE_PROXY_BIND } from '../../core/scope/scope.js';
22
import { isObject, isString, isFunction, hasOwn, isArray, keys, isInstanceOf } from '../../shared/utils.js';
33

44
/**
@@ -34,9 +34,12 @@ function createMachine(scopeOrConfig, maybeConfig) {
3434
let transitionStarted = false;
3535
try {
3636
const activeMachine = getActiveMachine();
37+
if (!canRunTransition(transition, payload, activeMachine)) {
38+
return false;
39+
}
3740
const from = machineTarget.current;
3841
transitionStarted = true;
39-
const nextMode = transition(activeMachine.data, payload, activeMachine);
42+
const nextMode = transition._target(activeMachine.data, payload, activeMachine);
4043
const to = isNonEmptyString(nextMode) ? nextMode : from;
4144
const context = {
4245
type,
@@ -69,11 +72,13 @@ function createMachine(scopeOrConfig, maybeConfig) {
6972
}
7073
});
7174
},
72-
can(type) {
75+
can(type, payload) {
7376
if (!isString(type)) {
7477
return false;
7578
}
76-
return !!getTransition(machineTarget.current, config, type);
79+
const transition = getTransition(machineTarget.current, config, type);
80+
return (!!transition &&
81+
canRunTransition(transition, payload, getActiveMachine()));
7782
},
7883
matches(mode) {
7984
return machineTarget.current === mode;
@@ -95,7 +100,7 @@ function createMachine(scopeOrConfig, maybeConfig) {
95100
});
96101
},
97102
};
98-
Object.defineProperty(machineTarget, _SCOPE_PROXY_BIND, {
103+
Object.defineProperty(machineTarget, SCOPE_PROXY_BIND, {
99104
value(handler, proxy) {
100105
let binding = bindings.get(handler.$id);
101106
if (!binding) {
@@ -249,9 +254,30 @@ function getTransition(mode, config, type) {
249254
return undefined;
250255
}
251256
const transition = transitions[type];
252-
return isFunction(transition)
253-
? transition
254-
: undefined;
257+
return resolveMachineTransition(transition);
258+
}
259+
function resolveMachineTransition(transition) {
260+
if (isFunction(transition)) {
261+
return {
262+
_target: transition,
263+
};
264+
}
265+
if (!isPlainObject(transition) || !hasOwn(transition, "target")) {
266+
return undefined;
267+
}
268+
const descriptor = transition;
269+
if (!isFunction(descriptor.target)) {
270+
return undefined;
271+
}
272+
return {
273+
_guard: isFunction(descriptor.guard) ? descriptor.guard : undefined,
274+
_target: descriptor.target,
275+
};
276+
}
277+
function canRunTransition(transition, payload, machine) {
278+
return transition._guard
279+
? transition._guard(machine.data, payload, machine)
280+
: true;
255281
}
256282
function getModeHook(hooks, mode) {
257283
if (!hooks || !hasOwn(hooks, mode)) {

dist/services/workflow/workflow.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createScope, _SCOPE_PROXY_BIND } from '../../core/scope/scope.js';
1+
import { createScope, SCOPE_PROXY_BIND } from '../../core/scope/scope.js';
22
import { _machine } from '../../injection-tokens.js';
33
import { isObject, isString, isFunction, isNumber, hasOwn, isArray, isInstanceOf, isBoolean } from '../../shared/utils.js';
44

@@ -199,7 +199,7 @@ function createWorkflowFactory($machine) {
199199
scheduleWorkflowBindings();
200200
},
201201
};
202-
Object.defineProperty(workflowTarget, _SCOPE_PROXY_BIND, {
202+
Object.defineProperty(workflowTarget, SCOPE_PROXY_BIND, {
203203
value(handler, proxy) {
204204
let binding = bindings.get(handler.$id);
205205
if (!binding) {

0 commit comments

Comments
 (0)